pax_global_header00006660000000000000000000000064137100020030014474gustar00rootroot0000000000000052 comment=bc1d4f6587a4a4829b5d55e3ca7ad584da6de545 jpnevulator-2.3.6/000077500000000000000000000000001371000200300140555ustar00rootroot00000000000000jpnevulator-2.3.6/.gitignore000066400000000000000000000000411371000200300160400ustar00rootroot00000000000000*.o jpnevulator.1.gz jpnevulator jpnevulator-2.3.6/AUTHORS000066400000000000000000000005011371000200300151210ustar00rootroot00000000000000AUTHORS - who wrote what on jpnevulator? ======================================== * Freddy Spierenburg Main author * Colin Foster --append functionality * Martin Blumenstingl Added cross compilation and Android (NDK) build support jpnevulator-2.3.6/Android.mk000066400000000000000000000005701371000200300157700ustar00rootroot00000000000000LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := jpnevulator # we need at least API level 21 for posix_openpt TARGET_PLATFORM := android-21 LOCAL_CFLAGS += -Wall LOCAL_SRC_FILES := main.c \ options.c \ jpnevulator.c \ byte.c \ interface.c \ tty.c \ pty.c \ io.c \ checksum.c \ crc16.c \ crc8.c \ list.c \ misc.c include $(BUILD_EXECUTABLE) jpnevulator-2.3.6/BUGS000066400000000000000000000047521371000200300145500ustar00rootroot00000000000000BUGS ==== This file describes several bugs known as of today. There do exist probably some more, but I'm not aware of them right now. Do not hesitate to send me your bug-reports though. I will gladly add them to this file and maybe even solve some of them. :-) Order of bytes broke when reading several tty devices at once ============================================================= The display of incoming bytes can be broke if you use multiple tty devices to read from. At the moment I do not have a solution for this problem. Since I use select() to watch the several tty devices and after the select() I have to read() them one by one, I can not completely 100% display which bytes came after which on different tty devices. Take the example below: $ jpnevulator --ascii --timing-print --tty /dev/ttyS0 --tty /dev/ttyUSB0 --read 2006-05-30 13:23:49.461075: /dev/ttyS0 00 00 05 3B 0D 00 00 05 ...;.... 2006-05-30 13:23:49.461113: /dev/ttyUSB0 00 05 3B 0D 00 00 05 3B 0D ..;....;. 2006-05-30 13:23:49.473074: /dev/ttyS0 3B 0D 00 00 05 3B 0D ;....;. 2006-05-30 13:23:49.473105: /dev/ttyUSB0 00 12 05 06 39 00 12 05 06 39 1F 00 22 80 00 0E ....9....9.."... $ And now see the order in which things really got sent on the line: /dev/ttyS0: 00 00 05 3B 0D /dev/ttyUSB0: 00 00 05 3B 0D /dev/ttyS0: 00 00 05 3B 0D /dev/ttyUSB0: 00 00 05 3B 0D /dev/ttyS0: 00 00 05 3B 0D /dev/ttyUSB0: 00 00 05 3B 0D 00 12 05 06 39 00 12 05 06 39 ... As you can see /dev/ttyUSB0 receives the echo of all things sent by /dev/ttyS0. This is exactly what happens. But since there does exist a small time between the select() who is happy expressing something is available and the read() who does get the available data, some extra data will be available. I have no idea on how I can use high level system call like select() and read() and be still able to put the bytes in the correct order. Anyone an idea? During the course of 2014 I've received a suggestion from both Colin Foster and Rolf Freitag to use threads to solve this problem. Just create a thread for every reading process and a thread for the writing process. I myself am a little bit sceptic about the solution, as I expect we are only moving the problem away into the kernel. But it might be worth trying to implement this idea. Since it's quiet a big change to the core of the software I do not expect myself to dive into it. But maybe my future self thinks differently. ;) jpnevulator-2.3.6/COPYING000066400000000000000000000431061371000200300151140ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. jpnevulator-2.3.6/Changelog000066400000000000000000000174351371000200300157010ustar00rootroot00000000000000jpnevulator (2.3.6); urgency=low * Fix potential buffer overflow problem. * Added dependency on misc.h. * Fix spelling errors in manual page. * Clarify a bit more why a certain part of the code does exist to do nothing in certain cases. * Updated FAQ that more than one question is answered. Two, yes two questions answered. Crazy!! It is like that for a mighty long time already, but now the FAQ knows too. :) -- Freddy Spierenburg Tue, 28 Jul 2020 12:39:34 +0200 jpnevulator (2.3.5); urgency=low * (Martin Blumenstingl) Added cross compilation and Android (NDK) build support. * Remove debian files. Moved them to their own repository at https://github.com/snarlistic/jpnevulator-debian. -- Freddy Spierenburg Fri, 08 May 2020 21:58:20 +0200 jpnevulator (2.3.4); urgency=low * Note to self: drink less beer during a software release. Now everything is in sync and I can release a proper version of the software. Stupid me! Please see version 2.3.2 for the real changes in this release and understand that 2.3.2, 2.3.3 and 2.3.4 are all the same. -- Freddy Spierenburg Sat, 27 Aug 2016 01:23:01 +0200 jpnevulator (2.3.3); urgency=low * Played a bit with Git tags, fucked up and now trying to correct the mess. -- Freddy Spierenburg Sat, 27 Aug 2016 01:08:25 +0200 jpnevulator (2.3.2); urgency=low * Added a .gitignore to the project. * Added an extra question to the FAQ. * Fixed some minor code style issues. * Fixed the copyright notice, we way into 2016. * Updated to Debian Standards-Version 3.9.8. -- Freddy Spierenburg Sat, 27 Aug 2016 01:05:54 +0200 jpnevulator (2.3.1); urgency=low * Moved the --base option to the generic options section instead of the read options section. -- Freddy Spierenburg Mon, 14 Dec 2015 00:31:08 +0100 jpnevulator (2.3.0); urgency=low * Fixed lintian warning (out-of-date-standards-version). Thank you Paul Slootman! * Added the --base option, a re-implementation of the patch Ark444 sent me in my first GitHub Pull Request. -- Freddy Spierenburg Sun, 13 Dec 2015 22:37:37 +0100 jpnevulator (2.2.1); urgency=low * Applied bugfix by David Binderman that fixed two stupid bugs in testing if a correct integer was given to the --width and --size options. Shame on me, the bug was there ever since the very first release. * Fixed some minor space usage in the README, as if anybody cares. ;) * Rolf Freitag suggested to remove the calls to fcntl() and cfmakeraw() in ttyOpen(). No longer will jpnevulator remove the parity settings made by stty(1). -- Freddy Spierenburg Tue, 29 Sep 2015 21:18:55 +0200 jpnevulator (2.2.0); urgency=low * Applied patch by Colin Foster that provides --append support. Thank you very much Colin! * Added threading suggestion to BUGS and TODO list. Idea provided by Colin Foster and Rolf Freitag. Thanks guys! -- Freddy Spierenburg Tue, 07 Oct 2014 19:00:24 +0200 jpnevulator (2.1.3); urgency=low * Fixed debian/changelog since it was missing the information on closing Debian bug #748568. -- Freddy Spierenburg Tue, 05 Aug 2014 21:39:36 +0200 jpnevulator (2.1.2); urgency=low * Jonathan Liu requested an increase in interface name and alias length: "Buffer size 32 is too small for /dev/serial/by-id/... devices." -- Freddy Spierenburg Thu, 12 Jun 2014 18:49:13 +0200 jpnevulator (2.1.1); urgency=low * Fixed segmentation fault using -o option and checked if any other option had the same problem, which of course turned out to be the case. -- Freddy Spierenburg Mon, 06 Aug 2012 21:20:35 +0200 jpnevulator (2.1.0); urgency=low * Fixed lintian problems. * Added support for --count option. This makes the software exit after reading / writing the given amount of bytes. -- Freddy Spierenburg Sun, 05 Aug 2012 22:47:38 +0200 jpnevulator (2.0.3); urgency=low * Fixed help output, --control-enable should be --control and options should be using a '=' character in between option and value. Thanks to Gustavo Conrad for sending me a bug report! -- Freddy Spierenburg Wed, 09 May 2012 21:25:05 +0200 jpnevulator (2.0.2); urgency=low * Fixed small --timing-delta bug. -- Freddy Spierenburg Tue, 17 Apr 2012 18:51:18 +0200 jpnevulator (2.0.1); urgency=low * This time increased the software version number for real. :( * Fixed a small time output bug. -- Freddy Spierenburg Mon, 16 Apr 2012 18:25:08 +0200 jpnevulator (2.0.0); urgency=low * Added pseudo-terminal device support. * Added support to monitor modem control bits. * Fixed typo in manual page. -- Freddy Spierenburg Sun, 15 Apr 2012 23:26:33 +0200 jpnevulator (1.3.1); urgency=low * Fixed spelling error in TODO file. * Fixed typo in manual page. * Fixed typo and current home address in README file. * Fixed many many Debian lintian errors and even some compile warnings. * Finally added a FAQ file. -- Freddy Spierenburg Sat, 21 Jan 2012 23:54:20 +0100 jpnevulator (1.3.0); urgency=low * Added the --pass option. A great idea mailed to me by Ardor. * Nicely use bool_t in a consequent manner. * Fixed a stupid typo bug in tty.h. It only became a compiler bug * since I now changed the tty variable into ttyReader. Stupid me! -- Freddy Spierenburg Wed, 22 Apr 2009 19:08:22 +0200 jpnevulator (1.2.3); urgency=low * Fixed some typo's in debian/control. * FIxed some typo's in the manual page and the files BUGS and README. -- Freddy Spierenburg Fri, 21 Jul 2006 15:17:52 +0200 jpnevulator (1.2.2); urgency=low * Fixed some compiler warnings; isblank() and combination printf() and sizeof(). -- Freddy Spierenburg Mon, 17 Jul 2006 00:04:54 +0200 jpnevulator (1.2.1); urgency=low * Compressed manual page with best compression. * Changed description in Debian control file. -- Freddy Spierenburg Sun, 25 Jun 2006 16:06:32 +0200 jpnevulator (1.2.0); urgency=low * Fixed small bug in list.c; listDestory() should also zero the number of elements in the list. * Fixed a bug; the --byte-count option now works correct. * Added a timeout for the display of ASCII data. -- Freddy Spierenburg Sun, 25 Jun 2006 14:05:43 +0200 jpnevulator (1.1.0); urgency=low * Added support for --delay-byte. This option waits the given amount of microseconds in between the sending of every input byte. * Renamed the --delay option to --delay-line. -- Freddy Spierenburg Sat, 03 Jun 2006 10:54:03 +0200 jpnevulator (1.0.3); urgency=low * Added support for --byte-count. This option writes a nice index number of the bytes received. * Written a nice manual page. -- Freddy Spierenburg Sat, 03 Jun 2006 02:41:12 +0200 jpnevulator (1.0.2); urgency=medium * Added support for aliasing of the tty names in the printing. Just give --tty "/dev/ttyS0:Motorola MTM800" and the printing of data will use Motorola MTM800 instead of /dev/ttyS0. If you need another separator then ':' use the --alias-separator option. Any string of characters can be used. -- Freddy Spierenburg Thu, 01 Jun 2006 21:42:09 +0200 jpnevulator (1.0.1); urgency=medium * Bugfix. Using one tty the --timing-print option didn't worked correctly anymore. -- Freddy Spierenburg Thu, 01 Jun 2006 11:35:10 +0200 jpnevulator (1.0.0); urgency=low * Initial release. -- Freddy Spierenburg Tue, 30 May 2006 21:56:54 +0200 jpnevulator-2.3.6/FAQ000066400000000000000000000056401371000200300144140ustar00rootroot00000000000000FAQ === This is actually a quiet lousy FAQ with not many questions answered. But the truth is that only one question is asked frequently. The first here in this FAQ. Please read it, please, please, pretty please. :) Q: Your serial tool seems to block my application. What am I doing wrong? A: First the bad news: It's impossible for two GNU/Linux processes to open the same serial port at the same time. Doing so results in undefined behaviour like you just experienced. Jpnevulator was never built to sit in between the kernel and your application. I'm sorry. The way I have used Jpnevulator over and over again, was with a special self (not entirely myself, but a colleague of mine) made device that sort of sits in between a serial cable. Out of the device comes another serial cable that mirrors everything that is sent over the original serial cable. A sort of man in the middle. Now with a little bit of luck some good news: A little while ago Eric Shattow suggested to use pseudo-terminal devices to sit in between the kernel and your application. Unfortunately it's still impossible to work with speed changes that way and hence Eric never replied when I asked him what he did think of the solution I provided to him. So this pseudo-terminal device support is not tested very well, only in a limited way by me. It works and I think it's even relatively easy to use stty to detect speed changes and act upon it. I leave that as an exercise to the reader, but I would love to hear from anyone having done it. Q: The --write mode or the --pass option is not working. A: When the first "bug report" arrived I was surprised by this myself and thought indeed this was a bug. Monday July 25th I even wrote a 'bug introduced in version 2.2.1' news item. But when I investigated the problem seriously it of course was not. Silly me! The problem was not a bug in the software, but incorrect terminal settings. Please see the stty(1) manual page for all possible settings, but if you set your terminal settings like output below, all should nicely work: $ stty -a < /dev/ttyUSB0 speed 115200 baud; rows 0; columns 0; line = 0; intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = ; eol2 = ; swtch = ; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0; -parenb -parodd -cmspar cs8 hupcl -cstopb cread clocal -crtscts -ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr -icrnl -ixon -ixoff -iuclc -ixany -imaxbel -iutf8 -opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0 -isig -icanon -iexten -echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke $ This of course if you want a speed of 115200 and no parity settings. Again, please see the stty(1) manual for all possible settings if you want things to behave differently. jpnevulator-2.3.6/INSTALL000066400000000000000000000020061371000200300151040ustar00rootroot00000000000000INSTALL ======= This file contains information on how to build and install jpnevulator on your computer. For run-time information, check the README file and the manual page. See the homepage for jpnevulator http://jpnevulator.snarl.nl/ for more information. Download ======== The latest version of the software is available from http://jpnevulator.snarl.nl/. At this moment there do not exist any pre-compiled binaries. Building jpnevulator is so easy that anybody interested in the program should be able to do it. Compiling ========= $ make After this make you will have a binary called jpnevulator in the main source directory. You can copy this binary everywhere you like. You can also pass CC=cross-compile-gcc to the make command to build with a different compiler (for example to cross-compile jpnevulator) than gcc. Android binary ============== $ ndk-build APP_PLATFORM=android-21 APP_BUILD_SCRIPT=Android.mk NDK_PROJECT_PATH=. After this the resulting binaries can be found in libs//jpnevulator jpnevulator-2.3.6/Makefile000066400000000000000000000047701371000200300155250ustar00rootroot00000000000000# jpnevulator - serial reader/writer # Copyright (C) 2006-2020 Freddy Spierenburg # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # Name of the binary. NAME=jpnevulator # Destination where to put our stuff 'make install'. ifeq ($(origin DESTDIR), undefined) bindir=/usr/local/bin mandir=/usr/local/man/man1 else bindir=$(DESTDIR)/usr/bin mandir=$(DESTDIR)/usr/share/man/man1 endif # List of the objects to built. OBJECTS=main.o OBJECTS+=options.o OBJECTS+=jpnevulator.o OBJECTS+=byte.o OBJECTS+=interface.o OBJECTS+=tty.o OBJECTS+=pty.o OBJECTS+=io.o OBJECTS+=checksum.o OBJECTS+=crc16.o OBJECTS+=crc8.o OBJECTS+=list.o OBJECTS+=misc.o # List of the manual pages. MANPAGES=jpnevulator.1.gz # Tools CLIBS?= CFLAGS+=-Wall LDFLAGS?= CC?=gcc GZIP=gzip INSTALL=install .PHONY: all FORCE clean install all: $(NAME) $(MANPAGES) $(NAME): $(OBJECTS) $(CC) $(CFLAGS) $(LDFLAGS) -o $(NAME) $(CLIBS) $(OBJECTS) $(MANPAGES): $(GZIP) --best -c `echo $@|sed 's/\.gz$$//'` > $@ clean: rm -f $(NAME) $(OBJECTS) $(MANPAGES) install: $(NAME) $(MANPAGES) $(INSTALL) -D -m 0755 $(NAME) $(bindir)/$(NAME) for manual in $(MANPAGES); do \ $(INSTALL) -D -m 0644 $$manual $(mandir)/$$manual; \ done # Dummy target as dependecy if something has to be build everytime FORCE: # Automatic collection of dependencies in the source files. # It's only updated the first time, after that it must be done maually # with "make depend" # Target to update the file, it's removed first dependall: dependrm depend #remove the file dependrm: rm -f dependencies.in # Build the file that contains the dependencies. No deps in this rule. # If there were deps it would be rebuilt every change, which is unneeded: depend: dependencies.in @echo "depend" dependencies.in: $(CC) -MM $(CFLAGS) $(patsubst %.o,%.c,$(OBJECTS)) >$@ # The dependecies are included from a separate file: -include dependencies.in jpnevulator-2.3.6/README000066400000000000000000000124111371000200300147340ustar00rootroot00000000000000README ====== This is jpnevulator. What once started as a Jackpot Navigator emulator (hence the strange name) is now a nice serial sniffer and you can use it to send data on a serial line too. It's sort of like a very simple terminal emulator, like picocom. But jpnevulator uses hexadecimal byte values to display and send along the line. Very handy indeed if you are working a lot with serial protocols. Please see the INSTALL file on how to build the program. It's so easy, anyone should be able to do it. Just make sure you have a compiler on your computer. You are running a Free Operating System aren't you? Of course you are! :-) If you need any more information on how to use the program, please read the fine manual. Have fun! As a last note. I once did read a README file or the like from a program, if I'm correct it was xdm. The author asked for anyone who used and liked his program to send him a postcard from the area where they live in. Of course I did and besides that liked the idea very much. So as a shameless act of copying I'm asking from you to do the same. If you like the program I would really love to hear from you by means of a postcard. You can send it to: Freddy Spierenburg Meer van Annecy 5 3446 JT Woerden The Netherlands Thanks in advance and again; have fun using this program! Thank you ========= Thanks to Michiel van Dam for some good additional ideas. I also like to thank Dr. Octagon and Colonel Claypool for inspiring music while coding. Thanks to Ardor for his question which inspired a new great idea! Shame on me I did not came up with it myself in the first place. :) Thanks to Paul Slootman for making it possible this software is part of Debian. Thanks to Eric Shattow for his idea to use pseudo-terminal devices. Thanks to Gustavo Conrad for his feature request to monitor the serial port control lines. Thanks to his question I decided to write the code to enable it. Thanks to Duane A. Damiano for his feature request to stop reading after a provided amount of bytes. Enhanced the request into stopping reading and writing where appropriate. Thanks to Jonathan Liu for bringing to my attention that 32 is too small to use /dev/serial/by-id/... devices. Thanks to Michael Tautschnig for his bugreport. Thanks to Colin Foster for his --append patch. Thanks to David Binderman for his bugreport and patch. Thanks to Rolf Freitag for his bugreport. Thanks to Ark444 for his GitHub Pull Request, he was the first! His patch provided binary output and I rewrote it so both the read and write mode of the software are able to use the binary format, see --base option. Thanks to Martin Blumenstingl for adding cross compilation and Android (NDK) build support. History ======= The first version of jpnevulator was written around the summer of 2004, when I worked as a software engineer for Gaming Support. At the time I was working on Jackpot Junction, a visual jackpot celebration program. For testing purposes I needed some jackpot values that would automatically change in time, like our Jackpot Navigator did when coins would get played on gaming machines. I would then feed those values by means of a serial cable to the Jackpot Junction system. The first version of jpnevulator was born! It had like six different jackpot values it would randomly increase and all those values where sent to the Jackpot Junction by means of the Cham2 protocol. Here the default message size of 22 stems from. :-) Soon I needed a more flexible system in which I could alter bytes in any way I wanted, because there where more protocols to send. That's when the second version of the program came into shape, sort of like the --write option is working nowadays. It would read bytes from stdin or a file and would write them to a serial interface. I used this version of jpnevulator a lot of times and was very happy with it. Then I started working for D.A.R.E!! in the start of 2006. For some serious debugging of the MIPS Linux kernel serial drivers for the AMD Alchemy AU1100 board I needed something in which I knew exactly what was sent on the serial line and what was not. I also needed something to read those bytes again. The --read option was born and worked wonderfully well. Not long after I invented the --read option I added support for the timing information. It was around then that I notices that jpnevulator was no longer a jackpot navigator emulator, but more a serial sniffer. And that it could be useful to other people. So that's why I released the software as Free Software. Let's see what happens to it... Copyright notice ================ Copyright (C) 2006-2020 Freddy Spierenburg This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Freddy Spierenburg jpnevulator-2.3.6/TODO000066400000000000000000000016241371000200300145500ustar00rootroot00000000000000TODO ==== This is the TODO file. Some of these things will eventually happen too. :) 2014-10-06: 6 Implement the threading solution to the order BUG. Don't expect me to put time into this anytime soon, but I'm open to patches. :) DONE ==== 2006-05-30: 0 (2006-06-02) Create --index option. This way some sort of index numbers will appear in front of all the read bytes. Might be handy with referencing. The option is named --byte-count instead of --index. 2006-05-30: 2 (2006-06-03) Write a decent manual page. 2006-05-30: 5 (2006-05-30) Release the whole software. 2006-05-30: 4 (2006-05-30) Setup a nice website and mailing list. Waited with a mailing list untill some users demand it. 2006-05-30: 1 (2006-06-03) Add --byte-delay option. This creates a delay between the sending of each byte in a line. Idea courtesy of Michiel van Dam. 2006-05-30: 3 (2006-06-06) Create a Debian package. jpnevulator-2.3.6/byte.c000066400000000000000000000135611371000200300151720ustar00rootroot00000000000000/* jpnevulator - serial reader/writer * Copyright (C) 2006-2020 Freddy Spierenburg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #ifndef __USE_ISOC99 #define __USE_ISOC99 /* for newly introduced isblank() */ #endif #include #include "byte.h" #define isBinaryDigit(digit) ((digit=='0')||(digit=='1')) #define bitGet(digit) (digit-'0') enum byteStateBinary { byteStateBinaryComplete=0, byteStateBinaryBitsIncomplete, byteStateBinaryError }; int byteBaseBinaryGet(FILE *fd) { int byte; int bitsRead; enum byteStateBinary state; /* No real reason to do this, but it makes the compiler happy. If this next line is not * here the compiler generates this warning below: * * warning: ‘byte’ may be used uninitialized in this function [-Wuninitialized] * * So let's initialize byte anyway. */ byte=0; for(bitsRead=0,state=byteStateBinaryBitsIncomplete;state!=byteStateBinaryComplete;) { static int character; switch(state) { case byteStateBinaryBitsIncomplete: { if(bitsRead<8) { character=fgetc(fd); if(isBinaryDigit(character)) { byte=(byte<<1)|bitGet(character); bitsRead++; } else if(isblank(character)) { if(bitsRead) { state=byteStateBinaryComplete; } } else { if(bitsRead) { if(ungetc(character,fd)!=EOF) { state=byteStateBinaryComplete; } else { state=byteStateBinaryError; } } else { state=byteStateBinaryError; } } } else { state=byteStateBinaryComplete; } break; } case byteStateBinaryError: { switch(character) { case EOF: { byte=byteRtrnEOF; break; } case '\n': { byte=byteRtrnEOL; break; } default: { byte=byteRtrnUnknown; break; } } state=byteStateBinaryComplete; break; } case byteStateBinaryComplete: { /* Do nothing, since it's impossible to reach this stage. Just * make the compiler happy. */ break; } } } return(byte); } /* Convert an ASCII character into a binary form. */ static char nibbleGet(char c) { return(isdigit(c)?c-'0':toupper(c)-'A'+0xA); } enum byteStateHexadecimal { byteStateHexadecimalComplete=0, byteStateHexadecimalNibbleFirst, byteStateHexadecimalHexNotation, byteStateHexadecimalNibbleSecond, byteStateHexadecimalError }; int byteBaseHexadecimalGet(FILE *fd) { int byte; enum byteStateHexadecimal state; /* No real reason to do this, but it makes the compiler happy. If this next line is not * here the compiler generates this warning below: * * warning: ‘byte’ may be used uninitialized in this function [-Wuninitialized] * * So let's initialize byte anyway. */ byte=0; for(state=byteStateHexadecimalNibbleFirst;state!=byteStateHexadecimalComplete;) { static int character; switch(state) { case byteStateHexadecimalNibbleFirst: { character=fgetc(fd); if(isxdigit(character)) { byte=nibbleGet(character); if(character=='0') { state=byteStateHexadecimalHexNotation; } else { state=byteStateHexadecimalNibbleSecond; } } else if(!isblank(character)) { state=byteStateHexadecimalError; } break; } case byteStateHexadecimalHexNotation: { character=fgetc(fd); if(tolower(character)=='x') { state=byteStateHexadecimalNibbleFirst; } else { if(ungetc(character,fd)!=EOF) { state=byteStateHexadecimalNibbleSecond; } else { state=byteStateHexadecimalError; } } break; } case byteStateHexadecimalNibbleSecond: { character=fgetc(fd); if(isxdigit(character)) { byte=(byte<<4)+nibbleGet(character); state=byteStateHexadecimalComplete; } else { state=byteStateHexadecimalError; } break; } case byteStateHexadecimalError: { switch(character) { case EOF: { byte=byteRtrnEOF; break; } case '\n': { byte=byteRtrnEOL; break; } default: { byte=byteRtrnUnknown; break; } } state=byteStateHexadecimalComplete; break; } case byteStateHexadecimalComplete: { /* Do nothing, since it's impossible to reach this stage. Just * make the compiler happy. */ break; } } } return(byte); } int byteGet(FILE *fd,enum byteBase base) { int byte; /* No real reason to do this, but it makes the compiler happy. If this next line is not * here the compiler generates this warning below: * * warning: ‘byte’ may be used uninitialized in this function [-Wuninitialized] * * So let's initialize byte anyway. */ byte=0; switch(base) { #define BASE(base,name,width) \ case name: { \ byte=name##Get(fd); \ break; \ } BASES #undef BASE } return(byte); } static void byteBaseBinaryPut(FILE *fd,unsigned char byte) { char bits[(sizeof(byte)*8)+1]; char *p; for(*(p=bits+sizeof(bits)-1)='\0';p--!=bits;byte>>=1) { *p='0'+(byte&0x01); } fprintf(fd,"%s",bits); } static void byteBaseHexadecimalPut(FILE *fd, unsigned char byte) { fprintf(fd,"%02X",byte); } void bytePut(FILE *fd,enum byteBase base,unsigned char byte) { switch(base) { #define BASE(base,name,width) \ case name: { \ name##Put(fd,byte); \ break; \ } BASES #undef BASE } } jpnevulator-2.3.6/byte.h000066400000000000000000000023041371000200300151700ustar00rootroot00000000000000/* jpnevulator - serial reader/writer * Copyright (C) 2006-2020 Freddy Spierenburg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __BYTE_H #define __BYTE_H enum byteRtrn { byteRtrnEOF=-128, byteRtrnEOL, byteRtrnUnknown }; #define BASES \ base2 \ base16 #define base2 BASE(2,byteBaseBinary,8) #define base16 BASE(16,byteBaseHexadecimal,2) enum byteBase { #define BASE(base,name,width) name=base, BASES #undef BASE }; extern int byteGet(FILE *,enum byteBase); extern void bytePut(FILE *,enum byteBase,unsigned char); #endif jpnevulator-2.3.6/checksum.c000066400000000000000000000017621371000200300160310ustar00rootroot00000000000000/* jpnevulator - serial reader/writer * Copyright (C) 2006-2020 Freddy Spierenburg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ unsigned short checksumCalculate(unsigned char *data,int length) { unsigned short checksum; int index; for(index=0,checksum=0;index>1)^poly; } else { crc>>=1; } data>>=1; } return(crc); } void crc16TableCreate(unsigned short seed,unsigned short poly) { unsigned short index; for(index=0;index<256;index++) { crcTable[index]=calcCRC(seed,poly,index); } } static unsigned short crcAdd(unsigned short crc,unsigned char byte) { return(crc>>8)^crcTable[(crc&0xFF)^byte]; } unsigned short crc16Calculate(unsigned char *data,int length) { unsigned short crc; int index; crc=0; for(index=0;index>7)^((mssg[index>>3]>>(index&7))&1)) { crc=(crc<<1)^_poly; } else { crc<<=1; } } for(index=0;index<8;index++) { crcReversed=(crcReversed<<1)|((crc>>index)&1); } return(crcReversed); } jpnevulator-2.3.6/crc8.h000066400000000000000000000016651371000200300150750ustar00rootroot00000000000000/* jpnevulator - serial reader/writer * Copyright (C) 2006-2020 Freddy Spierenburg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __CRC8_H #define __CRC8_H extern void crc8PolyInit(unsigned char); extern unsigned char crc8Calculate(unsigned char *,int); #endif jpnevulator-2.3.6/dependencies.in000066400000000000000000000013161371000200300170340ustar00rootroot00000000000000main.o: main.c jpnevulator.h options.h list.h misc.h byte.h options.o: options.c options.h list.h misc.h byte.h jpnevulator.h io.h \ crc16.h crc8.h interface.h tty.h pty.h jpnevulator.o: jpnevulator.c jpnevulator.h options.h list.h misc.h byte.h \ io.h interface.h checksum.h crc16.h crc8.h byte.o: byte.c byte.h interface.o: interface.c options.h list.h misc.h byte.h jpnevulator.h \ interface.h tty.o: tty.c jpnevulator.h options.h list.h misc.h byte.h interface.h \ tty.h pty.o: pty.c jpnevulator.h options.h list.h misc.h byte.h interface.h \ pty.h io.o: io.c io.h options.h list.h misc.h byte.h jpnevulator.h checksum.o: checksum.c crc16.o: crc16.c crc8.o: crc8.c list.o: list.c list.h misc.o: misc.c misc.h jpnevulator-2.3.6/interface.c000066400000000000000000000107561371000200300161720ustar00rootroot00000000000000/* jpnevulator - serial reader/writer * Copyright (C) 2006-2020 Freddy Spierenburg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include "options.h" #include "jpnevulator.h" #include "interface.h" #include "list.h" void interfaceInitialize(void) { listInitialize(&_jpnevulatorOptions.interface); } static int cmpr(void *p,void *q) { return(!strcasecmp(((struct interface *)p)->name,(char *)q)); } enum interfaceRtrn interfaceAdd(char *name,int (*interfaceOpen)(char *,int),int (*interfaceControlGet)(int,char *),void (*interfaceControlWrite)(FILE *,int),void (*interfaceClose)(int)) { struct interface *interface; int charactersPrinted; char error[1024]; char *alias; /* Was an interface name given? */ if(name==NULL) { /* Nope. Since the interface name is optional for certain types of interfaces, make sure * we have some sort of empty value to work with. */ name=""; } else { /* Is this interface already present in our list of interfaces? */ if(listSearch(&_jpnevulatorOptions.interface,cmpr,(void *)name)!=NULL) { return(interfaceRtrnDouble); } } interface=(struct interface *)malloc(sizeof(struct interface)); if(interface==NULL) { return(interfaceRtrnMemory); } /* Did the user give us an alias for the interface name? */ if((alias=strstr(name,_jpnevulatorOptions.aliasSeparator))!=NULL) { /* Yes, so split the alias from the name. */ *alias='\0'; /* Advance the alias to the real beginning of it. */ alias+=strlen(_jpnevulatorOptions.aliasSeparator); /* And put it in our alias reference. */ charactersPrinted=sprintf(interface->alias,"%.*s",(int)sizeof(interface->alias)-1,alias); if(charactersPrinted!=strlen(alias)) { fprintf(stderr,"%s: interface alias %s truncated to %d chars -> %s\n",PROGRAM_NAME,alias,charactersPrinted,interface->alias); } } else { /* No, empty the alias. */ strcpy(interface->alias,""); } charactersPrinted=sprintf(interface->name,"%.*s",(int)sizeof(interface->name)-1,name); if(charactersPrinted!=strlen(name)) { fprintf(stderr,"%s: interface %s truncated to %d chars -> %s\n",PROGRAM_NAME,name,charactersPrinted,interface->name); } interface->fd=interfaceOpen(interface->name,sizeof(interface->name)-1); if(interface->fd==-1) { snprintf(error,sizeof(error)-1,"%s: Unable to open interface %s",PROGRAM_NAME,interface->name); perror(error); return(interfaceRtrnOpen); } /* Initialize the byte count. We have not received/send any bytes yet. */ interface->byteCount=0UL; /* Put the control call-back in place and get the current state of the control bits if needed. */ interface->controlGet=interfaceControlGet; interface->controlWrite=interfaceControlWrite; if(boolIsSet(_jpnevulatorOptions.control)) { interface->control=interfaceControlGet(interface->fd,interface->name); } /* Add our interface to the list of interfaces. */ if(listAppend(&_jpnevulatorOptions.interface,(void *)interface)!=listRtrnOk) { fprintf(stderr,"%s: unable to add interface %s to the list of interfaces!\n",PROGRAM_NAME,interface->name); return(interfaceRtrnList); } /* Store a reference to the function to close this interface. */ interface->close=interfaceClose; /* Advance to this next position. so all interfaces will be inline with the order the user gave us. */ listNext(&_jpnevulatorOptions.interface); return(interfaceRtrnOk); } int interfaceControlGet(struct interface *interface) { return(interface->controlGet(interface->fd,interface->name)); } void interfaceControlWrite(struct interface *interface,FILE *output,int control) { interface->controlWrite(output,control); } static void garbageCollect(void *data) { struct interface *interface; interface=(struct interface *)data; interface->close(interface->fd); free(interface); } void interfaceDestroy(void) { listDestroy(&_jpnevulatorOptions.interface,garbageCollect); } jpnevulator-2.3.6/interface.h000066400000000000000000000031551371000200300161720ustar00rootroot00000000000000/* jpnevulator - serial reader/writer * Copyright (C) 2006-2020 Freddy Spierenburg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __INTERFACE_H #define __INTERFACE_H #define INTERFACE_NAME_LENGTH 512 struct interface { char name[INTERFACE_NAME_LENGTH+1]; char alias[INTERFACE_NAME_LENGTH+1]; int fd; unsigned long byteCount; int control; void (*close)(int); int (*controlGet)(int,char *); void (*controlWrite)(FILE *,int); }; enum interfaceRtrn { interfaceRtrnOk=0, interfaceRtrnDouble, interfaceRtrnMemory, interfaceRtrnOpen, interfaceRtrnList }; #define interfacePrint(x) (strlen((x)->alias)>0?(x)->alias:(x)->name) extern void interfaceInitialize(void); extern enum interfaceRtrn interfaceAdd(char *,int (*)(char *,int),int (*)(int,char *),void (*)(FILE *,int),void (*)(int)); extern int interfaceControlGet(struct interface *); extern void interfaceControlWrite(struct interface *,FILE *,int); extern void interfaceDestroy(void); #endif jpnevulator-2.3.6/io.c000066400000000000000000000024151371000200300146320ustar00rootroot00000000000000/* jpnevulator - serial reader/writer * Copyright (C) 2006-2020 Freddy Spierenburg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "io.h" #include "options.h" #include "jpnevulator.h" #define ioIsStdio() ((strcmp(_jpnevulatorOptions.io,ioMAGIC)==0)||strcmp(_jpnevulatorOptions.io,"-")==0) FILE *ioOpen(char *mode) { if(ioIsStdio()) { FILE *ioHandle; if(strstr(mode,"r")!=NULL) { ioHandle=stdin; } else { ioHandle=stdout; } return(ioHandle); } else { return(fopen(_jpnevulatorOptions.io,mode)); } } void ioClose(FILE *fd) { if(!ioIsStdio()) { fclose(fd); } } jpnevulator-2.3.6/io.h000066400000000000000000000017211371000200300146360ustar00rootroot00000000000000/* jpnevulator - serial reader/writer * Copyright (C) 2006-2020 Freddy Spierenburg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __IO_H #define __IO_H #include #define ioMAGIC "xi2aeniJeeHoo4wuohQuu7ioiev5eiJe" extern FILE *ioOpen(char *); extern void ioClose(FILE *); #endif jpnevulator-2.3.6/jpnevulator.1000066400000000000000000000232631371000200300165160ustar00rootroot00000000000000.TH JPNEVULATOR "1" "July 2020" "jpnevulator 2.3.6" "User Commands" .SH NAME jpnevulator \- Just another serial sniffer .SH SYNOPSIS .B jpnevulator [\fIOPTION\fR]... <\fIFILE\fR> .SH DESCRIPTION .PP jpnevulator is a handy serial sniffer. You can use it to send data on a serial device too. You can read or write from/to one or more serial devices at the same time. .PP In write (\-\-write) mode data to be sent on the serial device(s) is read from a file or stdin in base (\-\-base) defined notation. Data is sent on the serial device(s) line by line. .PP In read (\-\-read) mode data to be read from the serial device(s) is written to a file or stdout in base (\-\-base) defined notation. Skim through the options for several enhancements in the output. It's even possible to pass(\-\-pass) on the data between the several serial devices. .PP Mandatory arguments to long options are mandatory for short options too. .PP Generic options: .TP \fB\-B\fR, \fB\-\-base\fR=\fIBASE\fR Specify the base unit of read/write bytes. Currently only 2=binary and 16=hexadecimal are supported. The default base unit is hexadecimal. Selecting hexadecimal as the base unit, the input format is FD or 0xFD. Of course all input is treated case-insensitive. Spaces may or may not be included in the input. So DEADBEEF is exactly the same as DE AD BE EF. Selecting binary as the base unit, the input format is 01000010. Spaces may or may not be included in the input. So 01000010111 is exactely the same as 01000010 111. Yes, that's right! It's perfectly fine to write less than 8 bits, excluding the leading zeros. Please understand if doing so, spaces are needed to separate the individual bytes in this case, but that's obvious. If no spaces are used, the parses reads the first 8 bits before it continues with the next, as shown in the example above. .TP \fB\-l\fR, \fB\-\-alias\-separator\fR Use the given string as the alias separator. See \-\-tty for more information. .TP \fB\-f\fR, \fB\-\-file\fR=\fINAME\fR In write mode read the contents of the file given and send them on the serial device(s) and in read mode write the contents of the serial device(s) to the file given. .TP \fB\-h\fR, \fB\-\-help\fR Shows a brief list of options. .TP \fB\-o\fR, \fB\-\-count\fR=\fIBYTES\fR Exit after reading / writing the given amount of bytes. .TP \fB\-r\fR, \fB\-\-read\fR Put the program in read mode. This way you read the data from the given serial device(s) and write it to the file given or stdout if none given. See the read options section for more read specific options. .TP \fB\-t\fR, \fB\-\-tty\fR=\fINAME:ALIAS\fR The serial device to read from or write to. Use multiple times to read/write from/to more than one serial device(s). For handy reference you can also separate an alias from the tty name with a collon ':'. If a collon is for some strange reason part of your device name, you can use the \-\-alias\-separator option to specify another separation string. If an alias is given it will be used as the name of the serial device. .TP \fB\-v\fR, \fB\-\-version\fR Output the version information, a small GPL notice and exit. .TP \fB\-w\fR, \fB\-\-write\fR Put the program in write mode. This way you read data from a given file or stdin if none given and write it to the serial device(s) given. See the write options section for more write specific options. .PP Read options: .TP \fB\-a\fR, \fB\-\-ascii\fR Besides the normal output of the data, also display an extra column with the data in the ASCII representation. Non printable characters are displayed as a dot '.'. The ASCII data is displayed after the normal data. .TP \fB\-b\fR, \fB\-\-byte\-count\fR Besides the normal output also display an extra column with the current index number of the byte in the output. These numbers are displayed in front of the normal output. When readin from multiple serial devices at the same time the index number will increase per serial device. .TP \fB\-C\fR, \fB\-\-control\fR Monitor modem control bits (line enable, data terminal ready, request to send, secondary TXD, secondary RXD, clear to send, carrier detect, ring and data set ready) too and notify changes. Use the \-\-control\-poll option to specify how often to poll for the bits. .TP \fB\-D\fR, \fB\-\-control\-poll\fR=\fIMICROSECONDS\fR The control poll is the amount of microseconds to wait in between two checks of the modem control bits if nothing else is happening. .TP \fB\-P\fR, \fB\-\-pass\fR This one passes all the data between the serial devices. Handy if you want to put your serial sniffer in between the serial devices you want to sniff. .TP \fB\-q\fR, \fB\-\-pty\fR=\fI:ALIAS\fR The pseudo-terminal device to read from. Use multiple times to read from more than one pseudo-terminal device(s). For handy reference you can also use an alias to name the pty. Make sure it starts with a collon ':'. Use the \-\-alias\-separator option if you for some reason don't like to use a collon. If an alias is given it will be used as the name of the pseudo-terminal device. .TP \fB\-e\fR, \fB\-\-timing\-delta\fR=\fIMICROSECONDS\fR The timing delta is the amount of microseconds between two bytes that the latter is considered to be part of a new package. The default is 100 milliseconds. Use this option in conjunction with the \-\-timing\-print option. .TP \fB\-g\fR, \fB\-\-timing\-print\fR Print a line of timing information before every continues stream of bytes. When multiple serial devices are given also print the name or alias of the device where the data is coming from. .TP \fB\-i\fR, \fB\-\-width\fR=\fIWIDTH\fR The number of bytes to display on one line. The default is 16. .TP \fB\-A\fR, \fB\-\-append\fR Append to the output file instead of overwriting. The default is to overwrite. .TP \fB\-S\fR, \fB\-\-append\-separator\fR Use the given string as the append separator. The string is processed and the '\\n' sequence transforms into a real newline. So far no other sequences do anything special. The default is a single newline character. .PP Write options: .TP \fB\-c\fR, \fB\-\-checksum\fR Append a single checksum byte to the line of data written to the serial device(s) chosen. This checksum is a simple modulo 256 addition of all input bytes on a line. .TP \fB\-z\fR, \fB\-\-crc8\fR=\fIPOLY\fR Append a crc8 checksum to the line of data written to the serial device(s) chosen. Use the optionally given poly as the polynomial. Specify the polynomial as hexadecimal value, as in 0x07 (the default). .TP \fB\-y\fR, \fB\-\-crc16\fR=\fIPOLY\fR Append a crc16 checksum to the line of data written to the serial device(s) chosen. Use the optionally given poly as the polynomial. Specify the polynomial as hexadecimal value, as in 0xA001 (the default). .TP \fB\-k\fR, \fB\-\-delay\-byte\fR=\fIMICROSECONDS\fR This delay is an optional amount of microseconds to wait in between every input byte is sent on the serial device(s). .TP \fB\-d\fR, \fB\-\-delay\-line\fR=\fIMICROSECONDS\fR This delay is an optional amount of microseconds to wait in between every input line is sent on the serial device(s). .TP \fB\-j\fR, \fB\-\-fuck\-up\fR This is the special fuck up option. When the calculation of a checksum is chosen (see checksum and crc* options) the checksum will be crippled on purpose. Carefully named after the special Jan Arie de Bruin 'fuck up crc' button. .TP \fB\-n\fR, \fB\-\-no\-send\fR Do not actually send the bytes on the serial device(s). Rather pointless, but seemed one day long ago to be a rather handy feature. .TP \fB\-p\fR, \fB\-\-print\fR Besided sending the data on the serial device(s) also write the data to stdout. .TP \fB\-s\fR, \fB\-\-size\fR=\fISIZE\fR The maximum number of bytes per line to send on the serial device(s). The default is 22, coming from back in the Cham2 days of the program. .SH DIAGNOSTICS Normally, exit status is 0 if the program did run with no problem whatsoever. If the exit status is not equal to 0 an error message is printed on stderr which should help you solve the problem. .SH BUGS .PP \fBOrder of bytes broke when reading several tty devices at once\fR .PP The display of incoming bytes can be broke if you use multiple tty devices to read from. At the moment I do not have a solution for this problem. Since I use select() to watch the several tty devices and after the select() I have to read() them one by one, I can not completely 100% display which bytes came after which on different tty devices. Take the example below: .PP .RS .nf .sp .5 $ jpnevulator \-\-ascii \-\-timing\-print \-\-tty /dev/ttyS0 \-\-tty /dev/ttyUSB0 \-\-read 2006-05-30 13:23:49.461075: /dev/ttyS0 00 00 05 3B 0D 00 00 05 ...;.... 2006-05-30 13:23:49.461113: /dev/ttyUSB0 00 05 3B 0D 00 00 05 3B 0D ..;....;. 2006-05-30 13:23:49.473074: /dev/ttyS0 3B 0D 00 00 05 3B 0D ;....;. 2006-05-30 13:23:49.473105: /dev/ttyUSB0 00 12 05 06 39 00 12 05 06 39 1F 00 22 80 00 0E ....9....9.."... $ .sp .5 .fi .RE .PP And now see the order in which things really got sent on the line: .PP .RS .nf .sp .5 /dev/ttyS0: 00 00 05 3B 0D /dev/ttyUSB0: 00 00 05 3B 0D /dev/ttyS0: 00 00 05 3B 0D /dev/ttyUSB0: 00 00 05 3B 0D /dev/ttyS0: 00 00 05 3B 0D /dev/ttyUSB0: 00 00 05 3B 0D 00 12 05 06 39 00 12 05 06 39 ... .sp .5 .fi .RE .PP As you can see /dev/ttyUSB0 receives the echo of all things sent by /dev/ttyS0. This is exactly what happens. But since there does exist a small time between the select() who is happy expressing something is available and the read() who does get the available data, some extra data will be available. I have no idea on how I can use high level system call like select() and read() and be still able to put the bytes in the correct order. Anyone an idea? .SH AUTHOR Written by Freddy Spierenburg. .SH "REPORTING BUGS" Report bugs to . .SH COPYRIGHT Copyright \(co 2006-2020 Freddy Spierenburg jpnevulator-2.3.6/jpnevulator.c000066400000000000000000000514101371000200300165730ustar00rootroot00000000000000/* jpnevulator - serial reader/writer * Copyright (C) 2006-2020 Freddy Spierenburg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include "jpnevulator.h" #include "byte.h" #include "io.h" #include "interface.h" #include "checksum.h" #include "crc16.h" #include "crc8.h" #include "misc.h" struct jpnevulatorOptions _jpnevulatorOptions; static void messageChecksumAdd(unsigned char *message,int *size) { unsigned short checksum; switch(_jpnevulatorOptions.checksum) { case checksumTypeCrc8: { checksum=crc8Calculate(message,*size); /* Really dirty trick to put a 0x0D (CR) at the end of the message. But * it's Friday afternoon and frankly I don't care to code this once in * a life time usage properly. */ checksum|=0x0D00; break; } case checksumTypeCrc16: { checksum=crc16Calculate(message,*size); break; } default: case checksumTypeChecksum: { checksum=checksumCalculate(message,*size); break; } } message[(*size)++]=checksum&0xFF; message[(*size)++]=(checksum>>8)&0xFF; } /* Nice way of leaving no traces... * ...the more we know, the more we return. */ #define jpnevulatorGarbageCollect() { \ interfaceDestroy(); \ if(input!=NULL) { \ ioClose(input); \ } \ if(message!=NULL) { \ free(message); \ } \ } enum jpnevulatorRtrn jpnevulatorWrite(void) { int byte; struct interface *interface; FILE *input=NULL; unsigned char *message=NULL; int index; int line; bool_t writingStart; /* Open our input file. */ input=ioOpen("r"); if(input==NULL) { perror(PROGRAM_NAME": Unable to open input"); jpnevulatorGarbageCollect(); return(jpnevulatorRtrnNoInput); } /* Allocate memory for the messages to send. */ message=(unsigned char *)malloc(sizeof(message[0])*_jpnevulatorOptions.size); if(message==NULL) { perror(PROGRAM_NAME": Unable to allocate memory for message"); jpnevulatorGarbageCollect(); return(jpnevulatorRtrnNoMessage); } /* By default we do not expect to start writing because we have reached the * maximum amount (--count) of bytes. */ boolReset(writingStart); /* Collect and send the messages. We collect the messages byte by byte from the * input and send them on the line when we receive an end-of-line. Take notice * of this rather strange test. We need to test for writingStart too, since it's * possible the counter equals zero and we still need to write some bytes on the * line. I myself will probably not understand why within a couple of months, hence * this special remark. Not too many details, since I like to keep myself puzzeled * every once in a while. ;) */ line=1; for(index=0;(boolIsSet(writingStart)||(_jpnevulatorOptions.count!=0))&&((byte=byteGet(input,_jpnevulatorOptions.base))!=byteRtrnEOF);) { if(boolIsSet(writingStart)) { byte=byteRtrnEOL; } switch(byte) { case byteRtrnEOL: { int n; /* Add a checksum to the message if requested. */ if(_jpnevulatorOptions.checksum!=checksumTypeNone) { messageChecksumAdd(message,&index); if(boolIsSet(_jpnevulatorOptions.checksumFuckup)) { /* Subtract one from the last checksum byte of the message if the user * request to fuck up the checksum. */ message[index-1]-=1; } } /* Send the message on the line. */ if(boolIsSet(_jpnevulatorOptions.send)) { if((interface=(struct interface *)listFirst(&_jpnevulatorOptions.interface))!=NULL) { do { /* Delay between bytes if requested. */ if(_jpnevulatorOptions.delayByte>0) { int byteIndex; for(byteIndex=0;byteIndexfd,&(message[byteIndex]),1); if(n<0) { fprintf(stderr,"%s: %s: write of line %d byte %d failed(%d).\n",PROGRAM_NAME,interfacePrint(interface),line,byteIndex,n); } usleep(_jpnevulatorOptions.delayByte); } } else { n=write(interface->fd,message,sizeof(message[0])*index); if(n<0) { fprintf(stderr,"%s: %s: write of line %d failed(%d).\n",PROGRAM_NAME,interfacePrint(interface),line,n); } } } while((interface=(struct interface *)listNext(&_jpnevulatorOptions.interface))!=NULL); } } /* Print the message if requested. */ if(boolIsSet(_jpnevulatorOptions.print)) { for(n=0;n0) { usleep(_jpnevulatorOptions.delayLine); } /* Make sure this is reset again, otherwise we will never stop reading from stdin even though we have * already refused to write more data. :) */ boolReset(writingStart); /* Start again with a new message and increase the line counter. */ index=0; line++; break; } case byteRtrnUnknown: { /* Warn the user if we read invalid characters in the input file. We only give a warning and still * send the message. The user might now what he or she is doing :-) */ fprintf(stderr,"%s: invalid characters on input line %d. Message can be corrupted.\n",PROGRAM_NAME,line); break; } default: { /* Place the new input byte into the message. Check if there is enough room for * this new byte and do leave some room(2 bytes) for the checksum if necessary. * Nice trick ;-) */ if(index<(_jpnevulatorOptions.size-(_jpnevulatorOptions.checksum*2))) { message[index++]=byte; } else { fprintf(stderr,"%s: Input line %d too big. Increase message size (--size).\n",PROGRAM_NAME,line); } /* Do we count the amount of bytes to write and if so are we finished writing? */ if((_jpnevulatorOptions.count>0)&&(--_jpnevulatorOptions.count==0)) { boolSet(writingStart); } break; } } } /* Free allocated memory and close files opened. */ jpnevulatorGarbageCollect(); return(jpnevulatorRtrnOk); } #undef jpnevulatorGarbageCollect static void asciiWrite(FILE *output,char *ascii,int asciiSize,int *bytesWritten,bool_t fill) { if((*bytesWritten)!=0) { if(boolIsSet(_jpnevulatorOptions.ascii)) { if(boolIsSet(fill)) { int index; for(index=*bytesWritten;index<_jpnevulatorOptions.width;index++) { switch(_jpnevulatorOptions.base) { #define BASE(base,name,width) \ case name: { \ fprintf(output,"%s",SPACES(width+1)); \ break; \ } BASES #undef BASE } } } fprintf(output,"\t%s",ascii); memset(ascii,'\0',asciiSize); } fprintf(output,"\n"); *bytesWritten=0; } } static void headerWrite( FILE *output, char *ascii,int asciiSize, int *bytesWritten, struct interface *interfaceReader,char *interfaceNameCopy,int interfaceNameCopySize, struct timeval *timeCurrent,struct timeval *timeLast ) { struct tm *time; *timeLast=*timeCurrent; gettimeofday(timeCurrent,NULL); if( boolIsSet(_jpnevulatorOptions.timingPrint)&& ((memcmp(interfaceNameCopy,interfaceReader->name,interfaceNameCopySize)!=0)|| (((((timeCurrent->tv_sec-timeLast->tv_sec)*1000000L)+timeCurrent->tv_usec)-timeLast->tv_usec)>_jpnevulatorOptions.timingDelta)) ) { asciiWrite(output,ascii,asciiSize,bytesWritten,boolTrue); time=localtime(&(timeCurrent->tv_sec)); fprintf( output, "%04d-%02d-%02d %02d:%02d:%02d.%06ld:", time->tm_year+1900,time->tm_mon+1,time->tm_mday, time->tm_hour,time->tm_min,time->tm_sec,timeCurrent->tv_usec ); /* If more than one interface is given we want it always to * be displayed as part of the printing of the timing. It's * way to confusing otherwise. */ if(listElements(&_jpnevulatorOptions.interface)>1) { fprintf(output," %s",interfacePrint(interfaceReader)); } memcpy(interfaceNameCopy,interfaceReader->name,interfaceNameCopySize); fprintf(output,"\n"); } else { if( (listElements(&_jpnevulatorOptions.interface)>1)&& (memcmp(interfaceNameCopy,interfaceReader->name,interfaceNameCopySize)!=0) ) { asciiWrite(output,ascii,asciiSize,bytesWritten,boolTrue); fprintf(output,"%s\n",interfacePrint(interfaceReader)); memcpy(interfaceNameCopy,interfaceReader->name,interfaceNameCopySize); } } } static void controlHandle( FILE *output, char *ascii,int asciiSize, int *bytesWritten, struct interface *interfaceReader,char *interfaceNameCopy,int interfaceNameCopySize, struct timeval *timeCurrent,struct timeval *timeLast ) { int control; control=interfaceControlGet(interfaceReader); if(control!=interfaceReader->control) { /* We need this explicit call to asciiWrite, even though headerWrite will call asciiWrite() itself probably. Yes, the probably * means exactly what it says probably. It's possible that controlHandle() gets called and headerWrite() does not think it * needs to write a new header and so no need to write the ascii data, but new control data will get written before the ascii * data is written. That is, if the modem control bits change within the timing delta on an interface that has just received * data. Blam, nasty output! This explicit call to asciiWrite() fixes that. */ asciiWrite(output,ascii,asciiSize,bytesWritten,boolTrue); headerWrite(output,ascii,asciiSize,bytesWritten,interfaceReader,interfaceNameCopy,interfaceNameCopySize,timeCurrent,timeLast); interfaceControlWrite(interfaceReader,output,control); interfaceReader->control=control; } } /* Nice way of leaving no traces... * ...the more we know, the more we return. */ #define jpnevulatorGarbageCollect() { \ interfaceDestroy(); \ if(output!=NULL) { \ ioClose(output); \ } \ if(message!=NULL) { \ free(message); \ } \ if(ascii!=NULL) { \ free(ascii); \ } \ } enum jpnevulatorRtrn jpnevulatorRead(void) { FILE *output=NULL; unsigned char *message=NULL; char *ascii=NULL; int asciiSize; ssize_t bytesRead; int bytesWritten; struct timeval timeCurrent,timeLast,*timeoutPtr,timeout; unsigned long *timeoutReference; int timeoutDelta,timeoutCount; fd_set readfdsReal,readfdsCopy; struct interface *interfaceReader,*interfaceWriter; char interfaceNameCopy[sizeof(interfaceReader->name)]; int nfds; /* Open our output file. */ output=ioOpen(boolIsSet(_jpnevulatorOptions.append)?"a":"w"); if(output==NULL) { perror(PROGRAM_NAME": Unable to open output"); jpnevulatorGarbageCollect(); return(jpnevulatorRtrnNoOutput); } /* In append mode we first check if the file is empty. If not we * first append the given append separator. */ if(boolIsSet(_jpnevulatorOptions.append)) { struct stat outputStat; fstat(fileno(output),&outputStat); if(outputStat.st_size>0) { char *index; /* We need to parse the append separator a little bit and search for * the special newline sequence. Otherwise we simply put the found * character in place. */ for(index=_jpnevulatorOptions.appendSeparator;*index!='\0';index++) { if((*index=='\\')&&(*(index+1)=='n')) { fprintf(output,"\n"); index++; } else { fprintf(output,"%c",*index); } } } } /* Allocate memory for the messages to receive. */ message=(unsigned char *)malloc(sizeof(message[0])*_jpnevulatorOptions.size); if(message==NULL) { perror(PROGRAM_NAME": Unable to allocate memory for message"); jpnevulatorGarbageCollect(); return(jpnevulatorRtrnNoMessage); } /* Allocate memory for the ascii data to print if desired. */ if(boolIsSet(_jpnevulatorOptions.ascii)) { asciiSize=(sizeof(ascii[0])*_jpnevulatorOptions.width)+1; ascii=(char *)malloc(asciiSize); if(ascii==NULL) { perror(PROGRAM_NAME": Unable to allocate memory for ascii data"); jpnevulatorGarbageCollect(); return(jpnevulatorRtrnNoAscii); } memset(ascii,'\0',asciiSize); } else { /* No real reason to do this, but it makes the compiler happy. If this next line is not * here the compiler generates this warning below: * * jpnevulator.c:407:19: warning: ‘asciiSize’ may be used uninitialized in this function [-Wuninitialized] * * So let's initialize asciiSize anyway. */ asciiSize=0; } /* Initialize our last time to be far enough from the current time. Far * enough is a little bit more than --timing-delta away from it. This way * our first data will always gets it's timing information if requested. Take * notice that I use timeCurrent here, since that one will be assigned to * timeLast before gettimeofday() in the loop. */ gettimeofday(&timeCurrent,NULL); timeCurrent.tv_sec-=(_jpnevulatorOptions.timingDelta/1000000L)+1; /* Setup our set of read file descriptors to watch. We set up the copy * so we don't have to parse our list of interfaces every time we iterate. */ FD_ZERO(&readfdsCopy); nfds=0; if((interfaceReader=(struct interface *)listFirst(&_jpnevulatorOptions.interface))!=NULL) { do { FD_SET(interfaceReader->fd,&readfdsCopy); nfds=max(nfds,interfaceReader->fd); } while((interfaceReader=(struct interface *)listNext(&_jpnevulatorOptions.interface))!=NULL); } else { fprintf(stderr,"%s: No available interface to read from\n",PROGRAM_NAME); jpnevulatorGarbageCollect(); return(jpnevulatorRtrnNoTTY); } /* Clear our copy of the interface name, so if multiple interfaces are * given it will print the first one and only on a change the name * of the interface will be printed. */ memset(interfaceNameCopy,'\0',sizeof(interfaceNameCopy)); /* Do we need a timeout? We only need this when we also display the ASCII * values for the received bytes. In that case we use the timeout to display * the ASCII values automatically, otherwise they will never appear when less * then the line length bytes are received and no more bytes are coming. * * And of course we need the timeout too when we poll for modem control bits. */ timeoutCount=0; timeoutDelta=0; if(boolIsSet(_jpnevulatorOptions.ascii)||boolIsSet(_jpnevulatorOptions.control)) { timeoutPtr=&timeout; /* What timeout shall we use? If only one of the two is activated use * that one and otherwise use the smallest of the two. */ if(boolIsSet(_jpnevulatorOptions.ascii)&&boolIsSet(_jpnevulatorOptions.control)) { if(_jpnevulatorOptions.timingDelta<_jpnevulatorOptions.controlPoll) { timeoutReference=&_jpnevulatorOptions.timingDelta; timeoutDelta=(_jpnevulatorOptions.controlPoll/_jpnevulatorOptions.timingDelta)+1; } else { timeoutReference=&_jpnevulatorOptions.controlPoll; timeoutDelta=(_jpnevulatorOptions.timingDelta/_jpnevulatorOptions.controlPoll)+1; } } else if(boolIsSet(_jpnevulatorOptions.ascii)) { timeoutReference=&_jpnevulatorOptions.timingDelta; } else { timeoutReference=&_jpnevulatorOptions.controlPoll; } } else { timeoutPtr=NULL; } /* Receive our messages. */ bytesWritten=0; for(;_jpnevulatorOptions.count!=0;) { int index; int rtrn; /* Restore our set of read file descriptors. */ readfdsReal=readfdsCopy; if(timeoutPtr!=NULL) { timeoutPtr->tv_sec=(*timeoutReference)/1000000L; timeoutPtr->tv_usec=(*timeoutReference)%1000000L; } /* Wait and see if anything flows in. */ rtrn=select(nfds+1,&readfdsReal,NULL,NULL,timeoutPtr); if(rtrn==-1) { /* Forgotten why, but we do not do anything here. I once must have had a * very good reason, but I can't recall anymore. Let's just put in * another funny bullshit comment to give this all some kind of meaning. * I start to think it has to do something with the control option. I * don't think you want to write control information every time select * fails. But I could be wrong. It is too long ago. */ } else if(rtrn) { /* If we have received some data, we obviously need to reset out timeout counter. Otherwise it would * be considered cheating. Forgetting to reset this counter results in a slight different interpretation * of the --timing-delta option. Nice BUG, luckily found it myself. */ timeoutCount=0; /* Walk through all our interfaces and see what needs to be done. */ if((interfaceReader=(struct interface *)listFirst(&_jpnevulatorOptions.interface))!=NULL) { do { if(FD_ISSET(interfaceReader->fd,&readfdsReal)) { int size; /* How many bytes should we read? */ if((_jpnevulatorOptions.count>0)&&(_jpnevulatorOptions.count<_jpnevulatorOptions.size)) { /* We need less bytes than the input buffer can handle, so only take what needed. */ size=_jpnevulatorOptions.count; } else { /* Take as many as possible. No limit set or not yet within reach. */ size=_jpnevulatorOptions.size; } bytesRead=read(interfaceReader->fd,message,size); if(bytesRead>0) { /* Are we counting bytes and if so subtract the amount just read. */ if(_jpnevulatorOptions.count>0) { _jpnevulatorOptions.count-=bytesRead; } headerWrite(output,ascii,asciiSize,&bytesWritten,interfaceReader,interfaceNameCopy,sizeof(interfaceNameCopy),&timeCurrent,&timeLast); for(index=0;index=_jpnevulatorOptions.width) { asciiWrite(output,ascii,asciiSize,&bytesWritten,boolFalse); } else if(bytesWritten!=0) { fprintf(output," "); } if((bytesWritten==0)&&boolIsSet(_jpnevulatorOptions.byteCountDisplay)) { fprintf(output,"%08lX\t",interfaceReader->byteCount); } bytePut(output,_jpnevulatorOptions.base,message[index]); /* Increase the byte count for this interface. */ interfaceReader->byteCount++; if(boolIsSet(_jpnevulatorOptions.ascii)) { ascii[bytesWritten]=isprint(message[index])?message[index]:'.'; } bytesWritten++; } /* Does the user want to pass the data between all the interfaces? */ if(boolIsSet(_jpnevulatorOptions.pass)) { struct listElement *interfaceListPosition; /* Save the current position in the interface list, since we are about to traverse * it again. */ interfaceListPosition=listCurrentPositionSave(&_jpnevulatorOptions.interface); /* Traverse the interface list again in search for all the other (not this read) interface. For * every interface found write the read message to it. */ if((interfaceWriter=(struct interface *)listFirst(&_jpnevulatorOptions.interface))!=NULL) { do { if(interfaceWriter->fd!=interfaceReader->fd) { ssize_t n; n=write(interfaceWriter->fd,message,bytesRead); if(n<0) { fprintf(stderr,"%s: %s: write of %ld bytes failed(%ld).\n",PROGRAM_NAME,interfacePrint(interfaceWriter),bytesRead,n); } } } while((interfaceWriter=(struct interface *)listNext(&_jpnevulatorOptions.interface))!=NULL); } /* Restore the current position for the interface list again. */ listCurrentPositionLoad(&_jpnevulatorOptions.interface,interfaceListPosition); } fflush(output); } } /* See if we need to write some control data. */ if(boolIsSet(_jpnevulatorOptions.control)) { controlHandle(output,ascii,asciiSize,&bytesWritten,interfaceReader,interfaceNameCopy,sizeof(interfaceNameCopy),&timeCurrent,&timeLast); } } while((_jpnevulatorOptions.count!=0)&&((interfaceReader=(struct interface *)listNext(&_jpnevulatorOptions.interface))!=NULL)); } } else { /* Another timeout! Do we already need to write our ASCII data? */ if(timeoutCount>=timeoutDelta) { asciiWrite(output,ascii,asciiSize,&bytesWritten,boolTrue); timeoutCount=0; } else { timeoutCount++; } /* See if we need to write some control data. */ if(boolIsSet(_jpnevulatorOptions.control)) { if((interfaceReader=(struct interface *)listFirst(&_jpnevulatorOptions.interface))!=NULL) { do { controlHandle(output,ascii,asciiSize,&bytesWritten,interfaceReader,interfaceNameCopy,sizeof(interfaceNameCopy),&timeCurrent,&timeLast); } while((interfaceReader=(struct interface *)listNext(&_jpnevulatorOptions.interface))!=NULL); } } } } /* Might we possibly still need to write our ASCII data? */ asciiWrite(output,ascii,asciiSize,&bytesWritten,boolTrue); /* And if we didn't wrote our ASCII data we most probably need a newline. */ if(bytesWritten!=0) { fprintf(output,"\n"); } /* Close files opened. */ jpnevulatorGarbageCollect(); return(jpnevulatorRtrnOk); } #undef jpnevulatorGarbageCollect jpnevulator-2.3.6/jpnevulator.h000066400000000000000000000024401371000200300165770ustar00rootroot00000000000000#ifndef __JPNEVULATOR_H /* jpnevulator - serial reader/writer * Copyright (C) 2006-2020 Freddy Spierenburg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #define __JPNEVULATOR_H #include "options.h" #define PROGRAM_NAME "jpnevulator" #define PROGRAM_VERSION "2.3.6" enum jpnevulatorRtrn { jpnevulatorRtrnOk=0, jpnevulatorRtrnOptions, jpnevulatorRtrnNoTTY, jpnevulatorRtrnNoInput, jpnevulatorRtrnNoOutput, jpnevulatorRtrnNoMessage, jpnevulatorRtrnNoAscii }; extern struct jpnevulatorOptions _jpnevulatorOptions; extern enum jpnevulatorRtrn jpnevulatorWrite(void); extern enum jpnevulatorRtrn jpnevulatorRead(void); #endif jpnevulator-2.3.6/list.c000066400000000000000000000261311371000200300151770ustar00rootroot00000000000000/* jpnevulator - serial reader/writer * Copyright (C) 2006-2020 Freddy Spierenburg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "list.h" /* This routine initializes our list meta data. */ enum listRtrn listInitialize(list_t *list) { list->first=list->last=list->current=NULL; list->elements=0; return(listRtrnOk); } /* This routine appends one element to the list. It will * be appended just after the current element. */ enum listRtrn listAppend(list_t *list,void *data) { struct listElement *p; /* Allocate memory for the new element. */ p=(struct listElement *)malloc(sizeof(p[0])); if(p!=NULL) { /* Assign the data to the element. */ p->data=data; /* Do any elements exist already in the list? */ if(list->current!=NULL) { /* Yes, we will append this one after the current one. */ /* Is the current one the last one in the list? */ if(list->current->next!=NULL) { /* No, so we have to let the one after the current know * that we will be the previous element from now on. */ list->current->next->previous=p; } else { /* Yes, so we tell the meta data that we are the last in the * list right now. */ list->last=p; } /* Our next one will be the next one of the current element. */ p->next=list->current->next; /* Our previous one will be the current element. */ p->previous=list->current; /* And of course we should let the current element know that * we come next in the list. */ list->current->next=p; } else { /* No, there do not exist any elements in the list yet so we * are the first, last and current. */ p->previous=p->next=NULL; list->first=list->last=list->current=p; } /* Increase the amount of elements known to the list. */ list->elements++; return(listRtrnOk); } else { return(listRtrnMemory); } } /* This routine insert one element to the list. It will * be inserted just before the current element. */ enum listRtrn listInsert(list_t *list,void *data) { struct listElement *p; /* Allocate memory for the new element. */ p=(struct listElement *)malloc(sizeof(p[0])); if(p!=NULL) { /* Assign the data to the element. */ p->data=data; /* Do any elements exist already in the list? */ if(list->current!=NULL) { /* Yes, we will insert this one before the current one. */ /* Our previous one will be the one before the current element. */ p->previous=list->current->previous; /* We will be the previous one for the current element. */ list->current->previous=p; /* Our next one will be the current element. */ p->next=list->current; /* Are we now the first one in the list? */ if(p->previous!=NULL) { /* No, so we have to let the one before us know that we * will be the next element from now on. */ p->previous->next=p; } else { /* Yes, so we tell the meta data that we are the first in the * list right now. */ list->first=p; } } else { /* No, there do not exist any elements in the list yet so we * are the first, last and current. */ p->previous=p->next=NULL; list->first=list->last=list->current=p; } /* Increase the amount of elements known to the list. */ list->elements++; return(listRtrnOk); } else { return(listRtrnMemory); } } /* This routine removes the current element from the list. If none available * it does nothing. */ void listRemove(list_t *list,void (*garbageCollect)(void *)) { /* Any elements in the list available? */ if(list->current!=NULL) { struct listElement *p; /* Yes, let's remove the current one. */ p=list->current; /* Is it the first element in the list? */ if(p->previous!=NULL) { /* No, so let the element before know what the new element after * it will be. */ list->current->previous->next=p->next; } else { /* Yes, so update the first meta data. */ list->first=p->next; } /* Is it the last element in the list? */ if(p->next!=NULL) { /* No, so let the element after it know what the new element * before it will be. */ p->next->previous=p->previous; } else { /* Yes, so update the last meta data. */ list->last=p->previous; } /* Now we need to update the current meta data. Does an element * after this element exist? */ if(p->next!=NULL) { /* Yes, this next element will be the new current element. */ list->current=p->next; } else { /* No, the element before this one will be the new current element. */ list->current=p->previous; } /* Run the garbage collector for this item if required. */ if(garbageCollect!=NULL) { garbageCollect(p->data); } /* Return system resources. */ free(p); /* Decrease the amount of elements known to the list. */ list->elements--; } } /* This routine returns the data of the current element or NULL if * none is available. */ void *listCurrent(list_t *list) { return(list->current!=NULL?list->current->data:NULL); } /* This routine returns the first element in the list or NULL if * none is available. It also sets the current meta data to be the * first element in the list. */ void *listFirst(list_t *list) { list->current=list->first; return(listCurrent(list)); } /* This routine returns the next element in the list or NULL if * none is available. It also advances the current meta data to the * next element. */ void *listNext(list_t *list) { if((list->current!=NULL)&&(list->current->next!=NULL)) { list->current=list->current->next; return(listCurrent(list)); } else { return(NULL); } } /* This routine returns the previous element in the list or NULL if * none is available. It also sets the current meta data to the * previous element. */ void *listPrevious(list_t *list) { if((list->current!=NULL)&&(list->current->previous!=NULL)) { list->current=list->current->previous; return(listCurrent(list)); } else { return(NULL); } } /* This routine returns the last element in the list or NULL if * none is available. It also sets the current meta data to be the * last element in the list. */ void *listLast(list_t *list) { list->current=list->last; return(listCurrent(list)); } /* This routine search the list for a specific element given by needle and * returns that element or NULL if it's not found. */ void *listSearch(list_t *list,int (*compare)(void *,void *),void *needle) { void *data; int found; /* At first we of course didn't find anything and especially when we do not receive a list we make sure we did not found anything as of yet. */ found=0; /* Does any element exist in the list? */ if((data=listFirst(list))!=NULL) { do { /* Yes, so search for our element... */ if((found=compare(data,needle))) break; /* ...untill none left. */ } while((data=listNext(list))!=NULL); } /* If we found our data return it, otherwise return NULL. */ return(found?data:NULL); } /* This routine gracefully returns all system resources and empty the * list. */ void listDestroy(list_t *list,void (*garbageCollect)(void *)) { struct listElement *p; /* Search untill no elements are left... */ while(list->first!=NULL) { /* First save a reference to our next element. */ p=list->first->next; /* Run the garbage collector for this item if required. */ if(garbageCollect!=NULL) { garbageCollect(list->first->data); } /* Then return resources for the current one. */ free(list->first); /* And at last advance to our next element. */ list->first=p; } /* Reset all our meta data. */ list->first=list->last=list->current=NULL; list->elements=0; } #if 0 #include #include #include #include int cmpr(void *p,void *q) { return(!strcmp((char *)p,(char *)q)); } void garbageCollect(void *data) { printf("Taking down: %s\n",(char *)data); } int main(int argc,char **argv) { list_t list; int index; char *arg; int elements; /* Initialize our list. */ listInitialize(&list); /* Fill our list with all command line arguments given, but skip the * first argument since that's our needle (see below -> listSearch()). */ for(index=2;index=2) { /* ...search for the first one given... */ arg=listSearch(&list,cmpr,(void *)argv[1]); /* ...and tell if it's found. */ printf("searching for [%s]... %sfound\n",argv[1],arg!=NULL?"":"not "); } /* Randomly remove half of the arguments. */ srandom(getpid()); for(elements=listElements(&list)/2;elements>0;elements--) { int element=(random()%(argc-2))+2; arg=listSearch(&list,cmpr,(void *)argv[element]); printf("Removing [%s]... ",argv[element]); if(arg!=NULL) { listRemove(&list,NULL); printf("ok\n"); } else { printf("not in the list!\n"); } } /* Print all our arguments from the first to the last. */ printf("First to last:\n"); /* Does any argument exist? */ if((arg=(char *)listFirst(&list))!=NULL) { /* Yes, we print it and... */ do { printf("[%s]\n",arg); /* ...advance to the next argument, untill none left. */ } while((arg=(char *)listNext(&list))!=NULL); } else { printf("empty list\n"); } /* Print all our arguments from the last to the first. */ printf("Last to first:\n"); /* Does any argument exist? */ if((arg=(char *)listLast(&list))!=NULL) { /* Yes, we print it and... */ do { printf("[%s]\n",arg); /* ...advance to the next argument, untill none left. */ } while((arg=(char *)listPrevious(&list))!=NULL); } else { printf("empty list\n"); } /* Destroy the list and all its elements. */ listDestroy(&list,garbageCollect); return(0); } #endif jpnevulator-2.3.6/list.h000066400000000000000000000040221371000200300151770ustar00rootroot00000000000000/* jpnevulator - serial reader/writer * Copyright (C) 2006-2020 Freddy Spierenburg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LIST_H #define __LIST_H enum listRtrn { listRtrnOk=0, listRtrnMemory }; struct listElement { void *data; struct listElement *previous; struct listElement *next; }; typedef struct list { struct listElement *first; struct listElement *last; struct listElement *current; int elements; } list_t; /* Do notice the use of -> in listElements, so it conforms to all the * other routines in that you need to give the address of the list. */ #define listElements(x) ((x)->elements) extern enum listRtrn listInitialize(list_t *); extern enum listRtrn listAppend(list_t *,void *); extern enum listRtrn listInsert(list_t *,void *); extern void listRemove(list_t *,void (*)(void *)); extern void *listCurrent(list_t *); /* These next two are routines to temporary freeze the current pointer in the list. This * way one can traverse the list within a traverse of the list itself. */ #define listCurrentPositionSave(x) ((x)->current) #define listCurrentPositionLoad(x,y) ((x)->current=y) extern void *listFirst(list_t *); extern void *listNext(list_t *); extern void *listPrevious(list_t *); extern void *listLast(list_t *); extern void *listSearch(list_t *,int (*)(void *,void *),void *); extern void listDestroy(list_t *,void (*)(void *)); #endif jpnevulator-2.3.6/main.c000066400000000000000000000025631371000200300151530ustar00rootroot00000000000000/* jpnevulator - serial reader/writer * Copyright (C) 2006-2020 Freddy Spierenburg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "jpnevulator.h" #include "options.h" int main(int argc,char **argv) { int returnValue; if(optionsParse(argc,argv)!=optionsRtrnOk) { returnValue=jpnevulatorRtrnOptions; } else { switch(_jpnevulatorOptions.action) { case actionTypeRead: { returnValue=jpnevulatorRead(); break; } case actionTypeWrite: { returnValue=jpnevulatorWrite(); break; } case actionTypeNone: default: { /* Should be impossible. :-) */ returnValue=optionsRtrnImpossible; break; } } } return(returnValue); } jpnevulator-2.3.6/misc.c000066400000000000000000000016241371000200300151570ustar00rootroot00000000000000/* jpnevulator - serial reader/writer * Copyright (C) 2006-2020 Freddy Spierenburg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "misc.h" /* This is the string of spaces used by the SPACES() macro. */ char __spaces[]=__SPACES; jpnevulator-2.3.6/misc.h000066400000000000000000000032071371000200300151630ustar00rootroot00000000000000/* jpnevulator - serial reader/writer * Copyright (C) 2006-2020 Freddy Spierenburg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __MISC_H #define __MISC_H #define max(x,y) ((x)>(y)?(x):(y)) #define min(x,y) ((x)<(y)?(x):(y)) typedef char bool_t; #define boolTrue 1 #define boolFalse 0 #define boolSet(x) ((x)=boolTrue) #define boolReset(x) ((x)=boolFalse) #define boolIsSet(x) ((x)==boolTrue) #define boolIsNotSet(x) ((x)==boolFalse) /* This is a nice routine to get a pointer to a string of spaces. If zero is * given you will get a nice empty string. Otherwise you will get a pointer to * a string of exactly the given amount of spaces. If more than the maximum * amount of spaces is given, a pointer to the maximum string spaces string is * returned. */ #define __SPACES " " extern char __spaces[sizeof(__SPACES)]; #define SPACES(amount) (&(__spaces[sizeof(__spaces)-min(amount,sizeof(__spaces)-1)-1])) #endif jpnevulator-2.3.6/options.c000066400000000000000000000270601371000200300157210ustar00rootroot00000000000000/* jpnevulator - serial reader/writer * Copyright (C) 2006-2020 Freddy Spierenburg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include "options.h" #include "jpnevulator.h" #include "io.h" #include "crc16.h" #include "crc8.h" #include "list.h" #include "interface.h" #include "tty.h" #include "pty.h" #include "byte.h" static void usage(void) { printf( "Usage: %s [--version] [--help] [--checksum] [--crc16=poly]\n" " [--crc8=poly] [--fuck-up] [--file=file] [--no-send]\n" " [--delay-line=microseconds] [--delay-byte=microseconds]\n" " [--print] [--size=size] [--tty=tty] [--pty [=alias]] [--width] [--pass]\n" " [--read] [--write] [--timing-print] [--timing-delta=microseconds]\n" " [--ascii] [--alias-separator=separator] [--byte-count]\n" " [--append] [--append-separator=separator] [--control]\n" " [--control-poll=microseconds] [--count=bytes] [--base] \n", PROGRAM_NAME ); } static void optionsDefault(void) { /* Don't add a checksum by default. */ _jpnevulatorOptions.checksum=checksumTypeNone; /* Use the crc16 x^16+x^15+x^2+1 polynomial by default. */ _jpnevulatorOptions.crc16Poly=0xA001; crc16TableCreate(0,_jpnevulatorOptions.crc16Poly); /* Use the crc8 x^8+x^2+x+1 polynomial by default. */ _jpnevulatorOptions.crc8Poly=0x07; crc8PolyInit(_jpnevulatorOptions.crc8Poly); /* Don't fuck up the checksum(wrongly named crc) by default. */ boolReset(_jpnevulatorOptions.checksumFuckup); /* Read/Write standard input/output by default, but use some black magic to find out if a user selects * multiple I/O sources. Which we of course neglect anyway. */ sprintf(_jpnevulatorOptions.io,"%.*s",(int)sizeof(_jpnevulatorOptions.io)-1,ioMAGIC); /* Initialize the list where we store our interfaces. We do not yet add * the default interface. We postpone that up untill we know for sure the user * did not gave us any interface. */ interfaceInitialize(); /* By default we expect to send/receive Cham2 messages. */ _jpnevulatorOptions.size=22; /* By default we send our messages on the serial port... */ boolSet(_jpnevulatorOptions.send); /* ...but we probably don't want to see the messages on the console. Or do we? :-) */ boolReset(_jpnevulatorOptions.print); /* Do not delay between messages by default. */ _jpnevulatorOptions.delayLine=0L; /* Do not delay between bytes by default. */ _jpnevulatorOptions.delayByte=0L; /* Action type is mandatory (not by getopts, but by our own mechanism). */ _jpnevulatorOptions.action=actionTypeNone; /* By default we display 16 bytes on one line. */ _jpnevulatorOptions.width=16; /* By default we do not display timing information. */ boolReset(_jpnevulatorOptions.timingPrint); /* By default we couple bytes that arrive within 100 miliseconds. */ _jpnevulatorOptions.timingDelta=100000UL; /* By default we do not display ascii data in read mode. */ boolReset(_jpnevulatorOptions.ascii); /* By default the alias separator is the ':' character. */ _jpnevulatorOptions.aliasSeparator=":"; /* Do not show the byte count by default. */ boolReset(_jpnevulatorOptions.byteCountDisplay); /* Do not pass bytes between interfaces by default. */ boolReset(_jpnevulatorOptions.pass); /* Do not poll modem control bits by default. */ boolReset(_jpnevulatorOptions.control); /* Poll every milisecond by default (should suffice for 9600bps). */ _jpnevulatorOptions.controlPoll=1000UL; /* By default we read/write endlessly up untill the end of time. */ _jpnevulatorOptions.count=-1; /* By default, overwrite the existing output file */ boolReset(_jpnevulatorOptions.append); /* By default the append separator is a simple newline. */ _jpnevulatorOptions.appendSeparator="\n"; /* By default, read/write hex byte values. */ _jpnevulatorOptions.base=byteBaseHexadecimal; } static void optionsIOWrite(char *file) { int charactersPrinted; charactersPrinted=sprintf(_jpnevulatorOptions.io,"%.*s",(int)sizeof(_jpnevulatorOptions.io)-1,file); if(charactersPrinted!=strlen(file)) { fprintf(stderr,"%s: Filename truncated to %d chars\n",PROGRAM_NAME,charactersPrinted); } } static void optionsBaseWrite(char *base) { switch(atoi(base)) { #define BASE(xbase,name,width) \ case xbase: { \ _jpnevulatorOptions.base=name; \ break; \ } BASES #undef BASE default: { fprintf(stderr,"%s: Unsupported base unit selected, cowardly using default base(=%d) unit.\n",PROGRAM_NAME,_jpnevulatorOptions.base); break; } } } enum optionsRtrn optionsParse(int argc,char **argv) { int finished; optionsDefault(); for(finished=0;!finished;) { int option,option_index; static struct option long_options[]={ {"append",no_argument,NULL,'A'}, {"ascii",no_argument,NULL,'a'}, {"byte-count",no_argument,NULL,'b'}, {"base",required_argument,NULL,'B'}, {"checksum",no_argument,NULL,'c'}, {"control",no_argument,NULL,'C'}, {"control-poll",required_argument,NULL,'D'}, {"delay-line",required_argument,NULL,'d'}, {"timing-delta",required_argument,NULL,'e'}, {"file",required_argument,NULL,'f'}, {"timing-print",no_argument,NULL,'g'}, {"help",no_argument,NULL,'h'}, {"width",required_argument,NULL,'i'}, {"fuck-up",no_argument,NULL,'j'}, {"delay-byte",required_argument,NULL,'k'}, {"alias-separator",required_argument,NULL,'l'}, {"no-send",no_argument,NULL,'n'}, {"count",required_argument,NULL,'o'}, {"pass",no_argument,NULL,'P'}, {"print",no_argument,NULL,'p'}, {"pty",optional_argument,NULL,'q'}, {"read",no_argument,NULL,'r'}, {"size",required_argument,NULL,'s'}, {"append-separator",required_argument,NULL,'S'}, {"tty",required_argument,NULL,'t'}, {"version",no_argument,NULL,'v'}, {"write",no_argument,NULL,'w'}, {"crc16",optional_argument,NULL,'y'}, {"crc8",optional_argument,NULL,'z'}, {NULL,no_argument,NULL,0} }; option=getopt_long(argc,argv,"aAbB:cCd:D:e:f:ghi:jk:l:no:pPq:rs:S:t:vwy:z:",long_options,&option_index); switch(option) { case -1: { finished=!finished; break; } case 'a': { boolSet(_jpnevulatorOptions.ascii); break; } case 'A': { boolSet(_jpnevulatorOptions.append); break; } case 'b': { boolSet(_jpnevulatorOptions.byteCountDisplay); break; } case 'B': { optionsBaseWrite(optarg); break; } case 'c': { _jpnevulatorOptions.checksum=checksumTypeChecksum; break; } case 'C': { boolSet(_jpnevulatorOptions.control); break; } case 'd': { unsigned long delay; delay=atol(optarg); if(delay>0) { _jpnevulatorOptions.delayLine=delay; } else { fprintf(stderr,"%s: Discarding line delay. It should be bigger than zero.\n",PROGRAM_NAME); } break; } case 'D': { unsigned long poll; poll=atol(optarg); if(poll>0) { _jpnevulatorOptions.controlPoll=poll; } else { fprintf(stderr,"%s: Discarding control poll. It should be bigger than zero.\n",PROGRAM_NAME); } break; } case 'e': { _jpnevulatorOptions.timingDelta=atol(optarg); break; } case 'f': { optionsIOWrite(optarg); break; } case 'g': { boolSet(_jpnevulatorOptions.timingPrint); break; } case 'h': { usage(); return(optionsRtrnUsage); } case 'i': { int width; width=atoi(optarg); if(width>0) { _jpnevulatorOptions.width=width; } else { fprintf(stderr,"%s: Discarding width. It should be bigger than zero.\n",PROGRAM_NAME); } break; } case 'j': { boolSet(_jpnevulatorOptions.checksumFuckup); break; } case 'k': { unsigned long delay; delay=atol(optarg); if(delay>0) { _jpnevulatorOptions.delayByte=delay; } else { fprintf(stderr,"%s: Discarding byte delay. It should be bigger than zero.\n",PROGRAM_NAME); } break; } case 'l': { _jpnevulatorOptions.aliasSeparator=optarg; break; } case 'n': { boolReset(_jpnevulatorOptions.send); break; } case 'o': { int count; count=atoi(optarg); if(count>0) { _jpnevulatorOptions.count=count; } else { fprintf(stderr,"%s: Discarding count. It should be bigger than zero.\n",PROGRAM_NAME); } break; } case 'p': { boolSet(_jpnevulatorOptions.print); break; } case 'P': { boolSet(_jpnevulatorOptions.pass); break; } case 'q': { ptyAdd(optarg); break; } case 'r': { if(_jpnevulatorOptions.action!=actionTypeNone) { fprintf(stderr,"%s: Use --read or --write, but not both. Performing a read this time.\n",PROGRAM_NAME); } _jpnevulatorOptions.action=actionTypeRead; break; } case 's': { int size; size=atoi(optarg); if(size>0) { _jpnevulatorOptions.size=size; } else { fprintf(stderr,"%s: Discarding size. It should be bigger than zero.\n",PROGRAM_NAME); } break; } case 'S': { _jpnevulatorOptions.appendSeparator=optarg; break; } case 't': { ttyAdd(optarg); break; } case 'v': { printf( "%s version %s\n" "Copyright (C) 2006-2020 Freddy Spierenburg \n" "This is free software. You may redistribute copies of it under the terms of\n" "the GNU General Public License .\n" "There is NO WARRANTY, to the extent permitted by law.\n", PROGRAM_NAME,PROGRAM_VERSION ); return(optionsRtrnVersion); } case 'w': { if(_jpnevulatorOptions.action!=actionTypeNone) { fprintf(stderr,"%s: Use --read or --write, but not both. Performing a write this time.\n",PROGRAM_NAME); } _jpnevulatorOptions.action=actionTypeWrite; break; } case 'y': { _jpnevulatorOptions.checksum=checksumTypeCrc16; if(optarg) { printf("[%s]\n",optarg); _jpnevulatorOptions.crc16Poly=strtol(optarg,NULL,16); crc16TableCreate(0,_jpnevulatorOptions.crc16Poly); } break; } case 'z': { _jpnevulatorOptions.checksum=checksumTypeCrc8; if(optarg) { _jpnevulatorOptions.crc8Poly=strtol(optarg,NULL,16); crc8PolyInit(_jpnevulatorOptions.crc8Poly); } break; } } } /* As mentioned more early, we read/write standard input/output by default and warn the user if he * or she tries to use multiple sources. */ if(optind #include #include #include #include #include #include "jpnevulator.h" #include "interface.h" #include "pty.h" static int ptyOpen(char *name,int length) { int fd; fd=posix_openpt(O_RDWR); if(fd!=-1) { char *pts; int charactersPrinted; grantpt(fd); unlockpt(fd); pts=ptsname(fd); fprintf(stderr,"%s: slave pts device is %s.\n",PROGRAM_NAME,pts); charactersPrinted=sprintf(name,"%.*s",length,pts); if(charactersPrinted!=strlen(name)) { fprintf(stderr,"%s: interface %s truncated to %d chars -> %s\n",PROGRAM_NAME,pts,charactersPrinted,name); } } return(fd); } static int ptyControlGet(int fd,char *name) { /* A pty does not have modem control bits, so we simply return 0. This way the interface plays nicely * according to general rules and never produces any output. */ return(0); } static void ptyControlWrite(FILE *output,int control) { /* We don't do anything and cowardly refuse to write anything. */ } static void ptyClose(int fd) { close(fd); } enum interfaceRtrn ptyAdd(char *name) { return(interfaceAdd(name,ptyOpen,ptyControlGet,ptyControlWrite,ptyClose)); } jpnevulator-2.3.6/pty.h000066400000000000000000000015731371000200300150500ustar00rootroot00000000000000/* jpnevulator - serial reader/writer * Copyright (C) 2006-2020 Freddy Spierenburg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __PTY_H #define __PTY_H extern enum interfaceRtrn ptyAdd(char *); #endif jpnevulator-2.3.6/tty.c000066400000000000000000000035141371000200300150440ustar00rootroot00000000000000/* jpnevulator - serial reader/writer * Copyright (C) 2006-2020 Freddy Spierenburg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include "jpnevulator.h" #include "interface.h" #include "tty.h" static int ttyOpen(char *name,int length) { return(open(name,O_RDWR)); } static int ttyControlGet(int fd,char *name) { int control; control=0; if(ioctl(fd,TIOCMGET,&control)) { char error[256]; sprintf(error,"%s: Can't get the modem control bits of interface %s",PROGRAM_NAME,name); perror(error); } return control; } static void ttyControlWrite(FILE *output,int control) { fprintf( output, "le=%d, dtr=%d, rts=%d, st=%d, sr=%d, cts=%d, cd=%d, ri=%d, dsr=%d\n", control&TIOCM_LE?1:0, control&TIOCM_DTR?1:0, control&TIOCM_RTS?1:0, control&TIOCM_ST?1:0, control&TIOCM_SR?1:0, control&TIOCM_CTS?1:0, control&TIOCM_CD?1:0, control&TIOCM_RI?1:0, control&TIOCM_DSR?1:0 ); } static void ttyClose(int fd) { close(fd); } enum interfaceRtrn ttyAdd(char *name) { return(interfaceAdd(name,ttyOpen,ttyControlGet,ttyControlWrite,ttyClose)); } jpnevulator-2.3.6/tty.h000066400000000000000000000015731371000200300150540ustar00rootroot00000000000000/* jpnevulator - serial reader/writer * Copyright (C) 2006-2020 Freddy Spierenburg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __TTY_H #define __TTY_H extern enum interfaceRtrn ttyAdd(char *); #endif