pax_global_header00006660000000000000000000000064127747570210014526gustar00rootroot0000000000000052 comment=052b216326d607c9b76a1d0ab22295f0f5acb818 picocom-2.2/000077500000000000000000000000001277475702100130225ustar00rootroot00000000000000picocom-2.2/.gitignore000066400000000000000000000000201277475702100150020ustar00rootroot00000000000000*.o *~ /picocom picocom-2.2/CHANGES.old000066400000000000000000000115331277475702100145750ustar00rootroot00000000000000------------------------------------------------------------------------ r35 | npat | 2004-08-13 14:18:38 +0300 (Fri, 13 Aug 2004) | 3 lines Changed paths: M /picocom/trunk/Makefile picocom version 1.4 ------------------------------------------------------------------------ r34 | npat | 2004-08-13 14:15:23 +0300 (Fri, 13 Aug 2004) | 4 lines Changed paths: M /picocom/trunk/Makefile Added the "changes" target that generates the CHANGES file from the svn (version control) commit logs. ------------------------------------------------------------------------ r33 | npat | 2004-08-13 05:48:26 +0300 (Fri, 13 Aug 2004) | 4 lines Changed paths: M /picocom/trunk/Makefile M /picocom/trunk/picocom.8.xml Converted the manpage sources to use the xmlmp-1.1 DTD, and hence the xmlmp tools (see http://npat.efault.net/hacks/xmlmp) ------------------------------------------------------------------------ r28 | npat | 2004-08-12 15:17:54 +0300 (Thu, 12 Aug 2004) | 2 lines Changed paths: M /picocom/trunk/TODO Removed stale tasks from TODO. (Now it's empty). ------------------------------------------------------------------------ r27 | npat | 2004-08-12 15:16:03 +0300 (Thu, 12 Aug 2004) | 5 lines Changed paths: M /picocom/trunk/CONTRIBUTORS M /picocom/trunk/picocom.8.xml Added Julius P. Malkiewicz in the contributors list Updated documentation to comply with the changes made since r13 ------------------------------------------------------------------------ r26 | npat | 2004-08-12 14:45:11 +0300 (Thu, 12 Aug 2004) | 9 lines Changed paths: M /picocom/trunk/Makefile M /picocom/trunk/picocom.c Added support for UUCP-style locks. Lock handling is compiled-in if the macro UUCP_LOCK_DIR is defined; if it is, it must contain the name of the lock directory. Locking can be disabled at runtime using the "--noock" option. UUCP-locks support is based on a patch submitted by Julius P. Malkiewicz ------------------------------------------------------------------------ r25 | npat | 2004-08-12 11:55:56 +0300 (Thu, 12 Aug 2004) | 4 lines Changed paths: M /picocom/trunk/picocom.c Implemented th C-\ command, which generates a break sequence. Patch submitted by Julius P. Malkiewicz ------------------------------------------------------------------------ r24 | npat | 2004-08-12 11:52:39 +0300 (Thu, 12 Aug 2004) | 5 lines Changed paths: M /picocom/trunk/picocom.c If two escape-characters are sent in a row, the second is passed-through, and picocom returns to transparrent mode. Patch submitteb by Julius P. Malkiewicz ------------------------------------------------------------------------ r23 | npat | 2004-08-12 11:45:57 +0300 (Thu, 12 Aug 2004) | 7 lines Changed paths: M /picocom/trunk/term.c Fixed "term.c" to compile under FreeBSD (and probably other BSDs also). "term.c" now includes "termios.h" instead of "termio.h" in non-linux system. Added a declaration for "tioold"---that was missing---in the non-linux part of the DTR toggling code. Patch submitted by Julius P. Malkiewicz ------------------------------------------------------------------------ r12 | npat | 2004-08-11 19:03:48 +0300 (Wed, 11 Aug 2004) | 2 lines Changed paths: M /picocom/trunk/CONTRIBUTORS M /picocom/trunk/Makefile M /picocom/trunk/README A /picocom/trunk/picocom.8.xml Added r1.3 ------------------------------------------------------------------------ r10 | npat | 2004-08-11 19:02:35 +0300 (Wed, 11 Aug 2004) | 3 lines Changed paths: A /picocom/trunk/CONTRIBUTORS M /picocom/trunk/Makefile A /picocom/trunk/TODO M /picocom/trunk/picocom.c Added r1.2 ------------------------------------------------------------------------ r8 | npat | 2004-08-11 19:01:25 +0300 (Wed, 11 Aug 2004) | 2 lines Changed paths: M /picocom/trunk/Makefile A /picocom/trunk/NEWS M /picocom/trunk/README A /picocom/trunk/pcasc A /picocom/trunk/pcxm A /picocom/trunk/pcym A /picocom/trunk/pczm M /picocom/trunk/picocom.c Added r1.1 ------------------------------------------------------------------------ r6 | npat | 2004-08-11 18:59:25 +0300 (Wed, 11 Aug 2004) | 2 lines Changed paths: A /picocom/trunk/LICENSE.txt A /picocom/trunk/Makefile A /picocom/trunk/README A /picocom/trunk/picocom.c A /picocom/trunk/term.c A /picocom/trunk/term.h Imported r1.0 sources ------------------------------------------------------------------------ r1 | svn | 2004-08-11 08:02:59 +0300 (Wed, 11 Aug 2004) | 2 lines Changed paths: A /mu0 A /mu0/branches A /mu0/tags A /mu0/trunk A /picocom A /picocom/branches A /picocom/tags A /picocom/trunk A /ppgplot A /ppgplot/branches A /ppgplot/tags A /ppgplot/trunk Imported initial projects directory structure. ------------------------------------------------------------------------ picocom-2.2/CONTRIBUTORS000066400000000000000000000036641277475702100147130ustar00rootroot00000000000000 The following people contributed improvements, fixes, suggestions, and comments: - Oliver Kurth (oku@debian.org) contributed bug fixes and the manual page for picocm. - Julius P. Malkiewicz (julius@sonartech.com.au) contributed FreeBSD portablity fixes, the C-\ command implementation, the UUCP locks implementation, and other minor fixes. - Pavel Vymetalek (pvymetalek@seznam.cz) contributed the higher baudrates support. - Niels Moller (nisse@lysator.liu.se) contributed support for non-alphabetic escape keys. - (lwithers@gmail.com) contributed fixes to select(2) and read(2) error handling. - (bob.dunlop@xyzzy.org.uk) suggested fix to prevent serial port form becoming controlling terminal. - Edgar Johansen (edgar@storteig.com) suggested conditionals to enable compilation without UUCP_LOCK_DIR - Scott Tsai (scott.tw@gmail.com) suggested better UUCP_LOCK_DIR definition - Josh Handley (https://github.com/jhandley) added support for line-editing, autocompletion and history when entering "send" and "receive" file names. Editing support uses the "linenoise" library. See: https://github.com/jhandley/picocom - Salvatore Sanfilippo (https://github.com/antirez) is the author of the linenoise line-editing library. See: https://github.com/antirez/linenoise - From Cody Schafer's (https://gitbub.com/jmesmon) fork originated the idea to add custom baudrate support to picocom. See: https://github.com/jmesmon/picocom - From Paul Chakravarti's (https://github.com/paulchakravarti) fork orignated the idea to add support for disabling the send- and rceive-file commands, and for not using the shell when executing external programs (to avoid shell escapes). See: https://github.com/paulchakravarti/picocom-noexec - Vicente Olivert Riera (Vincent.Riera@imgtec.com) contribiuted minor patches - Peter Bradstreet (peter.bradstreet@oracle.com) helped diagnose input performance problems on overloaded / slow systems. picocom-2.2/LICENSE.txt000066400000000000000000000431311277475702100146470ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. picocom-2.2/Makefile000066400000000000000000000041131277475702100144610ustar00rootroot00000000000000 VERSION = 2.2 #CC = gcc CPPFLAGS = -DVERSION_STR=\"$(VERSION)\" CFLAGS = -Wall -g LD = $(CC) LDFLAGS = -g LDLIBS = all: picocom OBJS = ## Increase this to use larger input (e.g. copy-paste) buffer TTY_Q_SZ = 32768 CPPFLAGS += -DTTY_Q_SZ=$(TTY_Q_SZ) ## Comment this out to disable high-baudrate support CPPFLAGS += -DHIGH_BAUD ## Normally you should NOT enable both: UUCP-style and flock(2) ## locking. ## Comment this out to disable locking with flock CPPFLAGS += -DUSE_FLOCK ## Comment these out to disable UUCP-style lockdirs #UUCP_LOCK_DIR=/var/lock #CPPFLAGS += -DUUCP_LOCK_DIR=\"$(UUCP_LOCK_DIR)\" ## Comment these out to disable "linenoise"-library support HISTFILE = .picocom_history CPPFLAGS += -DHISTFILE=\"$(HISTFILE)\" \ -DLINENOISE OBJS += linenoise-1.0/linenoise.o linenoise-1.0/linenoise.o : linenoise-1.0/linenoise.c linenoise-1.0/linenoise.h ## Comment these IN to enable custom baudrate support. ## Currently works *only* with Linux (kernels > 2.6). #CPPFLAGS += -DUSE_CUSTOM_BAUD #OBJS += termios2.o #termios2.o : termios2.c termios2.h termbits2.h ## Comment this IN to remove help strings (saves ~ 4-6 Kb). #CPPFLAGS += -DNO_HELP OBJS += picocom.o term.o fdio.o split.o picocom : $(OBJS) $(LD) $(LDFLAGS) -o $@ $(OBJS) $(LDLIBS) picocom.o : picocom.c term.h term.o : term.c term.h split.o : split.c split.h fdio.o : fdio.c fdio.h .c.o : $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ -c $< doc : picocom.1.html picocom.1 picocom.1.pdf picocom.1 : picocom.1.md sed 's/\*\*\[/\*\*/g;s/\]\*\*/\*\*/g' $? \ | pandoc -s -t man \ -Vfooter="Picocom $(VERSION)" -Vdate="`date -I`" \ -o $@ picocom.1.html : picocom.1.md pandoc -s -t html \ -c ~/.pandoc/css/normalize-noforms.css \ -c ~/.pandoc/css/manpage.css \ --self-contained \ -o $@ $? picocom.1.pdf : picocom.1 groff -man -Tpdf $? > $@ clean: rm -f picocom.o term.o fdio.o split.o rm -f linenoise-1.0/linenoise.o rm -f termios2.o rm -f *~ rm -f \#*\# distclean: clean rm -f picocom realclean: distclean rm -f picocom.1 rm -f picocom.1.html rm -f picocom.1.pdf rm -f CHANGES picocom-2.2/README.md000066400000000000000000000266541277475702100143160ustar00rootroot00000000000000#picocom Minimal dumb-terminal emulator by Nick Patavalis (npat@efault.net) The latest release can be downloaded from: > https://github.com/npat-efault/picocom/releases As its name suggests, *picocom* is a minimal dumb-terminal emulation program. It is, in principle, very much like minicom, only it's "pico" instead of "mini"! It was designed to serve as a simple, manual, modem configuration, testing, and debugging tool. It has also served (quite well) as a low-tech serial communications program to allow access to all types of devices that provide serial consoles. It could also prove useful in many other similar tasks. It is ideal for embedded systems since its memory footprint is minimal (approximately 30K, when stripped). Apart from being a handy little tool, *picocom's* source distribution includes a simple, easy to use, and thoroughly documented terminal-management library, which could serve other projects as well. This library hides the termios(3) calls, and provides a less complex and safer (though certainly less feature-rich) interface. *picocom* runs on Linux, and with no or minor modifications it could run on any Unix-like system with the termios(3) library. For a description of picocom's operation, its command line options, and usage examples, see the manual page included in the source distribution as "picocom.1", and also html-ized as "picocom.1.html". People who have contributed to picocom, by offering feature implementations, bug-fixes, corrections, and suggestions are listed in the "CONTRIBUTORS" file. Please feel free to send comments, requests for new features (no promises, though!), bug-fixes and rants, to the author's email address shown at the top of this file. ## Compilation / Installation Change into picocom's source directory and say: make This will be enough to compile picocom for most modern Unix-like systems. If you want, you can then strip the resulting binary like this: strip picocom Striping the binary is not required, it just reduces its size by a few kilobytes. Then you can copy the picocom binary, as well as the man-page, to wherever you put your binaries and man-pages. For example: cp picocom ~/bin cp picocom.1 ~/man/man1 Again, this is not strictly necessary. You can run picocom and read its man-page directly from the source directory. If something goes wrong and picocom can't compile cleanly, or if it's lacking a feature you need, take a look at the included Makefile. It's very simple and easy to understand. It allows you to select compile-time options and enable or disable some compile-time features by commenting in or out the respective lines. Once you edit the Makefile, to recompile say: make clean make If your system's default make(1) command is not GNU Make (or compatible enough), find out how you can run GNU Make on your system. For example: gmake clean gmake Alternatively, you might have to make some trivial edits to the Makefile for it to work with your system's make(1) command. ## Using picocom If your computer is a PC and has the standard on-board RS-233 ports (usually accessible as two male DB9 connectors at the back) then under Linux these are accessed through device nodes most likely named: `/dev/ttyS0` and `/dev/ttyS1`. If your computer has no on-board serial ports, then you will need a USB-to-Serial adapter (or something similar). Once inserted to a USB port and recognized by Linux, a device node is created for each serial port accessed through the adapter(s). These nodes are most likely named `/dev/ttyUSB0`, `/dev/ttyUSB1`, and so on. For other systems and other Unix-like OSes you will have to consult their documentation as to how the serial port device nodes are named. Lets assume your serial port is accessed through a device node named `/dev/ttyS0`. You can start picocom with its default option values (default serial port settings) like this: picocom /dev/ttyS0 If you have not installed the picocom binary to a suitable place, then you can run it directly from the source distribution directory like this: ./picocom /dev/ttyS0 If this fails with a message like: FATAL: cannot open /dev/ttyS0: Permission denied This means that you do not have permissions to access the serial port's device node. To overcome this you can run picocom as root: sudo picocom /dev/ttyS0 Alternatively, and preferably, you can add yourself to the user-group that your system has for allowing access to serial ports. For most Unix-like systems this group is called "dialout". Consult you system's documentation to find out how you can do this (as it differs form system to system). On most Linux systems you can do it like this: sudo usermod -a -G dialout username You will need to log-out and then log-in back again for this change to take effect. You can explicitly set one or more of the serial port settings to the desired values using picocom's command line options. For example, to set the baud-rate to 115200bps (the default is 9600bps), and enable hardware flow-control (RTS/CTS handshake) you can say: picocom -b 115200 -f h /dev/ttyS0 or: picocom --baud 115200 --flow h /dev/ttyS0 To see all available options run picocom like this: picocom --help Once picocom starts, it initializes the serial port and prints the message: Terminal is ready From now on, every character you type is sent to the serial port, and every character received from the serial port is sent ro your terminal. Including control and special characters. Assuming that there is nothing connected to the other end of your serial port, to respond to the characters you send to it (e.g. echo them back to you), then nothing that you type in picocom will appear on your terminal. This is normal. To exit picocom you have to type: C-a, C-x Which means you have to type [Control-A] followed by [Control-X]. You can do this by pressing and holding down the [Control] key, then pressing (and releasing) the [A] key and then pressing (and releasing) the [X] key (while you still keep [Control] held down). This `C-a` is called the "escape character". It is used to inform picocom that the next character typed is to be interpreted as a command to picocom itself (in this case the exit command) and not to be sent-down to the serial port. There are several other commands (other than `C-a`, `C-x`), all prefixed by `C-a`. Next you should take a look at the very detailed picocom manual page. It can be accessed like this (assuming you are inside the picocom distribution source directory): man ./picocom.1 or (assuming you have installed the manual page to a suitable place): man picocom Thanks for using picocom ## A low-tech terminal server You can use *picocom* to patch-together a very simple, *very low-tech*, terminal server. The situation is like this: You have, in your lab, a box with several serial ports on it, where you connect the console ports of embedded devices, development boards, etc. Let's call it "termbox". You want to access these console ports remotely. If you provide shell-access to termbox for your users, then it's as simple as having the users say (from their remote workstations): $ ssh -t user@termbox picocom -b 115200 /dev/ttyS0 Or make a convenient script/alias for this. Remember the `-t` switch which instructs ssh to create a pseudo-tty, otherwise picocom won't work. What if you *don't* want to give users shell-access to termbox? Then you can use picocom in a setup like the one described below. Just remember, there are countless variations to this theme, the one below is just one of them. Also, keep in mind that some of the commands shown may have small differences from system to system; more so if you go from Linux to other Unix-like systems. Login to termbox and create a user called _termbox_: $ sudo useradd -r -m termbox The `-r` means "system account", and the `-m` means *do* make the home-directory. Mostly we need this account's home-directory as a convenient place to keep stuff; so it doesn't need a login shell or a password. Switch to the _termbox_ account and create a `bin` directory in its home-dir. $ sudo su termbox $ cd ~ $ mkdir bin Copy the picocom binary in `~termbox/bin` (if you don't have it globally installed): $ cp /path/to/picocom ./bin For every serial port you want to provide access to, create a file named after the port and put it in `~termbox/bin`. It should look like this: $ cat ./bin/ttyS0 #!/bin/sh exec /home/termbox/bin/picocom \ --send-cmd '' \ --receive-cmd '' \ -b 115200 \ /dev/ttyS0 And make it executable: $ chmod +x ./bin/ttyS0 Repeat accordingly for every other port. Now the contents of `~termbox/bin` should look like this: $ ls -l ./bin -rwxrwxr-x 1 termbox termbox 102128 Aug 29 13:56 picocom* -rwxrwxr-x 1 termbox termbox 108 Aug 29 14:07 ttyS0* -rwxrwxr-x 1 termbox termbox 108 Aug 29 14:07 ttyS1* ... and so on ... Exit the _termbox_ account: $ exit Now, for every serial port, create a user account named after the port, like this: $ sudo useradd -r -g dialout -d ~termbox -M -s ~termbox/bin/ttyS0 ttyS0 Observe that we make `dialout` the default group for this account, so the account has access to the serial ports. Also observe that we make the script we just wrote (`~termbox/bin/ttyS0`) the login-shell for the account. The `-d` option instructs useradd to use `/home/termbox` as the user's home directory, and the `-M` switch instructs it *not* to create the home-directory. We don't really need a home directory for the _ttyS0_ account, since picocom will not read or write any files; but we provide one, regardless, because *some* systems need a valid home-directory to cd-into on login (else they choke). We could as well have used `/` as the home directory, or we could have let useradd create the usual `/home/ttyS0`. Then set a password for the newly created account: $ sudo passwd ttyS0 Enter new UNIX password: ****** Retype new UNIX password: ****** Repeat (create user account, set password) for every port you want to give access to. You 're set. All a user has to do to remotely access the console connected to termbox's `/dev/ttyS0` port, is: ssh ttyS0@termbox Some interesting points: - If the default port settings you specified as command-line arguments to picocom in `~termbox/bin/ttySx` do not match the settings of the device connected to the port, the user can easily change them from within picocom, using picocom commands. - If a second user tries to remotely access the same port, at the same time, picocom won't let him (picocom will find the port locked and exit). - In the example `~termbox/bin/ttySx` scripts we have completely disabled the send- and receive-file picocom commands. This guarantees that picocom won't execute any external commands. If you want, you can enable the commands by providing specific file-upload and file-download programs as the arguments to the `--send-cmd` and `--receive-cmd` picocom command-line options (provided, of-course, that you trust these programs). Picocom (starting with release 2.0) does not use `/bin/sh` to execute the file-upload and file-download programs and *will not* let the user inject shell-commands when supplying additional arguments to them. - If you allow send- and receive-file operations as described above, you will, most likely, also need a way for your users to put files on termbox, and get files back from it. There are many ways to arrange for this, but they are beyond the scope of this simple example. Again, this is only *one* possible setup. There are countless other variations and elaborations you can try. Be creative! picocom-2.2/TODO000066400000000000000000000000021277475702100135020ustar00rootroot00000000000000 picocom-2.2/fdio.c000066400000000000000000000071001277475702100141050ustar00rootroot00000000000000/* vi: set sw=4 ts=4: * * fdio.c * * Functions for doing I/O on file descriptors. * * by Nick Patavalis (npat@efault.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA */ #include #include #include #include #include #include /**********************************************************************/ ssize_t writen_ni(int fd, const void *buff, size_t n) { size_t nl; ssize_t nw; const char *p; p = buff; nl = n; while (nl > 0) { do { nw = write(fd, p, nl); } while ( nw < 0 && errno == EINTR ); if ( nw <= 0 ) break; nl -= nw; p += nw; } return n - nl; } int fd_printf (int fd, const char *format, ...) { char buf[256]; va_list args; int len; va_start(args, format); len = vsnprintf(buf, sizeof(buf), format, args); buf[sizeof(buf) - 1] = '\0'; va_end(args); return writen_ni(fd, buf, len); } /**********************************************************************/ #ifndef LINENOISE static int cput(int fd, char c) { return write(fd, &c, 1); } static int cdel (int fd) { const char del[] = "\b \b"; return write(fd, del, sizeof(del) - 1); } static int xput (int fd, unsigned char c) { const char hex[] = "0123456789abcdef"; char b[4]; b[0] = '\\'; b[1] = 'x'; b[2] = hex[c >> 4]; b[3] = hex[c & 0x0f]; return write(fd, b, sizeof(b)); } static int xdel (int fd) { const char del[] = "\b\b\b\b \b\b\b\b"; return write(fd, del, sizeof(del) - 1); } int fd_readline (int fdi, int fdo, char *b, int bsz) { int r; unsigned char c; unsigned char *bp, *bpe; bp = (unsigned char *)b; bpe = (unsigned char *)b + bsz - 1; while (1) { r = read(fdi, &c, 1); if ( r <= 0 ) { r = -1; goto out; } switch (c) { case '\b': case '\x7f': if ( bp > (unsigned char *)b ) { bp--; if ( isprint(*bp) ) cdel(fdo); else xdel(fdo); } else { cput(fdo, '\x07'); } break; case '\x03': /* CTRL-c */ r = -1; errno = EINTR; goto out; case '\r': *bp = '\0'; r = bp - (unsigned char *)b; goto out; default: if ( bp < bpe ) { *bp++ = c; if ( isprint(c) ) cput(fdo, c); else xput(fdo, c); } else { cput(fdo, '\x07'); } break; } } out: return r; } #endif /* of LINENOISE */ /**********************************************************************/ /* * Local Variables: * mode:c * tab-width: 4 * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ picocom-2.2/fdio.h000066400000000000000000000023541277475702100141200ustar00rootroot00000000000000/* vi: set sw=4 ts=4: * * fdio.h * * Functions for doing I/O on file descriptors. * * by Nick Patavalis (npat@efault.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA */ #ifndef FDIO_H ssize_t writen_ni(int fd, const void *buff, size_t n); int fd_printf (int fd, const char *format, ...); #ifndef LINENOISE int fd_readline (int fdi, int fdo, char *b, int bsz); #endif #endif /* of FDIO_H */ /**********************************************************************/ /* * Local Variables: * mode:c * tab-width: 4 * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ picocom-2.2/linenoise-1.0/000077500000000000000000000000001277475702100153035ustar00rootroot00000000000000picocom-2.2/linenoise-1.0/.gitignore000066400000000000000000000000451277475702100172720ustar00rootroot00000000000000linenoise_example *.dSYM history.txt picocom-2.2/linenoise-1.0/LICENSE000066400000000000000000000026001277475702100163060ustar00rootroot00000000000000Copyright (c) 2010-2014, Salvatore Sanfilippo Copyright (c) 2010-2013, Pieter Noordhuis All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. picocom-2.2/linenoise-1.0/Makefile000066400000000000000000000002701277475702100167420ustar00rootroot00000000000000linenoise_example: linenoise.h linenoise.c linenoise_example: linenoise.c example.c $(CC) -Wall -W -Os -g -o linenoise_example linenoise.c example.c clean: rm -f linenoise_example picocom-2.2/linenoise-1.0/README.markdown000066400000000000000000000061221277475702100200050ustar00rootroot00000000000000# Linenoise A minimal, zero-config, BSD licensed, readline replacement used in Redis, MongoDB, and Android. * Single and multi line editing mode with the usual key bindings implemented. * History handling. * Completion. * About 1,100 lines of BSD license source code. * Only uses a subset of VT100 escapes (ANSI.SYS compatible). ## Can a line editing library be 20k lines of code? Line editing with some support for history is a really important feature for command line utilities. Instead of retyping almost the same stuff again and again it's just much better to hit the up arrow and edit on syntax errors, or in order to try a slightly different command. But apparently code dealing with terminals is some sort of Black Magic: readline is 30k lines of code, libedit 20k. Is it reasonable to link small utilities to huge libraries just to get a minimal support for line editing? So what usually happens is either: * Large programs with configure scripts disabling line editing if readline is not present in the system, or not supporting it at all since readline is GPL licensed and libedit (the BSD clone) is not as known and available as readline is (Real world example of this problem: Tclsh). * Smaller programs not using a configure script not supporting line editing at all (A problem we had with Redis-cli for instance). The result is a pollution of binaries without line editing support. So I spent more or less two hours doing a reality check resulting in this little library: is it *really* needed for a line editing library to be 20k lines of code? Apparently not, it is possibe to get a very small, zero configuration, trivial to embed library, that solves the problem. Smaller programs will just include this, supporing line editing out of the box. Larger programs may use this little library or just checking with configure if readline/libedit is available and resorting to linenoise if not. ## Terminals, in 2010. Apparently almost every terminal you can happen to use today has some kind of support for basic VT100 escape sequences. So I tried to write a lib using just very basic VT100 features. The resulting library appears to work everywhere I tried to use it, and now can work even on ANSI.SYS compatible terminals, since no VT220 specific sequences are used anymore. The library is currently about 1100 lines of code. In order to use it in your project just look at the *example.c* file in the source distribution, it is trivial. Linenoise is BSD code, so you can use both in free software and commercial software. ## Tested with... * Linux text only console ($TERM = linux) * Linux KDE terminal application ($TERM = xterm) * Linux xterm ($TERM = xterm) * Linux Buildroot ($TERM = vt100) * Mac OS X iTerm ($TERM = xterm) * Mac OS X default Terminal.app ($TERM = xterm) * OpenBSD 4.5 through an OSX Terminal.app ($TERM = screen) * IBM AIX 6.1 * FreeBSD xterm ($TERM = xterm) * ANSI.SYS Please test it everywhere you can and report back! ## Let's push this forward! Patches should be provided in the respect of linenoise sensibility for small easy to understand code. Send feedbacks to antirez at gmail picocom-2.2/linenoise-1.0/example.c000066400000000000000000000042361277475702100171070ustar00rootroot00000000000000#include #include #include #include "linenoise.h" void completion(const char *buf, linenoiseCompletions *lc) { if (buf[0] == 'h') { linenoiseAddCompletion(lc,"hello"); linenoiseAddCompletion(lc,"hello there"); } } int main(int argc, char **argv) { char *line; char *prgname = argv[0]; /* Parse options, with --multiline we enable multi line editing. */ while(argc > 1) { argc--; argv++; if (!strcmp(*argv,"--multiline")) { linenoiseSetMultiLine(1); printf("Multi-line mode enabled.\n"); } else if (!strcmp(*argv,"--keycodes")) { linenoisePrintKeyCodes(); exit(0); } else { fprintf(stderr, "Usage: %s [--multiline] [--keycodes]\n", prgname); exit(1); } } /* Set the completion callback. This will be called every time the * user uses the key. */ linenoiseSetCompletionCallback(completion); /* Load history from file. The history file is just a plain text file * where entries are separated by newlines. */ linenoiseHistoryLoad("history.txt"); /* Load the history at startup */ /* Now this is the main loop of the typical linenoise-based application. * The call to linenoise() will block as long as the user types something * and presses enter. * * The typed string is returned as a malloc() allocated string by * linenoise, so the user needs to free() it. */ while((line = linenoise("hello> ")) != NULL) { /* Do something with the string. */ if (line[0] != '\0' && line[0] != '/') { printf("echo: '%s'\n", line); linenoiseHistoryAdd(line); /* Add to the history. */ linenoiseHistorySave("history.txt"); /* Save the history on disk. */ } else if (!strncmp(line,"/historylen",11)) { /* The "/historylen" command will change the history len. */ int len = atoi(line+11); linenoiseHistorySetMaxLen(len); } else if (line[0] == '/') { printf("Unreconized command: %s\n", line); } free(line); } return 0; } picocom-2.2/linenoise-1.0/linenoise.c000066400000000000000000001061351277475702100174420ustar00rootroot00000000000000/* linenoise.c -- VERSION 1.0 * * Guerrilla line editing library against the idea that a line editing lib * needs to be 20,000 lines of C code. * * You can find the latest source code at: * * http://github.com/antirez/linenoise * * Does a number of crazy assumptions that happen to be true in 99.9999% of * the 2010 UNIX computers around. * * ------------------------------------------------------------------------ * * Copyright (c) 2010-2014, Salvatore Sanfilippo * Copyright (c) 2010-2013, Pieter Noordhuis * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * ------------------------------------------------------------------------ * * References: * - http://invisible-island.net/xterm/ctlseqs/ctlseqs.html * - http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html * * Todo list: * - Filter bogus Ctrl+ combinations. * - Win32 support * * Bloat: * - History search like Ctrl+r in readline? * * List of escape sequences used by this program, we do everything just * with three sequences. In order to be so cheap we may have some * flickering effect with some slow terminal, but the lesser sequences * the more compatible. * * EL (Erase Line) * Sequence: ESC [ n K * Effect: if n is 0 or missing, clear from cursor to end of line * Effect: if n is 1, clear from beginning of line to cursor * Effect: if n is 2, clear entire line * * CUF (CUrsor Forward) * Sequence: ESC [ n C * Effect: moves cursor forward n chars * * CUB (CUrsor Backward) * Sequence: ESC [ n D * Effect: moves cursor backward n chars * * The following is used to get the terminal width if getting * the width with the TIOCGWINSZ ioctl fails * * DSR (Device Status Report) * Sequence: ESC [ 6 n * Effect: reports the current cusor position as ESC [ n ; m R * where n is the row and m is the column * * When multi line mode is enabled, we also use an additional escape * sequence. However multi line editing is disabled by default. * * CUU (Cursor Up) * Sequence: ESC [ n A * Effect: moves cursor up of n chars. * * CUD (Cursor Down) * Sequence: ESC [ n B * Effect: moves cursor down of n chars. * * When linenoiseClearScreen() is called, two additional escape sequences * are used in order to clear the screen and position the cursor at home * position. * * CUP (Cursor position) * Sequence: ESC [ H * Effect: moves the cursor to upper left corner * * ED (Erase display) * Sequence: ESC [ 2 J * Effect: clear the whole screen * */ #include #include #include #include #include #include #include #include #include #include #include #include "linenoise.h" #define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100 #define LINENOISE_MAX_LINE 4096 static char *unsupported_term[] = {"dumb","cons25","emacs",NULL}; static linenoiseCompletionCallback *completionCallback = NULL; static struct termios orig_termios; /* In order to restore at exit.*/ static int rawmode = 0; /* For atexit() function to check if restore is needed*/ static int mlmode = 0; /* Multi line mode. Default is single line. */ static int atexit_registered = 0; /* Register atexit just 1 time. */ static int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN; static int history_len = 0; static char **history = NULL; /* The linenoiseState structure represents the state during line editing. * We pass this state to functions implementing specific editing * functionalities. */ struct linenoiseState { int ifd; /* Terminal stdin file descriptor. */ int ofd; /* Terminal stdout file descriptor. */ char *buf; /* Edited line buffer. */ size_t buflen; /* Edited line buffer size. */ const char *prompt; /* Prompt to display. */ size_t plen; /* Prompt length. */ size_t pos; /* Current cursor position. */ size_t oldpos; /* Previous refresh cursor position. */ size_t len; /* Current edited line length. */ size_t cols; /* Number of columns in terminal. */ size_t maxrows; /* Maximum num of rows used so far (multiline mode) */ int history_index; /* The history index we are currently editing. */ }; enum KEY_ACTION{ KEY_NULL = 0, /* NULL */ CTRL_A = 1, /* Ctrl+a */ CTRL_B = 2, /* Ctrl-b */ CTRL_C = 3, /* Ctrl-c */ CTRL_D = 4, /* Ctrl-d */ CTRL_E = 5, /* Ctrl-e */ CTRL_F = 6, /* Ctrl-f */ CTRL_H = 8, /* Ctrl-h */ TAB = 9, /* Tab */ CTRL_K = 11, /* Ctrl+k */ CTRL_L = 12, /* Ctrl+l */ ENTER = 13, /* Enter */ CTRL_N = 14, /* Ctrl-n */ CTRL_P = 16, /* Ctrl-p */ CTRL_T = 20, /* Ctrl-t */ CTRL_U = 21, /* Ctrl+u */ CTRL_W = 23, /* Ctrl+w */ ESC = 27, /* Escape */ BACKSPACE = 127 /* Backspace */ }; static void linenoiseAtExit(void); int linenoiseHistoryAdd(const char *line); static void refreshLine(struct linenoiseState *l); /* Debugging macro. */ #if 0 FILE *lndebug_fp = NULL; #define lndebug(...) \ do { \ if (lndebug_fp == NULL) { \ lndebug_fp = fopen("/tmp/lndebug.txt","a"); \ fprintf(lndebug_fp, \ "[%d %d %d] p: %d, rows: %d, rpos: %d, max: %d, oldmax: %d\n", \ (int)l->len,(int)l->pos,(int)l->oldpos,plen,rows,rpos, \ (int)l->maxrows,old_rows); \ } \ fprintf(lndebug_fp, ", " __VA_ARGS__); \ fflush(lndebug_fp); \ } while (0) #else #define lndebug(fmt, ...) #endif /* ======================= Low level terminal handling ====================== */ /* Set if to use or not the multi line mode. */ void linenoiseSetMultiLine(int ml) { mlmode = ml; } /* Return true if the terminal name is in the list of terminals we know are * not able to understand basic escape sequences. */ static int isUnsupportedTerm(void) { char *term = getenv("TERM"); int j; if (term == NULL) return 0; for (j = 0; unsupported_term[j]; j++) if (!strcasecmp(term,unsupported_term[j])) return 1; return 0; } /* Raw mode: 1960 magic shit. */ static int enableRawMode(int fd) { struct termios raw; if (!isatty(STDIN_FILENO)) goto fatal; if (!atexit_registered) { atexit(linenoiseAtExit); atexit_registered = 1; } if (tcgetattr(fd,&orig_termios) == -1) goto fatal; raw = orig_termios; /* modify the original mode */ /* input modes: no break, no CR to NL, no parity check, no strip char, * no start/stop output control. */ raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); /* output modes - disable post processing */ raw.c_oflag &= ~(OPOST); /* control modes - set 8 bit chars */ raw.c_cflag |= (CS8); /* local modes - choing off, canonical off, no extended functions, * no signal chars (^Z,^C) */ raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); /* control chars - set return condition: min number of bytes and timer. * We want read to return every single byte, without timeout. */ raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */ /* put terminal in raw mode after flushing */ if (tcsetattr(fd,TCSAFLUSH,&raw) < 0) goto fatal; rawmode = 1; return 0; fatal: errno = ENOTTY; return -1; } static void disableRawMode(int fd) { /* Don't even check the return value as it's too late. */ if (rawmode && tcsetattr(fd,TCSAFLUSH,&orig_termios) != -1) rawmode = 0; } /* Use the ESC [6n escape sequence to query the horizontal cursor position * and return it. On error -1 is returned, on success the position of the * cursor. */ static int getCursorPosition(int ifd, int ofd) { char buf[32]; int cols, rows; unsigned int i = 0; /* Report cursor location */ if (write(ofd, "\x1b[6n", 4) != 4) return -1; /* Read the response: ESC [ rows ; cols R */ while (i < sizeof(buf)-1) { if (read(ifd,buf+i,1) != 1) break; if (buf[i] == 'R') break; i++; } buf[i] = '\0'; /* Parse it. */ if (buf[0] != ESC || buf[1] != '[') return -1; if (sscanf(buf+2,"%d;%d",&rows,&cols) != 2) return -1; return cols; } /* Try to get the number of columns in the current terminal, or assume 80 * if it fails. */ static int getColumns(int ifd, int ofd) { struct winsize ws; if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) { /* ioctl() failed. Try to query the terminal itself. */ int start, cols; /* Get the initial position so we can restore it later. */ start = getCursorPosition(ifd,ofd); if (start == -1) goto failed; /* Go to right margin and get position. */ if (write(ofd,"\x1b[999C",6) != 6) goto failed; cols = getCursorPosition(ifd,ofd); if (cols == -1) goto failed; /* Restore position. */ if (cols > start) { char seq[32]; snprintf(seq,32,"\x1b[%dD",cols-start); if (write(ofd,seq,strlen(seq)) == -1) { /* Can't recover... */ } } return cols; } else { return ws.ws_col; } failed: return 80; } /* Clear the screen. Used to handle ctrl+l */ void linenoiseClearScreen(void) { if (write(STDOUT_FILENO,"\x1b[H\x1b[2J",7) <= 0) { /* nothing to do, just to avoid warning. */ } } /* Beep, used for completion when there is nothing to complete or when all * the choices were already shown. */ static void linenoiseBeep(void) { fprintf(stderr, "\x7"); fflush(stderr); } /* ============================== Completion ================================ */ /* Free a list of completion option populated by linenoiseAddCompletion(). */ static void freeCompletions(linenoiseCompletions *lc) { size_t i; for (i = 0; i < lc->len; i++) free(lc->cvec[i]); if (lc->cvec != NULL) free(lc->cvec); } /* This is an helper function for linenoiseEdit() and is called when the * user types the key in order to complete the string currently in the * input. * * The state of the editing is encapsulated into the pointed linenoiseState * structure as described in the structure definition. */ static int completeLine(struct linenoiseState *ls) { linenoiseCompletions lc = { 0, NULL }; int nread, nwritten; char c = 0; completionCallback(ls->buf,&lc); if (lc.len == 0) { linenoiseBeep(); } else { size_t stop = 0, i = 0; while(!stop) { /* Show completion or original buffer */ if (i < lc.len) { struct linenoiseState saved = *ls; ls->len = ls->pos = strlen(lc.cvec[i]); ls->buf = lc.cvec[i]; refreshLine(ls); ls->len = saved.len; ls->pos = saved.pos; ls->buf = saved.buf; } else { refreshLine(ls); } nread = read(ls->ifd,&c,1); if (nread <= 0) { freeCompletions(&lc); return -1; } switch(c) { case 9: /* tab */ i = (i+1) % (lc.len+1); if (i == lc.len) linenoiseBeep(); break; case 27: /* escape */ /* Re-show original buffer */ if (i < lc.len) refreshLine(ls); stop = 1; break; default: /* Update buffer and return */ if (i < lc.len) { nwritten = snprintf(ls->buf,ls->buflen,"%s",lc.cvec[i]); ls->len = ls->pos = nwritten; } stop = 1; break; } } } freeCompletions(&lc); return c; /* Return last read character */ } /* Register a callback function to be called for tab-completion. */ void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) { completionCallback = fn; } /* This function is used by the callback function registered by the user * in order to add completion options given the input string when the * user typed . See the example.c source code for a very easy to * understand example. */ void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str) { size_t len = strlen(str); char *copy, **cvec; copy = malloc(len+1); if (copy == NULL) return; memcpy(copy,str,len+1); cvec = realloc(lc->cvec,sizeof(char*)*(lc->len+1)); if (cvec == NULL) { free(copy); return; } lc->cvec = cvec; lc->cvec[lc->len++] = copy; } /* =========================== Line editing ================================= */ /* We define a very simple "append buffer" structure, that is an heap * allocated string where we can append to. This is useful in order to * write all the escape sequences in a buffer and flush them to the standard * output in a single call, to avoid flickering effects. */ struct abuf { char *b; int len; }; static void abInit(struct abuf *ab) { ab->b = NULL; ab->len = 0; } static void abAppend(struct abuf *ab, const char *s, int len) { char *new = realloc(ab->b,ab->len+len); if (new == NULL) return; memcpy(new+ab->len,s,len); ab->b = new; ab->len += len; } static void abFree(struct abuf *ab) { free(ab->b); } /* Single line low level line refresh. * * Rewrite the currently edited line accordingly to the buffer content, * cursor position, and number of columns of the terminal. */ static void refreshSingleLine(struct linenoiseState *l) { char seq[64]; size_t plen = strlen(l->prompt); int fd = l->ofd; char *buf = l->buf; size_t len = l->len; size_t pos = l->pos; struct abuf ab; while((plen+pos) >= l->cols) { buf++; len--; pos--; } while (plen+len > l->cols) { len--; } abInit(&ab); /* Cursor to left edge */ snprintf(seq,64,"\r"); abAppend(&ab,seq,strlen(seq)); /* Write the prompt and the current buffer content */ abAppend(&ab,l->prompt,strlen(l->prompt)); abAppend(&ab,buf,len); /* Erase to right */ snprintf(seq,64,"\x1b[0K"); abAppend(&ab,seq,strlen(seq)); /* Move cursor to original position. */ snprintf(seq,64,"\r\x1b[%dC", (int)(pos+plen)); abAppend(&ab,seq,strlen(seq)); if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */ abFree(&ab); } /* Multi line low level line refresh. * * Rewrite the currently edited line accordingly to the buffer content, * cursor position, and number of columns of the terminal. */ static void refreshMultiLine(struct linenoiseState *l) { char seq[64]; int plen = strlen(l->prompt); int rows = (plen+l->len+l->cols-1)/l->cols; /* rows used by current buf. */ int rpos = (plen+l->oldpos+l->cols)/l->cols; /* cursor relative row. */ int rpos2; /* rpos after refresh. */ int col; /* colum position, zero-based. */ int old_rows = l->maxrows; int fd = l->ofd, j; struct abuf ab; /* Update maxrows if needed. */ if (rows > (int)l->maxrows) l->maxrows = rows; /* First step: clear all the lines used before. To do so start by * going to the last row. */ abInit(&ab); if (old_rows-rpos > 0) { lndebug("go down %d", old_rows-rpos); snprintf(seq,64,"\x1b[%dB", old_rows-rpos); abAppend(&ab,seq,strlen(seq)); } /* Now for every row clear it, go up. */ for (j = 0; j < old_rows-1; j++) { lndebug("clear+up"); snprintf(seq,64,"\r\x1b[0K\x1b[1A"); abAppend(&ab,seq,strlen(seq)); } /* Clean the top line. */ lndebug("clear"); snprintf(seq,64,"\r\x1b[0K"); abAppend(&ab,seq,strlen(seq)); /* Write the prompt and the current buffer content */ abAppend(&ab,l->prompt,strlen(l->prompt)); abAppend(&ab,l->buf,l->len); /* If we are at the very end of the screen with our prompt, we need to * emit a newline and move the prompt to the first column. */ if (l->pos && l->pos == l->len && (l->pos+plen) % l->cols == 0) { lndebug(""); abAppend(&ab,"\n",1); snprintf(seq,64,"\r"); abAppend(&ab,seq,strlen(seq)); rows++; if (rows > (int)l->maxrows) l->maxrows = rows; } /* Move cursor to right position. */ rpos2 = (plen+l->pos+l->cols)/l->cols; /* current cursor relative row. */ lndebug("rpos2 %d", rpos2); /* Go up till we reach the expected positon. */ if (rows-rpos2 > 0) { lndebug("go-up %d", rows-rpos2); snprintf(seq,64,"\x1b[%dA", rows-rpos2); abAppend(&ab,seq,strlen(seq)); } /* Set column. */ col = (plen+(int)l->pos) % (int)l->cols; lndebug("set col %d", 1+col); if (col) snprintf(seq,64,"\r\x1b[%dC", col); else snprintf(seq,64,"\r"); abAppend(&ab,seq,strlen(seq)); lndebug("\n"); l->oldpos = l->pos; if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */ abFree(&ab); } /* Calls the two low level functions refreshSingleLine() or * refreshMultiLine() according to the selected mode. */ static void refreshLine(struct linenoiseState *l) { if (mlmode) refreshMultiLine(l); else refreshSingleLine(l); } /* Insert the character 'c' at cursor current position. * * On error writing to the terminal -1 is returned, otherwise 0. */ int linenoiseEditInsert(struct linenoiseState *l, char c) { if (l->len < l->buflen) { if (l->len == l->pos) { l->buf[l->pos] = c; l->pos++; l->len++; l->buf[l->len] = '\0'; if ((!mlmode && l->plen+l->len < l->cols) /* || mlmode */) { /* Avoid a full update of the line in the * trivial case. */ if (write(l->ofd,&c,1) == -1) return -1; } else { refreshLine(l); } } else { memmove(l->buf+l->pos+1,l->buf+l->pos,l->len-l->pos); l->buf[l->pos] = c; l->len++; l->pos++; l->buf[l->len] = '\0'; refreshLine(l); } } return 0; } /* Move cursor on the left. */ void linenoiseEditMoveLeft(struct linenoiseState *l) { if (l->pos > 0) { l->pos--; refreshLine(l); } } /* Move cursor on the right. */ void linenoiseEditMoveRight(struct linenoiseState *l) { if (l->pos != l->len) { l->pos++; refreshLine(l); } } /* Move cursor to the start of the line. */ void linenoiseEditMoveHome(struct linenoiseState *l) { if (l->pos != 0) { l->pos = 0; refreshLine(l); } } /* Move cursor to the end of the line. */ void linenoiseEditMoveEnd(struct linenoiseState *l) { if (l->pos != l->len) { l->pos = l->len; refreshLine(l); } } /* Substitute the currently edited line with the next or previous history * entry as specified by 'dir'. */ #define LINENOISE_HISTORY_NEXT 0 #define LINENOISE_HISTORY_PREV 1 void linenoiseEditHistoryNext(struct linenoiseState *l, int dir) { if (history_len > 1) { /* Update the current history entry before to * overwrite it with the next one. */ free(history[history_len - 1 - l->history_index]); history[history_len - 1 - l->history_index] = strdup(l->buf); /* Show the new entry */ l->history_index += (dir == LINENOISE_HISTORY_PREV) ? 1 : -1; if (l->history_index < 0) { l->history_index = 0; return; } else if (l->history_index >= history_len) { l->history_index = history_len-1; return; } strncpy(l->buf,history[history_len - 1 - l->history_index],l->buflen); l->buf[l->buflen-1] = '\0'; l->len = l->pos = strlen(l->buf); refreshLine(l); } } /* Delete the character at the right of the cursor without altering the cursor * position. Basically this is what happens with the "Delete" keyboard key. */ void linenoiseEditDelete(struct linenoiseState *l) { if (l->len > 0 && l->pos < l->len) { memmove(l->buf+l->pos,l->buf+l->pos+1,l->len-l->pos-1); l->len--; l->buf[l->len] = '\0'; refreshLine(l); } } /* Backspace implementation. */ void linenoiseEditBackspace(struct linenoiseState *l) { if (l->pos > 0 && l->len > 0) { memmove(l->buf+l->pos-1,l->buf+l->pos,l->len-l->pos); l->pos--; l->len--; l->buf[l->len] = '\0'; refreshLine(l); } } /* Delete the previosu word, maintaining the cursor at the start of the * current word. */ void linenoiseEditDeletePrevWord(struct linenoiseState *l) { size_t old_pos = l->pos; size_t diff; while (l->pos > 0 && l->buf[l->pos-1] == ' ') l->pos--; while (l->pos > 0 && l->buf[l->pos-1] != ' ') l->pos--; diff = old_pos - l->pos; memmove(l->buf+l->pos,l->buf+old_pos,l->len-old_pos+1); l->len -= diff; refreshLine(l); } /* This function is the core of the line editing capability of linenoise. * It expects 'fd' to be already in "raw mode" so that every key pressed * will be returned ASAP to read(). * * The resulting string is put into 'buf' when the user type enter, or * when ctrl+d is typed. * * The function returns the length of the current buffer. */ static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt) { struct linenoiseState l; /* Populate the linenoise state that we pass to functions implementing * specific editing functionalities. */ l.ifd = stdin_fd; l.ofd = stdout_fd; l.buf = buf; l.buflen = buflen; l.prompt = prompt; l.plen = strlen(prompt); l.oldpos = l.pos = 0; l.len = 0; l.cols = getColumns(stdin_fd, stdout_fd); l.maxrows = 0; l.history_index = 0; /* Buffer starts empty. */ l.buf[0] = '\0'; l.buflen--; /* Make sure there is always space for the nulterm */ /* The latest history entry is always our current buffer, that * initially is just an empty string. */ linenoiseHistoryAdd(""); if (write(l.ofd,prompt,l.plen) == -1) return -1; while(1) { char c; int nread; char seq[3]; nread = read(l.ifd,&c,1); if (nread < 0) { return nread; } else if (nread == 0) { errno = EAGAIN; return -1; } /* Only autocomplete when the callback is set. It returns < 0 when * there was an error reading from fd. Otherwise it will return the * character that should be handled next. */ if (c == 9 && completionCallback != NULL) { c = completeLine(&l); /* Return on errors */ if (c < 0) return l.len; /* Read next character when 0 */ if (c == 0) continue; } switch(c) { case ENTER: /* enter */ history_len--; free(history[history_len]); if (mlmode) linenoiseEditMoveEnd(&l); return (int)l.len; case CTRL_C: /* ctrl-c */ errno = EAGAIN; return -1; case BACKSPACE: /* backspace */ case 8: /* ctrl-h */ linenoiseEditBackspace(&l); break; case CTRL_D: /* ctrl-d, remove char at right of cursor, or if the line is empty, act as end-of-file. */ if (l.len > 0) { linenoiseEditDelete(&l); } else { history_len--; free(history[history_len]); return -1; } break; case CTRL_T: /* ctrl-t, swaps current character with previous. */ if (l.pos > 0 && l.pos < l.len) { int aux = buf[l.pos-1]; buf[l.pos-1] = buf[l.pos]; buf[l.pos] = aux; if (l.pos != l.len-1) l.pos++; refreshLine(&l); } break; case CTRL_B: /* ctrl-b */ linenoiseEditMoveLeft(&l); break; case CTRL_F: /* ctrl-f */ linenoiseEditMoveRight(&l); break; case CTRL_P: /* ctrl-p */ linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_PREV); break; case CTRL_N: /* ctrl-n */ linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_NEXT); break; case ESC: /* escape sequence */ /* Read the next two bytes representing the escape sequence. * Use two calls to handle slow terminals returning the two * chars at different times. */ if (read(l.ifd,seq,1) == -1) break; if (read(l.ifd,seq+1,1) == -1) break; /* ESC [ sequences. */ if (seq[0] == '[') { if (seq[1] >= '0' && seq[1] <= '9') { /* Extended escape, read additional byte. */ if (read(l.ifd,seq+2,1) == -1) break; if (seq[2] == '~') { switch(seq[1]) { case '3': /* Delete key. */ linenoiseEditDelete(&l); break; } } } else { switch(seq[1]) { case 'A': /* Up */ linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_PREV); break; case 'B': /* Down */ linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_NEXT); break; case 'C': /* Right */ linenoiseEditMoveRight(&l); break; case 'D': /* Left */ linenoiseEditMoveLeft(&l); break; case 'H': /* Home */ linenoiseEditMoveHome(&l); break; case 'F': /* End*/ linenoiseEditMoveEnd(&l); break; } } } /* ESC O sequences. */ else if (seq[0] == 'O') { switch(seq[1]) { case 'H': /* Home */ linenoiseEditMoveHome(&l); break; case 'F': /* End*/ linenoiseEditMoveEnd(&l); break; } } break; default: if (linenoiseEditInsert(&l,c)) return -1; break; case CTRL_U: /* Ctrl+u, delete the whole line. */ buf[0] = '\0'; l.pos = l.len = 0; refreshLine(&l); break; case CTRL_K: /* Ctrl+k, delete from current to end of line. */ buf[l.pos] = '\0'; l.len = l.pos; refreshLine(&l); break; case CTRL_A: /* Ctrl+a, go to the start of the line */ linenoiseEditMoveHome(&l); break; case CTRL_E: /* ctrl+e, go to the end of the line */ linenoiseEditMoveEnd(&l); break; case CTRL_L: /* ctrl+l, clear screen */ linenoiseClearScreen(); refreshLine(&l); break; case CTRL_W: /* ctrl+w, delete previous word */ linenoiseEditDeletePrevWord(&l); break; } } return l.len; } /* This special mode is used by linenoise in order to print scan codes * on screen for debugging / development purposes. It is implemented * by the linenoise_example program using the --keycodes option. */ void linenoisePrintKeyCodes(void) { char quit[4]; printf("Linenoise key codes debugging mode.\n" "Press keys to see scan codes. Type 'quit' at any time to exit.\n"); if (enableRawMode(STDIN_FILENO) == -1) return; memset(quit,' ',4); while(1) { char c; int nread; nread = read(STDIN_FILENO,&c,1); if (nread <= 0) continue; memmove(quit,quit+1,sizeof(quit)-1); /* shift string to left. */ quit[sizeof(quit)-1] = c; /* Insert current char on the right. */ if (memcmp(quit,"quit",sizeof(quit)) == 0) break; printf("'%c' %02x (%d) (type quit to exit)\n", isprint(c) ? c : '?', (int)c, (int)c); printf("\r"); /* Go left edge manually, we are in raw mode. */ fflush(stdout); } disableRawMode(STDIN_FILENO); } /* This function calls the line editing function linenoiseEdit() using * the STDIN file descriptor set in raw mode. */ static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) { int count; if (buflen == 0) { errno = EINVAL; return -1; } if (!isatty(STDIN_FILENO)) { /* Not a tty: read from file / pipe. */ if (fgets(buf, buflen, stdin) == NULL) return -1; count = strlen(buf); if (count && buf[count-1] == '\n') { count--; buf[count] = '\0'; } } else { /* Interactive editing. */ if (enableRawMode(STDIN_FILENO) == -1) return -1; count = linenoiseEdit(STDIN_FILENO, STDOUT_FILENO, buf, buflen, prompt); disableRawMode(STDIN_FILENO); printf("\n"); } return count; } /* The high level function that is the main API of the linenoise library. * This function checks if the terminal has basic capabilities, just checking * for a blacklist of stupid terminals, and later either calls the line * editing function or uses dummy fgets() so that you will be able to type * something even in the most desperate of the conditions. */ char *linenoise(const char *prompt) { char buf[LINENOISE_MAX_LINE]; int count; if (isUnsupportedTerm()) { size_t len; printf("%s",prompt); fflush(stdout); if (fgets(buf,LINENOISE_MAX_LINE,stdin) == NULL) return NULL; len = strlen(buf); while(len && (buf[len-1] == '\n' || buf[len-1] == '\r')) { len--; buf[len] = '\0'; } return strdup(buf); } else { count = linenoiseRaw(buf,LINENOISE_MAX_LINE,prompt); if (count == -1) return NULL; return strdup(buf); } } /* ================================ History ================================= */ /* Free the history, but does not reset it. Only used when we have to * exit() to avoid memory leaks are reported by valgrind & co. */ static void freeHistory(void) { if (history) { int j; for (j = 0; j < history_len; j++) free(history[j]); free(history); } } /* At exit we'll try to fix the terminal to the initial conditions. */ static void linenoiseAtExit(void) { disableRawMode(STDIN_FILENO); freeHistory(); } /* This is the API call to add a new entry in the linenoise history. * It uses a fixed array of char pointers that are shifted (memmoved) * when the history max length is reached in order to remove the older * entry and make room for the new one, so it is not exactly suitable for huge * histories, but will work well for a few hundred of entries. * * Using a circular buffer is smarter, but a bit more complex to handle. */ int linenoiseHistoryAdd(const char *line) { char *linecopy; if (history_max_len == 0) return 0; /* Initialization on first call. */ if (history == NULL) { history = malloc(sizeof(char*)*history_max_len); if (history == NULL) return 0; memset(history,0,(sizeof(char*)*history_max_len)); } /* Don't add duplicated lines. */ if (history_len && !strcmp(history[history_len-1], line)) return 0; /* Add an heap allocated copy of the line in the history. * If we reached the max length, remove the older line. */ linecopy = strdup(line); if (!linecopy) return 0; if (history_len == history_max_len) { free(history[0]); memmove(history,history+1,sizeof(char*)*(history_max_len-1)); history_len--; } history[history_len] = linecopy; history_len++; return 1; } /* Set the maximum length for the history. This function can be called even * if there is already some history, the function will make sure to retain * just the latest 'len' elements if the new history length value is smaller * than the amount of items already inside the history. */ int linenoiseHistorySetMaxLen(int len) { char **new; if (len < 1) return 0; if (history) { int tocopy = history_len; new = malloc(sizeof(char*)*len); if (new == NULL) return 0; /* If we can't copy everything, free the elements we'll not use. */ if (len < tocopy) { int j; for (j = 0; j < tocopy-len; j++) free(history[j]); tocopy = len; } memset(new,0,sizeof(char*)*len); memcpy(new,history+(history_len-tocopy), sizeof(char*)*tocopy); free(history); history = new; } history_max_len = len; if (history_len > history_max_len) history_len = history_max_len; return 1; } /* Save the history in the specified file. On success 0 is returned * otherwise -1 is returned. */ int linenoiseHistorySave(const char *filename) { FILE *fp = fopen(filename,"w"); int j; if (fp == NULL) return -1; for (j = 0; j < history_len; j++) fprintf(fp,"%s\n",history[j]); fclose(fp); return 0; } /* Load the history from the specified file. If the file does not exist * zero is returned and no operation is performed. * * If the file exists and the operation succeeded 0 is returned, otherwise * on error -1 is returned. */ int linenoiseHistoryLoad(const char *filename) { FILE *fp = fopen(filename,"r"); char buf[LINENOISE_MAX_LINE]; if (fp == NULL) return -1; while (fgets(buf,LINENOISE_MAX_LINE,fp) != NULL) { char *p; p = strchr(buf,'\r'); if (!p) p = strchr(buf,'\n'); if (p) *p = '\0'; linenoiseHistoryAdd(buf); } fclose(fp); return 0; } picocom-2.2/linenoise-1.0/linenoise.h000066400000000000000000000047601277475702100174500ustar00rootroot00000000000000/* linenoise.h -- VERSION 1.0 * * Guerrilla line editing library against the idea that a line editing lib * needs to be 20,000 lines of C code. * * See linenoise.c for more information. * * ------------------------------------------------------------------------ * * Copyright (c) 2010-2014, Salvatore Sanfilippo * Copyright (c) 2010-2013, Pieter Noordhuis * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __LINENOISE_H #define __LINENOISE_H #ifdef __cplusplus extern "C" { #endif typedef struct linenoiseCompletions { size_t len; char **cvec; } linenoiseCompletions; typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *); void linenoiseSetCompletionCallback(linenoiseCompletionCallback *); void linenoiseAddCompletion(linenoiseCompletions *, const char *); char *linenoise(const char *prompt); int linenoiseHistoryAdd(const char *line); int linenoiseHistorySetMaxLen(int len); int linenoiseHistorySave(const char *filename); int linenoiseHistoryLoad(const char *filename); void linenoiseClearScreen(void); void linenoiseSetMultiLine(int ml); void linenoisePrintKeyCodes(void); #ifdef __cplusplus } #endif #endif /* __LINENOISE_H */ picocom-2.2/pcasc000077500000000000000000000001501277475702100140350ustar00rootroot00000000000000#!/bin/sh exec picocom \ --send-cmd="ascii-xfr -senv -l5" \ --receive-cmd="ascii-xfr -rnv" "$@" picocom-2.2/pcxm000077500000000000000000000001311277475702100137120ustar00rootroot00000000000000#!/bin/sh exec picocom \ --send-cmd="sx -b -vv" \ --receive-cmd="rx -b -vv" "$@" picocom-2.2/pcym000077500000000000000000000001341277475702100137160ustar00rootroot00000000000000#!/bin/sh exec picocom \ --send-cmd="sb -b -vv" \ --receive-cmd="rb -b -E -vv" "$@" picocom-2.2/pczm000077500000000000000000000001341277475702100137170ustar00rootroot00000000000000#!/bin/sh exec picocom \ --send-cmd="sz -b -vv" \ --receive-cmd="rz -b -E -vv" "$@" picocom-2.2/picocom.1000066400000000000000000000412741277475702100145450ustar00rootroot00000000000000.\" Automatically generated by Pandoc 1.16.0.2 .\" .TH "PICOCOM" "1" "2016-10-04" "Picocom 2.2" "User Commands" .hy .SH NAME .PP picocom \- minimal dumb\-terminal emulation program .SH SYNOPSIS .PP \f[B]picocom\f[] [ \f[I]options\f[] ] \f[I]device\f[] .SH DESCRIPTION .PP As its name suggests, \f[B]picocom(1)\f[] is a minimal dumb\-terminal emulation program. It is, in principle, very much like \f[B]minicom(1)\f[], only it\[aq]s "pico" instead of "mini"! It was designed to serve as a simple, manual, modem configuration, testing, and debugging tool. It has also served (quite well) as a low\-tech serial communications program to allow access to all types of devices that provide serial consoles. It could also prove useful in many other similar tasks. .PP When picocom starts it opens the terminal (serial device) given as its non\-option argument. Unless the \f[B]\-\-noinit\f[] option is given, it configures the device to the settings specified by the option\-arguments (or to some default settings), and sets it to "raw" mode. If \f[B]\-\-noinit\f[] is given, the initialization and configuration is skipped; the device is just opened. Following this, picocom sets the standard\-input and standard\-output to raw mode. Having done so, it goes in a loop where it listens for input from stdin, or from the serial port. Input from the serial port is copied to the standard output while input from the standard input is copied to the serial port. Picocom also scans its input stream for a user\-specified control character, called the \f[I]escape character\f[] (being by default \f[B]C\-a\f[]). If the escape character is seen, then instead of sending it to the serial\-device, the program enters "command mode" and waits for the next character (which is called the "function character"). Depending on the value of the function character, picocom performs one of the operations described in the \f[B]COMMANDS\f[] section below. .SH COMMANDS .PP Commands are given to picocom by first keying the \f[I]espace character\f[] which by default is \f[B]C\-a\f[] (see \f[B]OPTIONS\f[] below for how to change it), and then keying one of the function (command) characters shown here. .TP .B \f[I]escape character\f[] Send the escape character to the serial port and return to "transparent" mode. This means that if the escape character (\f[B]C\-a\f[], by default) is typed twice, the program sends the escape character to the serial port, and remains in transparent mode. .RS .RE .TP .B \f[B]C\-x\f[] Exit the program: if the \f[B]\-\-noreset\f[] option was not given then the serial port is reset to its original settings before exiting; if it was given the serial port is not reset. .RS .RE .TP .B \f[B]C\-q\f[] Quit the program \f[I]without\f[] reseting the serial port, regardless of the \f[B]\-\-noreset\f[] option. .RS .RE .TP .B \f[B]C\-p\f[] Pulse the DTR line. Lower it for 1 sec, and then raise it again. .RS .RE .TP .B \f[B]C\-t\f[] Toggle the DTR line. If DTR is up, then lower it. If it is down, then raise it. .RS .RE .TP .B \f[B]C\-backslash\f[] Generate a break sequence on the serial line. A break sequence is usually generated by marking (driving to logical one) the serial Tx line for an amount of time coresponding to several character durations. .RS .RE .TP .B \f[B]C\-b\f[] Set baurdate. Prompts you to enter a baudrate numerically (in bps) and configures the serial port accordingly. .RS .RE .TP .B \f[B]C\-u\f[] Baud up. Increase the baud\-rate. The list of baud\-rates stepped\-through by this command is: 50, 75, 110, 134, 150, 200, 300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200. If \f[C]HIGH_BAUD\f[] support is compiled\-in, then the following baud\-rates are also added to the list: 230400, 460800, 500000, 576000, 921600, 1000000, 1152000, 1500000, 2000000, 2500000, 3000000, 3500000, 4000000. Depending on you system, any of the higher baud rates may be missing. .RS .RE .TP .B \f[B]C\-d\f[] Baud down. Decrease the baud\-rate. The list of baud\-rates stepped\-through by this command is the same as for the "baud\-up" command. .RS .RE .TP .B \f[B]C\-f\f[] Cycle through flow\-control settings (RTS/CTS, XON/XOFF, none). .RS .RE .TP .B \f[B]C\-y\f[] Cycle through parity settings (even, odd, none). .RS .RE .TP .B \f[B]C\-i\f[] Cycle through databits\-number settings (5, 6, 7, 8). .RS .RE .TP .B \f[B]C\-j\f[] Cycle through stopbits\-number settings (1, 2). .RS .RE .TP .B \f[B]C\-c\f[] Toggle local\-echo mode. .RS .RE .TP .B \f[B]C\-v\f[] Show program options (like baud rate, data bits, etc) as well as the actual serial port settings. Only the options and port settings that can be modified online (through commands) are shown, not those that can only be set at the command\-line. See \f[B]DISPLAY OF OPTIONS AND PORT SETTINGS\f[] for details. .RS .RE .TP .B \f[B]C\-h\f[] or \f[B]C\-k\f[] Show help or show keys. Prints a short description of all available function (command) keys. .RS .RE .TP .B \f[B]C\-s\f[] Send (upload) a file. See \f[B]SENDING AND RECEIVING FILES\f[] below. .RS .RE .TP .B \f[B]C\-r\f[] Receive (download) a file. See \f[B]SENDING AND RECEIVING FILES\f[] below. .RS .RE .PP After performing one of the above operations, the program leaves the command mode and enters transparent mode. Example: To increase the baud\-rate by two steps, you have to type: .RS .PP \f[B]C\-a\f[], \f[B]C\-u\f[], \f[B]C\-a\f[], \f[B]C\-u\f[] .RE .PP assuming of\-course that \f[B]C\-a\f[] is the escape character. .SH OPTIONS .PP Picocom accepts the following command\-line options. .TP .B \f[B]\-\-baud\f[] | \f[B]\-b\f[] Defines the baud\-rate to set the serial\-port (terminal) to. .RS .RE .TP .B \f[B]\-\-flow\f[] | \f[B]\-f\f[] Defines the flow\-control mode to set the serial\-port to. Must be one of: \f[B]x\f[] for xon/xoff (software) mode, \f[B]h\f[] for hardware flow control (RTS/CTS), \f[B]n\f[] for no flow control. (Default: \f[B]n\f[]) .RS .RE .TP .B \f[B]\-\-parity\f[] | \f[B]\-y\f[] Defines the parity mode to set the serial\-port to. Must be one of: \f[B]o\f[] for odd parity mode, \f[B]e\f[] for even parity mode, \f[B]n\f[] for no parity mode. (Default: \f[B]n\f[]) .RS .RE .TP .B \f[B]\-\-databits\f[] | \f[B]\-d\f[] Defines the number of data bits in every character. Must be one of: \f[B]5\f[], \f[B]6\f[], \f[B]7\f[], \f[B]8\f[]. (Default: \f[B]8\f[]) .RS .RE .TP .B \f[B]\-\-stopbits\f[] | \f[B]\-p\f[] Defines the number of stop bits in every character. Must be one of: \f[B]1\f[], or \f[B]2\f[]. (Default: \f[B]1\f[]) .RS .RE .TP .B \f[B]\-\-escape\f[] | \f[B]\-e\f[] Defines the character that will make picocom enter command\-mode (see description above). If \f[B]x\f[] is given, then \f[B]C\-x\f[] will make picocom enter command mode. (Default: \f[B]a\f[]) .RS .RE .TP .B \f[B]\-\-echo\f[] | \f[B]\-c\f[] Enable local echo. Every character being read from the terminal (standard input) is echoed to the terminal (standard output) subject to the echo\-mapping configuration (see \f[B]\-\-emap\f[] option). (Default: Disabled) .RS .RE .TP .B \f[B]\-\-noinit\f[] | \f[B]\-i\f[] If given, picocom will not initialize, reset, or otherwise meddle with the serial port at start\-up. It will just open it. This is useful, for example, for connecting picocom to already\-connected modems, or already configured ports without terminating the connection, or altering the settings. If required, serial port parameters can then be adjusted at run\-time by commands. (Default: Disabled) .RS .RE .TP .B \f[B]\-\-noreset\f[] | \f[B]\-r\f[] If given, picocom will not reset the serial port when exiting. It will just close the filedes and do nothing more. This is useful, for example, for leaving modems connected when exiting picocom. Regardless whether the \f[B]\-\-noreset\f[] option is given, the user can exit picocom using the "Quit" command (instead of "Exit"), which never resets the serial port. If \f[B]\-\-noreset\f[] is given then "Quit" and "Exit" behave essentially the same. (Default: Disabled) .RS .RE .TP .B \f[B]\-\-nolock\f[] | \f[B]\-l\f[] If given, picocom will \f[I]not\f[] attempt to lock the serial port before opening it. Normally, depending on how it\[aq]s compiled, picocom attempts to get a UUCP\-style lock\-file (e.g. \[aq]/var/lock/LCK..ttyS0\[aq]) before opening the port, or attempts to lock the port device\-node using \f[B]flock(2)\f[]. Failing to do so, results in the program exiting after emitting an error\-message. It is possible that your picocom binary is compiled without support for locking. In this case the \f[B]\-\-nolock\f[] option is accepted, but has no effect. (Default: Disabled) .RS .RE .TP .B \f[B]\-\-send\-cmd\f[] | \f[B]\-s\f[] Specifies the external program (and any arguments to it) that will be used for transmitting files. If the argument to \f[B]\-\-send\-cmd\f[] is the empty string (\[aq]\[aq]), the send\-file command is disabled. See \f[B]SENDING AND RECEIVING FILES\f[]. (Default: \f[B]sz \-vv\f[]) .RS .RE .TP .B \f[B]\-\-receive\-cmd\f[] | \f[B]\-v\f[] Specifies the external program (and any arguments to it) that will be used for receiving files. If the argument to \f[B]\-\-receive\-cmd\f[] is the empty string (\[aq]\[aq]), the receive\-file command is disabled. See \f[B]SENDING AND RECEIVING FILES\f[]. (Default: \f[B]rz \-vv\f[]) .RS .RE .TP .B \f[B]\-\-imap\f[] Specifies the input character map (i.e. special characters to be replaced when read from the serial port). See \f[B]INPUT, OUTPUT, AND ECHO MAPPING\f[]. (Defaul: Empty) .RS .RE .TP .B \f[B]\-\-omap\f[] Specifies the output character map (i.e. special characters to be replaced before being written to serial port). See \f[B]INPUT, OUTPUT, AND ECHO MAPPING\f[]. (Defaul: Empty) .RS .RE .TP .B \f[B]\-\-emap\f[] Specifies the local\-echo character map (i.e. special characters to be replaced before being echoed\-back to the terminal, if local\-echo is enabled). See \f[B]INPUT, OUTPUT, AND ECHO MAPPING\f[]. (Defaul: \f[B]delbs,crcrlf\f[]) .RS .RE .TP .B \f[B]\-\-help\f[] | \f[B]\-h\f[] Print a short help message describing the command\-line options. Picocom\[aq]s version, ompile\-time options, and enabled features are also shown. .RS .RE .SH DISPLAY OF OPTIONS AND PORT SETTINGS .PP The "show program options" command (\f[B]C\-v\f[]), as well as the commands that change program options (\f[B]C\-b\f[], \f[B]C\-u\f[], \f[B]C\-d\f[], \f[B]C\-f\f[], etc) print messages showing the current values (or the new values, if they were changed) for the respective options. If picocom determines that an actual serial\-port setting differs from the current value of the respective option (for whatever reason), then the value of the option is shown followed by the value of the actual serial\-port setting in parenthesis. Example: .IP .nf \f[C] ***\ baud:\ 115200\ (9600)\ \f[] .fi .PP This means that a baud rate of 115200bps has been selected (from the command line, or using commands that change the baudrate) but the serial\-port is actually operating at 9600bps (the driver may not support the higher setting, and has silently replaced it with a safe default, or the setting may have been changed from outside picocom). If the option and the corresponding serial\-port setting are the same, only a single value is shown. Example: .IP .nf \f[C] ***\ baud:\ 9600 \f[] .fi .PP This behavioir was intriduced in picocom 2.0. Older releases displayed only the option values, not the actual serial\-port settings corresponding to them. .SH SENDING AND RECEIVING FILES .PP Picocom can send and receive files over the serial port using external programs that implement the respective protocols. In Linux typical programs for this purpose are: .IP \[bu] 2 \f[B]rx(1)\f[] \- receive using the X\-MODEM protocol .IP \[bu] 2 \f[B]rb(1)\f[] \- receive using the Y\-MODEM protocol .IP \[bu] 2 \f[B]rz(1)\f[] \- receive using the Z\-MODEM protocol .IP \[bu] 2 \f[B]sx(1)\f[] \- send using the X\-MODEM protocol .IP \[bu] 2 \f[B]sb(1)\f[] \- send using the Y\-MODEM protocol .IP \[bu] 2 \f[B]sz(1)\f[] \- send using the Z\-MODEM protocol .IP \[bu] 2 \f[B]ascii\-xfr(1)\f[] \- receive or transmit ASCII files .PP The name of, and the command\-line options to, the program to be used for transmitting files are given by the \f[B]\-\-send\-cmd\f[] option. Similarly the program to receive files, and its argumets, are given by the \f[B]\-\-receive\-cmd\f[] option. For example, in order to start a picocom session that uses \f[B]sz(1)\f[] to transmit files, and \f[B]rz(1)\f[] to receive files, you have to say something like this: .IP .nf \f[C] picocom\ \-\-send\-cmd\ "sz\ \-vv"\ \-\-receive\-cmd\ "rz\ \-vv"\ ... \f[] .fi .PP If the argument to the \f[B]\-send\-cmd\f[] option, or the argument to the \f[B]\-\-receive\-cmd\f[] option is the empty string, then the respective command is disabled. For example, in order to disable both the "send" and the "receive" commands you can invoke picocom like this: .IP .nf \f[C] picocom\ \-\-send\-cmd\ \[aq]\[aq]\ \-\-receive\-cmd\ \[aq]\[aq]\ ... \f[] .fi .PP A picocom session with both, the send\- and the receive\-file commands disabled does not \f[B]fork(2)\f[] and does not run any external programs. .PP During the picocom session, if you key the "send" or "receive" commands (e.g. by pressing \f[B]C\-a\f[], \f[B]C\-s\f[], or \f[B]C\-a\f[], \f[B]C\-r\f[]) you will be prompted for a filename. At this prompt you can enter one or more file\-names, and any additional arguments to the transmission or reception program. Command\-line editing and rudimentary pathname completion are available at this prompt, if you have compiled picocom with support for the linenoise library. Pressing \f[B]C\-c\f[] at this prompt will cancel the file transfer command and return to normal picocom operation. After entering a filename (and / or additional transmission or reception program arguments) and assuming you have not canceled the operation by pressing \f[B]C\-c\f[], picocom will start the the external program as specified by the \f[B]\-\-send\-cmd\f[], or \f[B]\-\-receive\-cmd\f[] option, and with any filenames and additional arguments you may have supplied. The standard input and output of the external program will be connected to the serial port. The standard error of the external program will be connected to the terminal which\-\-\-while the program is running\-\-\-will revert to canonical mode. Pressing \f[B]C\-c\f[] while the external program is running will prematurely terminate it (assuming that the program itself does not ignore SIGINT), and return control to picocom. Pressing \f[B]C\-c\f[] at any other time, has no special effect; the character is normally passed to the serial port. .SH INPUT, OUTPUT, AND ECHO MAPPING .PP Using the \f[B]\-\-imap\f[], \f[B]\-\-omap\f[], and \f[B]\-\-emap\f[] options you can make picocom map (tranlate, replace) certain special characters after being read from the serial port (with \f[B]\-\-imap\f[]), before being written to the serial port (with \f[B]\-\-omap\f[]), and before being locally echoed to the terminal (standard output) if local echo is enabled (with \f[B]\-\-emap\f[]). These mapping options take, each, a single argument which is a comma\-separated list of one or more of the following identifiers: .IP \[bu] 2 \f[B]crlf\f[] (map CR to LF), .IP \[bu] 2 \f[B]crcrlf\f[] (map CR to CR + LF), .IP \[bu] 2 \f[B]igncr\f[] (ignore CR), .IP \[bu] 2 \f[B]lfcr\f[] (map LF to CR), .IP \[bu] 2 \f[B]lfcrlf\f[] (map LF to CR + LF), .IP \[bu] 2 \f[B]ignlf\f[] (ignore LF), .IP \[bu] 2 \f[B]bsdel\f[] (map BS to DEL), .IP \[bu] 2 \f[B]delbs\f[] (map DEL to BS) .PP For example the command: .IP .nf \f[C] picocom\ \-\-omap\ crlf,delbs\ \-\-imap\ inglf,bsdel\ \-\-emap\ crcrlf\ ... \f[] .fi .PP will: .IP \[bu] 2 Replace every CR (carriage return, 0x0d) caracter with LF (line feed, 0x0a) and every DEL (delete, 0x7f) character with BS (backspace, 0x08) before writing it to the serial port. .IP \[bu] 2 Ignore (not write to the terminal) every LF character read from the serial port, and replace every BS character read from the serial port with DEL. .IP \[bu] 2 Replace every CR character with CR and LF when echoing to the terminal (if local\-echo is enabled). .SH AUTHOR .PP Written by Nick Patavalis .SH AVAILABILITY .PP Download the latest release from: .SH COPYRIGHT .PP Copyright (c) 2004\-2016 Nick Patavalis .PP This file is part of Picocom. .PP Picocom 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. .PP Picocom is distributed in the hope that 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. .PP You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111\-1307 USA picocom-2.2/picocom.1.html000066400000000000000000000574601277475702100155140ustar00rootroot00000000000000 PICOCOM(1)

NAME

picocom - minimal dumb-terminal emulation program

SYNOPSIS

picocom [ options ] device

DESCRIPTION

As its name suggests, picocom(1) is a minimal dumb-terminal emulation program. It is, in principle, very much like minicom(1), only it's "pico" instead of "mini"! It was designed to serve as a simple, manual, modem configuration, testing, and debugging tool. It has also served (quite well) as a low-tech serial communications program to allow access to all types of devices that provide serial consoles. It could also prove useful in many other similar tasks.

When picocom starts it opens the terminal (serial device) given as its non-option argument. Unless the --noinit option is given, it configures the device to the settings specified by the option-arguments (or to some default settings), and sets it to "raw" mode. If --noinit is given, the initialization and configuration is skipped; the device is just opened. Following this, picocom sets the standard-input and standard-output to raw mode. Having done so, it goes in a loop where it listens for input from stdin, or from the serial port. Input from the serial port is copied to the standard output while input from the standard input is copied to the serial port. Picocom also scans its input stream for a user-specified control character, called the escape character (being by default C-a). If the escape character is seen, then instead of sending it to the serial-device, the program enters "command mode" and waits for the next character (which is called the "function character"). Depending on the value of the function character, picocom performs one of the operations described in the COMMANDS section below.

COMMANDS

Commands are given to picocom by first keying the espace character which by default is C-a (see OPTIONS below for how to change it), and then keying one of the function (command) characters shown here.

escape character

Send the escape character to the serial port and return to "transparent" mode. This means that if the escape character (C-a, by default) is typed twice, the program sends the escape character to the serial port, and remains in transparent mode.

C-x

Exit the program: if the --noreset option was not given then the serial port is reset to its original settings before exiting; if it was given the serial port is not reset.

C-q

Quit the program without reseting the serial port, regardless of the --noreset option.

C-p

Pulse the DTR line. Lower it for 1 sec, and then raise it again.

C-t

Toggle the DTR line. If DTR is up, then lower it. If it is down, then raise it.

C-backslash

Generate a break sequence on the serial line. A break sequence is usually generated by marking (driving to logical one) the serial Tx line for an amount of time coresponding to several character durations.

C-b

Set baurdate. Prompts you to enter a baudrate numerically (in bps) and configures the serial port accordingly.

C-u

Baud up. Increase the baud-rate. The list of baud-rates stepped-through by this command is: 50, 75, 110, 134, 150, 200, 300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200. If HIGH_BAUD support is compiled-in, then the following baud-rates are also added to the list: 230400, 460800, 500000, 576000, 921600, 1000000, 1152000, 1500000, 2000000, 2500000, 3000000, 3500000, 4000000. Depending on you system, any of the higher baud rates may be missing.

C-d

Baud down. Decrease the baud-rate. The list of baud-rates stepped-through by this command is the same as for the "baud-up" command.

C-f

Cycle through flow-control settings (RTS/CTS, XON/XOFF, none).

C-y

Cycle through parity settings (even, odd, none).

C-i

Cycle through databits-number settings (5, 6, 7, 8).

C-j

Cycle through stopbits-number settings (1, 2).

C-c

Toggle local-echo mode.

C-v

Show program options (like baud rate, data bits, etc) as well as the actual serial port settings. Only the options and port settings that can be modified online (through commands) are shown, not those that can only be set at the command-line. See DISPLAY OF OPTIONS AND PORT SETTINGS for details.

C-h or C-k

Show help or show keys. Prints a short description of all available function (command) keys.

C-s

Send (upload) a file. See SENDING AND RECEIVING FILES below.

C-r

Receive (download) a file. See SENDING AND RECEIVING FILES below.

After performing one of the above operations, the program leaves the command mode and enters transparent mode. Example: To increase the baud-rate by two steps, you have to type:

C-a, C-u, C-a, C-u

assuming of-course that C-a is the escape character.

OPTIONS

Picocom accepts the following command-line options.

--baud | -b

Defines the baud-rate to set the serial-port (terminal) to.

--flow | -f

Defines the flow-control mode to set the serial-port to. Must be one of: x for xon/xoff (software) mode, h for hardware flow control (RTS/CTS), n for no flow control. (Default: n)

--parity | -y

Defines the parity mode to set the serial-port to. Must be one of: o for odd parity mode, e for even parity mode, n for no parity mode. (Default: n)

--databits | -d

Defines the number of data bits in every character. Must be one of: 5, 6, 7, 8. (Default: 8)

--stopbits | -p

Defines the number of stop bits in every character. Must be one of: 1, or 2. (Default: 1)

--escape | -e

Defines the character that will make picocom enter command-mode (see description above). If x is given, then C-x will make picocom enter command mode. (Default: a)

--echo | -c

Enable local echo. Every character being read from the terminal (standard input) is echoed to the terminal (standard output) subject to the echo-mapping configuration (see --emap option). (Default: Disabled)

--noinit | -i

If given, picocom will not initialize, reset, or otherwise meddle with the serial port at start-up. It will just open it. This is useful, for example, for connecting picocom to already-connected modems, or already configured ports without terminating the connection, or altering the settings. If required, serial port parameters can then be adjusted at run-time by commands. (Default: Disabled)

--noreset | -r

If given, picocom will not reset the serial port when exiting. It will just close the filedes and do nothing more. This is useful, for example, for leaving modems connected when exiting picocom. Regardless whether the --noreset option is given, the user can exit picocom using the "Quit" command (instead of "Exit"), which never resets the serial port. If --noreset is given then "Quit" and "Exit" behave essentially the same. (Default: Disabled)

--nolock | -l

If given, picocom will not attempt to lock the serial port before opening it. Normally, depending on how it's compiled, picocom attempts to get a UUCP-style lock-file (e.g. '/var/lock/LCK..ttyS0') before opening the port, or attempts to lock the port device-node using flock(2). Failing to do so, results in the program exiting after emitting an error-message. It is possible that your picocom binary is compiled without support for locking. In this case the --nolock option is accepted, but has no effect. (Default: Disabled)

--send-cmd | -s

Specifies the external program (and any arguments to it) that will be used for transmitting files. If the argument to --send-cmd is the empty string (''), the send-file command is disabled. See SENDING AND RECEIVING FILES. (Default: sz -vv)

--receive-cmd | -v

Specifies the external program (and any arguments to it) that will be used for receiving files. If the argument to --receive-cmd is the empty string (''), the receive-file command is disabled. See SENDING AND RECEIVING FILES. (Default: rz -vv)

--imap

Specifies the input character map (i.e. special characters to be replaced when read from the serial port). See INPUT, OUTPUT, AND ECHO MAPPING. (Defaul: Empty)

--omap

Specifies the output character map (i.e. special characters to be replaced before being written to serial port). See INPUT, OUTPUT, AND ECHO MAPPING. (Defaul: Empty)

--emap

Specifies the local-echo character map (i.e. special characters to be replaced before being echoed-back to the terminal, if local-echo is enabled). See INPUT, OUTPUT, AND ECHO MAPPING. (Defaul: delbs,crcrlf)

--help | -h

Print a short help message describing the command-line options. Picocom's version, ompile-time options, and enabled features are also shown.

DISPLAY OF OPTIONS AND PORT SETTINGS

The "show program options" command (C-v), as well as the commands that change program options (C-b, C-u, C-d, C-f, etc) print messages showing the current values (or the new values, if they were changed) for the respective options. If picocom determines that an actual serial-port setting differs from the current value of the respective option (for whatever reason), then the value of the option is shown followed by the value of the actual serial-port setting in parenthesis. Example:

*** baud: 115200 (9600) 

This means that a baud rate of 115200bps has been selected (from the command line, or using commands that change the baudrate) but the serial-port is actually operating at 9600bps (the driver may not support the higher setting, and has silently replaced it with a safe default, or the setting may have been changed from outside picocom). If the option and the corresponding serial-port setting are the same, only a single value is shown. Example:

*** baud: 9600

This behavioir was intriduced in picocom 2.0. Older releases displayed only the option values, not the actual serial-port settings corresponding to them.

SENDING AND RECEIVING FILES

Picocom can send and receive files over the serial port using external programs that implement the respective protocols. In Linux typical programs for this purpose are:

  • rx(1) - receive using the X-MODEM protocol
  • rb(1) - receive using the Y-MODEM protocol
  • rz(1) - receive using the Z-MODEM protocol
  • sx(1) - send using the X-MODEM protocol
  • sb(1) - send using the Y-MODEM protocol
  • sz(1) - send using the Z-MODEM protocol
  • ascii-xfr(1) - receive or transmit ASCII files

The name of, and the command-line options to, the program to be used for transmitting files are given by the --send-cmd option. Similarly the program to receive files, and its argumets, are given by the --receive-cmd option. For example, in order to start a picocom session that uses sz(1) to transmit files, and rz(1) to receive files, you have to say something like this:

picocom --send-cmd "sz -vv" --receive-cmd "rz -vv" ...

If the argument to the -send-cmd option, or the argument to the --receive-cmd option is the empty string, then the respective command is disabled. For example, in order to disable both the "send" and the "receive" commands you can invoke picocom like this:

picocom --send-cmd '' --receive-cmd '' ...

A picocom session with both, the send- and the receive-file commands disabled does not fork(2) and does not run any external programs.

During the picocom session, if you key the "send" or "receive" commands (e.g. by pressing C-a, C-s, or C-a, C-r) you will be prompted for a filename. At this prompt you can enter one or more file-names, and any additional arguments to the transmission or reception program. Command-line editing and rudimentary pathname completion are available at this prompt, if you have compiled picocom with support for the linenoise library. Pressing C-c at this prompt will cancel the file transfer command and return to normal picocom operation. After entering a filename (and / or additional transmission or reception program arguments) and assuming you have not canceled the operation by pressing C-c, picocom will start the the external program as specified by the --send-cmd, or --receive-cmd option, and with any filenames and additional arguments you may have supplied. The standard input and output of the external program will be connected to the serial port. The standard error of the external program will be connected to the terminal which---while the program is running---will revert to canonical mode. Pressing C-c while the external program is running will prematurely terminate it (assuming that the program itself does not ignore SIGINT), and return control to picocom. Pressing C-c at any other time, has no special effect; the character is normally passed to the serial port.

INPUT, OUTPUT, AND ECHO MAPPING

Using the --imap, --omap, and --emap options you can make picocom map (tranlate, replace) certain special characters after being read from the serial port (with --imap), before being written to the serial port (with --omap), and before being locally echoed to the terminal (standard output) if local echo is enabled (with --emap). These mapping options take, each, a single argument which is a comma-separated list of one or more of the following identifiers:

  • crlf (map CR to LF),
  • crcrlf (map CR to CR + LF),
  • igncr (ignore CR),
  • lfcr (map LF to CR),
  • lfcrlf (map LF to CR + LF),
  • ignlf (ignore LF),
  • bsdel (map BS to DEL),
  • delbs (map DEL to BS)

For example the command:

picocom --omap crlf,delbs --imap inglf,bsdel --emap crcrlf ...

will:

  • Replace every CR (carriage return, 0x0d) caracter with LF (line feed, 0x0a) and every DEL (delete, 0x7f) character with BS (backspace, 0x08) before writing it to the serial port.

  • Ignore (not write to the terminal) every LF character read from the serial port, and replace every BS character read from the serial port with DEL.

  • Replace every CR character with CR and LF when echoing to the terminal (if local-echo is enabled).

AUTHOR

Written by Nick Patavalis

AVAILABILITY

Download the latest release from: https://github.com/npat-efault/picocom/releases

COPYRIGHT

Copyright (c) 2004-2016 Nick Patavalis

This file is part of Picocom.

Picocom 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.

Picocom is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

picocom-2.2/picocom.1.md000066400000000000000000000364541277475702100151500ustar00rootroot00000000000000% PICOCOM(1) --- header: User Commands --- # NAME picocom - minimal dumb-terminal emulation program # SYNOPSIS **picocom** [ _options_ ] _device_ # DESCRIPTION As its name suggests, **picocom(1)** is a minimal dumb-terminal emulation program. It is, in principle, very much like **minicom(1)**, only it's "pico" instead of "mini"! It was designed to serve as a simple, manual, modem configuration, testing, and debugging tool. It has also served (quite well) as a low-tech serial communications program to allow access to all types of devices that provide serial consoles. It could also prove useful in many other similar tasks. When picocom starts it opens the terminal (serial device) given as its non-option argument. Unless the **--noinit** option is given, it configures the device to the settings specified by the option-arguments (or to some default settings), and sets it to "raw" mode. If **--noinit** is given, the initialization and configuration is skipped; the device is just opened. Following this, picocom sets the standard-input and standard-output to raw mode. Having done so, it goes in a loop where it listens for input from stdin, or from the serial port. Input from the serial port is copied to the standard output while input from the standard input is copied to the serial port. Picocom also scans its input stream for a user-specified control character, called the _escape character_ (being by default **C-a**). If the escape character is seen, then instead of sending it to the serial-device, the program enters "command mode" and waits for the next character (which is called the "function character"). Depending on the value of the function character, picocom performs one of the operations described in the **[COMMANDS]** section below. # COMMANDS Commands are given to picocom by first keying the *espace character* which by default is **C-a** (see **[OPTIONS]** below for how to change it), and then keying one of the function (command) characters shown here. *escape character* : Send the escape character to the serial port and return to "transparent" mode. This means that if the escape character (**C-a**, by default) is typed twice, the program sends the escape character to the serial port, and remains in transparent mode. **C-x** : Exit the program: if the **--noreset** option was not given then the serial port is reset to its original settings before exiting; if it was given the serial port is not reset. **C-q** : Quit the program _without_ reseting the serial port, regardless of the **--noreset** option. **C-p** : Pulse the DTR line. Lower it for 1 sec, and then raise it again. **C-t** : Toggle the DTR line. If DTR is up, then lower it. If it is down, then raise it. **C-backslash** : Generate a break sequence on the serial line. A break sequence is usually generated by marking (driving to logical one) the serial Tx line for an amount of time coresponding to several character durations. **C-b** : Set baurdate. Prompts you to enter a baudrate numerically (in bps) and configures the serial port accordingly. **C-u** : Baud up. Increase the baud-rate. The list of baud-rates stepped-through by this command is: 50, 75, 110, 134, 150, 200, 300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200. If `HIGH_BAUD` support is compiled-in, then the following baud-rates are also added to the list: 230400, 460800, 500000, 576000, 921600, 1000000, 1152000, 1500000, 2000000, 2500000, 3000000, 3500000, 4000000. Depending on you system, any of the higher baud rates may be missing. **C-d** : Baud down. Decrease the baud-rate. The list of baud-rates stepped-through by this command is the same as for the "baud-up" command. **C-f** : Cycle through flow-control settings (RTS/CTS, XON/XOFF, none). **C-y** : Cycle through parity settings (even, odd, none). **C-i** : Cycle through databits-number settings (5, 6, 7, 8). **C-j** : Cycle through stopbits-number settings (1, 2). **C-c** : Toggle local-echo mode. **C-v** : Show program options (like baud rate, data bits, etc) as well as the actual serial port settings. Only the options and port settings that can be modified online (through commands) are shown, not those that can only be set at the command-line. See **[DISPLAY OF OPTIONS AND PORT SETTINGS]** for details. **C-h** or **C-k** : Show help or show keys. Prints a short description of all available function (command) keys. **C-s** : Send (upload) a file. See **[SENDING AND RECEIVING FILES]** below. **C-r** : Receive (download) a file. See **[SENDING AND RECEIVING FILES]** below. After performing one of the above operations, the program leaves the command mode and enters transparent mode. Example: To increase the baud-rate by two steps, you have to type: > **C-a**, **C-u**, **C-a**, **C-u** assuming of-course that **C-a** is the escape character. # OPTIONS Picocom accepts the following command-line options. **--baud** | **-b** : Defines the baud-rate to set the serial-port (terminal) to. **--flow** | **-f** : Defines the flow-control mode to set the serial-port to. Must be one of: **x** for xon/xoff (software) mode, **h** for hardware flow control (RTS/CTS), **n** for no flow control. (Default: **n**) **--parity** | **-y** : Defines the parity mode to set the serial-port to. Must be one of: **o** for odd parity mode, **e** for even parity mode, **n** for no parity mode. (Default: **n**) **--databits** | **-d** : Defines the number of data bits in every character. Must be one of: **5**, **6**, **7**, **8**. (Default: **8**) **--stopbits** | **-p** : Defines the number of stop bits in every character. Must be one of: **1**, or **2**. (Default: **1**) **--escape** | **-e** : Defines the character that will make picocom enter command-mode (see description above). If **x** is given, then **C-x** will make picocom enter command mode. (Default: **a**) **--echo** | **-c** : Enable local echo. Every character being read from the terminal (standard input) is echoed to the terminal (standard output) subject to the echo-mapping configuration (see **--emap** option). (Default: Disabled) **--noinit** | **-i** : If given, picocom will not initialize, reset, or otherwise meddle with the serial port at start-up. It will just open it. This is useful, for example, for connecting picocom to already-connected modems, or already configured ports without terminating the connection, or altering the settings. If required, serial port parameters can then be adjusted at run-time by commands. (Default: Disabled) **--noreset** | **-r** : If given, picocom will not reset the serial port when exiting. It will just close the filedes and do nothing more. This is useful, for example, for leaving modems connected when exiting picocom. Regardless whether the **--noreset** option is given, the user can exit picocom using the "Quit" command (instead of "Exit"), which never resets the serial port. If **--noreset** is given then "Quit" and "Exit" behave essentially the same. (Default: Disabled) **--nolock** | **-l** : If given, picocom will _not_ attempt to lock the serial port before opening it. Normally, depending on how it's compiled, picocom attempts to get a UUCP-style lock-file (e.g. '/var/lock/LCK..ttyS0') before opening the port, or attempts to lock the port device-node using **flock(2)**. Failing to do so, results in the program exiting after emitting an error-message. It is possible that your picocom binary is compiled without support for locking. In this case the **--nolock** option is accepted, but has no effect. (Default: Disabled) **--send-cmd** | **-s** : Specifies the external program (and any arguments to it) that will be used for transmitting files. If the argument to **--send-cmd** is the empty string (''), the send-file command is disabled. See **[SENDING AND RECEIVING FILES]**. (Default: **sz -vv**) **--receive-cmd** | **-v** : Specifies the external program (and any arguments to it) that will be used for receiving files. If the argument to **--receive-cmd** is the empty string (''), the receive-file command is disabled. See **[SENDING AND RECEIVING FILES]**. (Default: **rz -vv**) **--imap** : Specifies the input character map (i.e. special characters to be replaced when read from the serial port). See **[INPUT, OUTPUT, AND ECHO MAPPING]**. (Defaul: Empty) **--omap** : Specifies the output character map (i.e. special characters to be replaced before being written to serial port). See **[INPUT, OUTPUT, AND ECHO MAPPING]**. (Defaul: Empty) **--emap** : Specifies the local-echo character map (i.e. special characters to be replaced before being echoed-back to the terminal, if local-echo is enabled). See **[INPUT, OUTPUT, AND ECHO MAPPING]**. (Defaul: **delbs,crcrlf**) **--help** | **-h** : Print a short help message describing the command-line options. Picocom's version, ompile-time options, and enabled features are also shown. # DISPLAY OF OPTIONS AND PORT SETTINGS The "show program options" command (**C-v**), as well as the commands that change program options (**C-b**, **C-u**, **C-d**, **C-f**, etc) print messages showing the current values (or the new values, if they were changed) for the respective options. If picocom determines that an actual serial-port setting differs from the current value of the respective option (for whatever reason), then the value of the option is shown followed by the value of the actual serial-port setting in parenthesis. Example: *** baud: 115200 (9600) This means that a baud rate of 115200bps has been selected (from the command line, or using commands that change the baudrate) but the serial-port is actually operating at 9600bps (the driver may not support the higher setting, and has silently replaced it with a safe default, or the setting may have been changed from outside picocom). If the option and the corresponding serial-port setting are the same, only a single value is shown. Example: *** baud: 9600 This behavioir was intriduced in picocom 2.0. Older releases displayed only the option values, not the actual serial-port settings corresponding to them. # SENDING AND RECEIVING FILES Picocom can send and receive files over the serial port using external programs that implement the respective protocols. In Linux typical programs for this purpose are: - **rx(1)** - receive using the X-MODEM protocol - **rb(1)** - receive using the Y-MODEM protocol - **rz(1)** - receive using the Z-MODEM protocol - **sx(1)** - send using the X-MODEM protocol - **sb(1)** - send using the Y-MODEM protocol - **sz(1)** - send using the Z-MODEM protocol - **ascii-xfr(1)** - receive or transmit ASCII files The name of, and the command-line options to, the program to be used for transmitting files are given by the **--send-cmd** option. Similarly the program to receive files, and its argumets, are given by the **--receive-cmd** option. For example, in order to start a picocom session that uses **sz(1)** to transmit files, and **rz(1)** to receive files, you have to say something like this: picocom --send-cmd "sz -vv" --receive-cmd "rz -vv" ... If the argument to the **-send-cmd** option, or the argument to the **--receive-cmd** option is the empty string, then the respective command is disabled. For example, in order to disable both the "send" and the "receive" commands you can invoke picocom like this: picocom --send-cmd '' --receive-cmd '' ... A picocom session with both, the send- and the receive-file commands disabled does not **fork(2)** and does not run any external programs. During the picocom session, if you key the "send" or "receive" commands (e.g. by pressing **C-a**, **C-s**, or **C-a**, **C-r**) you will be prompted for a filename. At this prompt you can enter one or more file-names, and any additional arguments to the transmission or reception program. Command-line editing and rudimentary pathname completion are available at this prompt, if you have compiled picocom with support for the linenoise library. Pressing **C-c** at this prompt will cancel the file transfer command and return to normal picocom operation. After entering a filename (and / or additional transmission or reception program arguments) and assuming you have not canceled the operation by pressing **C-c**, picocom will start the the external program as specified by the **--send-cmd**, or **--receive-cmd** option, and with any filenames and additional arguments you may have supplied. The standard input and output of the external program will be connected to the serial port. The standard error of the external program will be connected to the terminal which---while the program is running---will revert to canonical mode. Pressing **C-c** while the external program is running will prematurely terminate it (assuming that the program itself does not ignore SIGINT), and return control to picocom. Pressing **C-c** at any other time, has no special effect; the character is normally passed to the serial port. # INPUT, OUTPUT, AND ECHO MAPPING Using the **--imap**, **--omap**, and **--emap** options you can make picocom map (tranlate, replace) certain special characters after being read from the serial port (with **--imap**), before being written to the serial port (with **--omap**), and before being locally echoed to the terminal (standard output) if local echo is enabled (with **--emap**). These mapping options take, each, a single argument which is a comma-separated list of one or more of the following identifiers: - **crlf** (map CR to LF), - **crcrlf** (map CR to CR + LF), - **igncr** (ignore CR), - **lfcr** (map LF to CR), - **lfcrlf** (map LF to CR + LF), - **ignlf** (ignore LF), - **bsdel** (map BS to DEL), - **delbs** (map DEL to BS) For example the command: picocom --omap crlf,delbs --imap inglf,bsdel --emap crcrlf ... will: - Replace every CR (carriage return, 0x0d) caracter with LF (line feed, 0x0a) and every DEL (delete, 0x7f) character with BS (backspace, 0x08) before writing it to the serial port. - Ignore (not write to the terminal) every LF character read from the serial port, and replace every BS character read from the serial port with DEL. - Replace every CR character with CR and LF when echoing to the terminal (if local-echo is enabled). # AUTHOR Written by Nick Patavalis # AVAILABILITY Download the latest release from: # COPYRIGHT Copyright (c) 2004-2016 Nick Patavalis This file is part of Picocom. Picocom 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. Picocom is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA picocom-2.2/picocom.1.pdf000066400000000000000000000617431277475702100153200ustar00rootroot00000000000000%PDF-1.4 %âãÏÓ 3 0 obj << /Contents [4 0 R ] /Group << /CS /DeviceRGB /S /Transparency >> /Parent 2 0 R /Type /Page >> endobj 4 0 obj << /Filter [/FlateDecode ] /Length 3515 >> stream xœ­Ùrã6òÝ_õ“\e3o&OÏ$qjçHF©­­8µES°Ä˜‡†Çh´ß_ñ?n7.$%'»;©È4ú¾ÀO„þ¿yuAÉðÿï.ùþß^|"ß®/ÔšØ#quuñÕw!¡.Y?¢u~á:® òëêÃÝíûÛ÷oïWôþŠü·ÿ®ÈMB9ÌÕ/kÉmSUY½éþM¯~#ë-òÂÉãD}õ$‘òÑICXϰzwEÖ¿_ÄN”İh½¹|yõö á…Àª Œ"ºð7&Ôt†E8ž<²}Ü’8ˆl$~]틼ɛJœHVÏê¡*ê¢ÊJ5Ü ÕÃsÏZ˜'Y5”Y_4µšØ·Í¶ÍØŒ[^êx_Ö‰ìǾ{ÿáãÝÇ—ùã%Ž—ø§Ïµ¸BãùQ6_8pK•qͯö[𨝛=ò¦; â·s VÆu$Eþ¸†’¬>¹xerÌuà¯ñØÚ!1zýæãíÏwÖwïß½ÌfxƒúN~Q£$™Ÿüг*ñœ¬ŠÞÔYÅÆQ7l·¬ë»k=¥˜Ù¼•"ö;Y;Caœ–†ˆ‰™ˆiËÄ”4fBßbWw½| Ï…Æõø¼o‹:/ö%3Þ–öPPfßÒÖ-5à¼#“·Ž›&‚ÈjÈw¤,ž„/óCÇ‹Ëóp&‡©“,s”³cd(kðq<æš4uy$EïR·#—(†KRÔ]ϲ iÉ%¹ü¹ëÉAàâQßñ}O"3‚Ê:²a]±­Ù†ô ÿYlˆC'Âq}* °##]Q! D‚!+áoss5qŸÀ¦(°ØD#*€lXEò¦þc;´\¦×¤u+êí5È=H`u PHèý ž°ðmJ‡(Âxô3I¡@{ì†30@òýêÓPô €À ï²kÃÐñ#waé†ô×]S²n&«¼Ê–†G`b›àgn “ØšÍí2pm»T‰Ðбǡ A…•xŽ—j¼õÊ#iúd9 àE™µ¤Ïº§ÎYç;c¤§ŽxÀ?v¬&Ò/’®ÏÚ¾C%ÍžÕÈ:F”oÝ“üQl÷ J¦I$p2@"ÛA'·…t+ðJK™AÆfÀ&C :R7õ³˜$k¨Œ\y(°µg;T¬îòK RC¿íÇÀ-®>;‰ë‰@QêÌ ç纄ÎÙ÷}‡žˆ7½HD ù ´ep4òÏkè²pp-E¯| PìN}'IƒÙä6Z.íXn©#ÝžåÅà0Žü…ÀõYñ‘&1w‘–ÅÈDÝ€*5s5 ÓÔäè¡/vvM…˜>J‘G<×ÒÊ*eCÙk﯄÷„1×8 á²Íä‘ •$3‡KðÜ61Lp 'â½!I\îŸõ|Á(9 ×ñµ>%:Š9Ç‘ÿ΄æ…fèqå„cç ɘ öüX2ø©ØïÙæK%"H¶b­žŸ*Qtä÷¡–Ì66˾“Ñ q\ϲÌ6wSà&©;ƒ|àqlÉÊè3P~\ùz 6k7ÏE½z![5Õ =Î-ªDî‰ ®@d%äi¢X«øA*D(/˜oùŒhošl¸…m0.p¯)›fOàBγ€ä¤6‚é#X„ Çw-ðAº~S€2è±¹Ñ^‰\· çªÇû¦íyù–‚„‰Ìtî8Nüá 88„‚ª7ûB¤L¦¨ˆ”ËR»¢dÆXQzcpcQp£)—`Oï/Ì`.‘2À-éGHm‹P7¡ÒVˆR;Ej•gµˆ)®oYV­‘5AnŸµ+ÕÓ`Ï}Û¢ÊwY›å(U v•Œféz* Î!¹A) ×–ë;¸îç™Bè§3#É¥7Ó> ¸ˆí Í7à9ޤ¢ôdÝs¿z``-zÕêá8>£ƒûb¨ ÂІÞ} |R.Ü>g“*”9•)Øý•CâÍ832^LFÒ­£Žg6'hm-eˆ9Õ<ÚêÍȱ§· 6øöî¶XÈ¥C/ž 4+ŸeyïyÏj7Fwôë"ʨ<¢5k¡¢ÊE“‹{ÆKîzÇ Až&N:†|êK·‘¡Y  Øšéd3ãÙÚ/š(ŽC޾Št.£Z_>u¾D/µ¼¼Cc™¾ð(©¡^¢˜vüšíû¡„ä¨)Hxò®PTà²r`XQâRDůê«ê‘`¶wŒi{ÖKªŽ`ÈÐ ŽŠ¾µ]’¹ªrìò¶xæµmö“LåöýÛ·¯Þ½þx¶5Ô±Üì­=0 Ö™8v<Ïò®I‘ÆÖ´3m¢þµÆ¥½c‘€s"(’£ÀŸûb‡(€¤~~¬ìî¢ÉÁ± XQËÆÎæbò™è…"Äu]„@¤TÚ9ô-dR²M‚)p´PÙžØuÓ)‡guû,7PÎGŒ]/0ù3žÃXÖn®rM¬üºx6üzâÐÄ©ˆÌc÷)õEÇ.¡Š+à‰§k&ááý‡›Yä "o.ulE¢¦ù€êÒqÒ4$)ÕRñ=C¢ËÛ5c¹/-%ßQo17ÄzÅèRÔ; ªá“ò3CSÿMñ=jhŠáp´ûº_I÷EµvePã)ô±W>Í2þzÎH®vÄÁ´×‘Y륵É_ìOˆHLrɾ€¿ù3šªÕÊFÏÀ3úm~œ)êd†tå;¦»(›–õCË­ø²o!ûÛƒ[¨{³| R'Ea­¡š!ËjÙ)*'þ±BD5V©Žè ¡ûÕ²‘,$?¨kô2º~¦>k^«Ð A aì‹G³ŒÀÈÆÞzÊÕ%ÞF2®)8“/ü4YR‰‰®¥ª¬¨y¥eHAaEý„¾|5c/RœýrF¥&;0ò›/Øvy÷5ÈþLæÝ©×Þ´»‡6ÁxŸ!=•Yë~.Áð¬R­(Ñ©žÆ-Ãþ—QbêfZà@-p6Ž”@u¿yÒQ/„-â‹öù_Ó[~1—vd]ºòÖ­¦®ì@v$BÇ_hS§añ7ÈàB÷ø±LçktE-MŒfíéŒkR;%Ùȉ]Ò8«£}Jãì¶·Ô¸Oç4nÒ(×þ4Øw2)8ýjó³Ù 'J&<…,Ðw°hj€Ù‘ˆn8Íc¶’¹és¡¬Çæª N/šHéK°l!'(’âÌ"—Ã))ùó6Òíóþœ”ìÆgCÙ1NåëõϤ,jˆäïÒ¦/°ç~d‹Y Ì…ÿãÆ×fEÇ›KÙVÕ.`¢vV^rnPÿÅÌüô"EwŽî…k‘,™=?MZ³Ý–3vÜ=òØ×°¿têëž(vBï‹ÄÞ‚›æFîˆ"'Zp‡úÚæ s¶—ï»ó¸ðåO]™u»3üÀ\ÌH9Bé;¿g5|Œdä¡eÙHõÓÀꜩJTúÁ³f}5Ýp¢ý5tÎGcf+Ï4sËsA•µOhò3?·6ñ÷«M[Â4t ©k4E!”Ͷ€ SOÈ- ÊÖ_8q\©³šdU3@ðFPTñ€×ïö(ÎùU¬ŒP”ZÁKN[cô–³Ý$,šZõF¶Ò»¹Å`R÷ba/ÒšrNCìN¤Š»B²¡Ý€Ì&ýǶ©öYÍ€Ìá}Ô¤lØp¥ª‡ ˜œsù߯ ?zØwÀ}ãº@ÞþÑl9þì;ȰA2(”R|DNh»ù¾y'>9½Hñm8Ç7or¿K¾} lÇ1¹Ç©s0›Îh‰[ýqdÝs«=¦ïÆìÚ÷Ç…ÝFK¿ë^©<÷»¶¶;cñÑÄ¡èÌÖ¯è³7Ý×ã t¯däš¶P'V û8¼&”ºðã×øáÌ5¬…"ü¡|ìø$ø›òùÑÐÔ³'ü$°'Âxº…‚sv5ëùÜÝ£ò¬3Á4æŸæüp÷ýÿúöÕ/¯NàÆ'ïÖºaÏuW­íYù ìÜ%Ã+£ëqVvhqä©´FÅ•,{#(žõõs{Æ•!o¨9Ä5@¶ÙŒW ¨*_Ï¥Eóω¨¬Î=ßrãÇ¿¡‹ÿà/ò%èp™¹ò…à³Ë%+f<õÊ“3 ÇÇž¥,¡/ `_íõ´@Ìœi—¢ï鎠åf#²W€·)êfÕ€T‰Ý®Øâ÷È?"øWeGHòpæþ *ºNŸ{ø/çpö"åa6g<Ìd‡ãžáaT~áÃ"_öa<'VkµC^3át‰qìî˜H«¡íx¤Å¬í5¨Mæ˜ß Ì!tæìÄýXŽ6>…µQ:!sjv•Œ¼ñér~ËÃJVñO£Tçÿ’ã8ì/‰„>©–ðÛÀÅ’F]´y`žÿã? RG®ÇžK£êÞ¸Áÿ ôì1Ô&2„´%“Á7kòÓÅOÿÆS$endstream endobj 14 0 obj << /Contents [15 0 R ] /Group << /CS /DeviceRGB /S /Transparency >> /Parent 2 0 R /Type /Page >> endobj 15 0 obj << /Filter [/FlateDecode ] /Length 2577 >> stream xœÅZÛrÛF}×WÌ#YEAÀàî7¯,¥”J,ÅbRÙ²ö‡" q±ÄÔ~‚ý)øÇí¹bÊ*ׯ©Ø8—îžÓݧ{øYÈ„ÿøßñîÌB?ÃÿŸÎLôÓÙgô¯å™üÞÇÈ´ÜÁWg×.²L´ÜÀÓ2>3 Ó„Oèãìîæòöòöׇ™õ0G¯ý3GçÅÖœý^’]æ»]”­Ë± *àü?hùs«¢eÈw®#huqmyB˱!LéÙe³™£å'Å*íxÇéï uyˆS‚ªm‘×[ô-ç²x¡á»tîr­~jâ<«ŠÍ#ªD„¾¦à…hÜõ`ÎÕûwf‰Up$ùñÃÕåÕÍÊw×7¿\M~¶")T±ïžØžáö½Mc4ß=Àûƒ¤ÑŠ)£if| 1ádj±9ZöF^ Áðk<&ûÇÏÀ3 Û|å¸ÊÀl.éÛM¬fO o;ÈZ ¥mxÎ\ “X6ìl»'¬Ù®Is"´LÓâ‚­$“{J¢Î -¿xÅRÍEŒÈ°|K2¾+¢¬&-ÖöMˆXöD  Т-[`IN”ºzŽvû”¼A"µÿrƒ!2r”dqA¢’0!))i()A«ªždâ×/‚Iö`¥C^£­0Ž ßùìV9ª{òf€Æž»ØæÀ½¢#÷+.úp<Êí—Mýºi?h·Þ)Z¶>ÅFeYs8o ²¬ v6ÀhÆ–I7‘ü“Rú+±ü‰-Ú·Oñ6*€e’B°lrF9ê—>FNèéó‰øh„®ªò`†N°'™Aè*'ÇõQŸ}2±3ØÖí¶ÒÛÙ eáÇÙ]çà‘Rÿ(ŽÉ¾ÒÙi“§4„qQLløšý‰–Ê5%¯6ÆQMóFª¸¦aÅÁQ³š"YŽëêç¿#_3u:‡G£Ì˜¾ä…DÃ?yBƒg”Bz¨òÛÁ Škšo"Ä[FàéØè±™Ô÷ÙmⲩÀ ¹I;²kÁ°ð:¢+¨×+—~­ËŠV ?ͮوÙsž] ÿPÞý§*O¢³íK«ˆë'!»ï@âÔ0Õ¢;ìêù’ÇÑ6¨Í{!Ù{˜Ÿ={©ìY.?}“;Ò~kåƒDù0ðôì£ò ”­ÓêÍdÏt™a&ÎnÄìP›¾›FtÔÀcSc=¿ÇþÀ_ÄB/ñY·¼Ô¸•!ž8:b–¯×ݪ{¬v¼ùžíˆ8w;° ß:ACx• œÌöµõŒbÒŨq^VÅ*&· ¤ÚsZ¶Î86Eòñ F8ÿtº¾ž˜4ì„R„®'ê¸ú5ŒŠ®$ö¶lÉÖíÙ/g’Åu,†ç ˰eBRú¢HW‘¿"½â>ë?nÞ¨#M#÷5üÐ{Í$ÿ5“‚£I=s(в_WÈp o4l[Sí'Afyú5´ £ËjAöåÊ? 2ëØÎêQ^Œ54ñK¶g~ïá„zÁáã0c¾¼.¡GO¦Ž}îìøCÎØž/Ÿž’èaÔö-…tó!uòÒ€×ç-­g™8!Hi vml5ÆÁ$’0ëÿòmæºamÄÐçâ‘Ó{žh#a¤HJô˜E=Ã7_ŽðŒ5=²‰bóy2ÅQ+·¹ Œ-xXhøšÚ`ÄØ²‡¡bÓõ ì~/6µUû)lu›TlÒ‹(@f<‰Ì£æŒc‹û–«Œ5xÙ¢Kõé×Õ—¶Q¸mÁÐÎî… i´ä,H´F›ÌGá.ë8ŠÒ ¬L^¹}I²}]õng#Ý•‡¨ã4*[Í} ¶¾¢Ž¼‘Ñ’×ß¿¬WŸH\±¾M÷mvÑ~ÏKèìëcÍ›oÂׯ`‡³˜û8£u¯Âé5'ð)˜ñ©ó9ª‚ñ ®å¿°¢”@yè]RÒ]ËŸ*ŒÊ uYždIE•LÊ ÕçâñfÓ¹8Dß9áâíDêâÒíXL¤wnT¨ ’¿ÛÊ–Pr8¢â)) Ú‘õÀû”TÛ>.Ô >~?Ù½`e†r¨>2Šª©÷­°·7ÕgVÐIlؾÝô‰Ö,ùâURõÝi¹eøV±.>×%ÙÔé¢{±¡©S>H {^WüÒ]Ezà]Õ¢s®@™"WÚP¥Ô-ø–¬5ÃêN40®Žß•Ìðbé„ßΖÌþàJÒ×ÙÞ¼¹ÌEÉ31ÐïÆJ\k„ëý"Æ•ñàVÏu²,z7ÑP¾D;ÂZÙô–%¨"£5=N½¨³¦Jv¬©,/Šû§¥DzZ}„^[ZŸ;×Èì_¦HÇ<êYê+;Ñ DÀ_ý#¦î·LžÏu¦å[æ9ïÿ£nÓ7-•‹ò«%úíì·³ÿ`Qyendstream endobj 16 0 obj << /Contents [17 0 R ] /Group << /CS /DeviceRGB /S /Transparency >> /Parent 2 0 R /Type /Page >> endobj 17 0 obj << /Filter [/FlateDecode ] /Length 3281 >> stream xœÍZÛrãÆ}×WLíåâÎb0¸î›£•%öJ¶¸©JYy¡h”¬T>Áùþcºç .¤¶Ö®J¼¶εûôéËàˆÔÿÓÝ#ÿ~¾pÈ·¿?­.Ìï¡KÂØ#«üôxñîŸ0‡¬6ÐZ¥uxx&?-în®n¯n¿X°‡Kò¥ÿ\’·“s.>5¢&WÕn—”YóGLhoðòdõ8 ôiÌi™‘ÐÔqñ€x¸ÅñXVõ%Yý|áÔu±ë*³/Ñ’“cm&öÇóz ½ Ÿ—FŒ«±7ò˜«q,¤¾Ãõìrå'¹ªG#¿[´(Ê%Ùçi•V;òœ)«–Ôr+íV_žd_Õ-yÞŠ’½HSî‡j:N¹™î×¼ÍËGJnZçŒr”N¬Û¶>4­ÝN‹ªö XÚnþVˆL4öЧÝÌ*½-[Rܳ$å;žÚ!œo [$»ªUëúJ‹Õ6oü{hÄæP,ɦª»ãz°Pèéu“ýšìö…P] ‘¨Î`%ˆ&}ŸÔª™Ø5$­ÊR¤­È†Båe¡ßÕ¨‰ÎÕuG`ûQH3‡ÆccáQc!6X`Ô˜Z'©³B4 î PKL@>3ƒ5‚ÀÆ›B2HÖs"ÊÈ̺Kµoóªì:-ò¦$«Q4_²µ}„2nù€FŸ&XCðôÝÝNªò ¿ùá·ofDŒ»ïÐäúz’Tq yXäeÓŠ$#Õ†¼¹†¹ß<\.A’yº%¥Ñ¯R/r^1M×7ç©•!6cKÂLÿu%qи‘ÁPI.ˆç”’¤RT‡^)ܧ‘ã~ö!JÜ}yR®AlñdÏ…(S%F²[cXnÛ“+÷tJË¢lAjÅ‹a²“]}½æÒ`flr(Ú÷äCÞ$k žW¨>çàÎå_Té?‘Ô‹s¤ñ`†€Çs´ÎÝø•Ãw'´Þ ªè‰”qŸAÒ¶b·ï{-ÚªÆãY¿(ÚV ؾȕ-,ÖS˜Vµ%ØŸiæ-…G?€ÇU½Cõèçô]ÖÖÓ[\’LÀN2EX]/Üx\C³*ÉVû ÞÐÌkõx&yûà0 {·Ï ½TµXÀ8+òÞ2!Ÿ>]Ý›ö¥År¯Xôq†±ϧcÎÆ•Þ)¥‚så=Á1£‰úNü¿RÚ¶/÷HI‰“h9";€#Ñ~Ôj¡–VœVßèÎc®¬†ò¹™­f¦ 3Àµ÷aƦl#ŠxO[JŠó‰¤þ{yX¸`—(¬±œ†\3%ߨi½€òÀ5[pÍïI^HʯHV‘¦Z"Õ‚ÑC ‹ìëê±NvkêDÖ4làš“M l-vy«šà{꺪;  äqgÜ´eÌ@µiÄ õÕûªirà!ØWÒ’—êPw \çeR¿X’ÏmýÐZ¯žóv[l]5‡ýH{›.Pï2¤ÓG/oʆ«&å¼~x¬ßž9Õš¯…ØEû*|JÒTì!¶ZškMÌõ¨;Ãö mÒ@äKŒ_Ð0c€.bºãb6jûxƒýN÷áÇ.õæÝ¸¯ì˜î2t Í9âGÎ`n‚Îû½Hóß„ !´"üÒ.>íºþ .1ÂÐÈN8)Õ 8¤Ìu'ƒ^HR›à†òØ›tx<ìÀK–.½T–i-#µLÆÎm”±àÍFÁÍ‹i¬Bœ©¹øöGFr ÕŠxR³)—ÓÈ {þ íM‘¿ÍÇP¶d€Êht.XR´#Ô ¤Õ—¾Ù´µrvºý ‰_1ùòÄr —þ<ûöò™FúÑÀ—¯î…¸$çìÐ÷ƒyÞ_üpóñ[òõÇäÇë«ë›¿aë››ï®ïÇ´Le`&,BôØ‘›¶‘¡}EßüËXðñéérVì‹ÎÌÛi0ÉÃuLìCÞ2ˆ&E*rõS ?kÅÎL iLô鬉º¡½êÄÎ !ÆîW2]_1ÑÀÊ.úAe3,½$Æ ­v-i´]KÅ“¦µý³´ç®¥üˆnÔR¸Ú—Yy¼ÛG[ roüV~Óù@Q#Ý&Jµ!õã.|r`bõ§ò%5öùî#ÃB†´!ªç(Cu0±³jÊÐÍŽ2t{LsS(kÇ@´éÏÙ,A ºšÔœ{Ç'ƒ )å,£8|ˆøÿ=£Ô¿›Q¼( ÓÊ^¾Kö£XÔ–„zö êÁDSÛÏË=D"é6©“ÃI˜ë t>68H ë(-̰û"IMM©ÆbĦ† ±¯Lȃœ:²‚ÎNÚ>\R"ýF¿ø ˹ùx÷i¥Lo>ÄZ’ÛO+ì£Â&ŸÆ|®"âúêÏ·äû¯ïîŸ îÑ`†šïÉ5Ó+A˜çMÝDõŠRõÅÈT„*…P{?ˆ¶{íöïPÏ}k¨qfk|fš“éÚÚŽÁ;dؿ˔Ñ~1LŸkÇÄ ÎŸ)²zîlåÃ*V!ˆE§H·‡N ÝL0ýgšÊâ¨b1‰)%Áò";®ÈÿaFÜ̾ƒÌ³X’|co\…(uÎ3ÜÝgÀn 83þ¨ÛÁàÝyWSØ3g &9æÂÀd¨£S( 9õ≻9ëm™(ÖÍ2U1¢Ç­ü9­‹ÍhõÏq@Ü?™.nE±Ç8t{.åÞ0UÄ›+·ôPט˜iûFÁ¤{R`ú,Zã s§™ cÓÞÏf6fÝVg“î«Ë-½áMz¾>ê1“ ÎÃAúIcO F< ªlmÑ£snMúë0:¯ÝätÓ 2 H++6EàL(,4j“’@p€X@…aGYßm„ª®e¹viZ?û=Sä„Ї™3ÃŒ¯ÎºšÛH>ë“p«' ïH§r CyÓq^,Ýõ\ Š·3Rõ§”_T¦1ì>c0÷bof`3ížD^’}‚ºÛŠ&œ‘kõ]Ã{Cªsٽ˽q"üÓ⫯¾Â’£Ì'’CöÞ4ó]Ç1­‡E c˜#Û§‚ ³ÓæÀÜÉ/5v") â ®FjÀW­¶Þ7²¬¿ ÔFê{ €‹É‰Á€†Z`\ÀŒ¢œÁ·713àï]΋{Á­àÅœ¹f ]q¼y°?²Q:ƒ)M/xŸóÈ %+CÙáÙºÃ8+«sSüøxÁ+–Ž%ì’ùý¾šJ€ÅvÉ݉­’û6ÄoF4¦T…âo +[8M—‡äxÐnAkM²@_†|EW­ïfǺŽÔ’•¬>Û`æ[æÄ”uìvªäåÄ‘¡ ÍÝ3ì<×÷õ0‰%Hò›<†Ç©ÏÍ€(P, |5Ò_%/­-¯iAÁziÎlîjkq"ÈÆO–Vt]/Ö0«ÌWÌx5Ì9d"#µpî3 ä›ú> /Parent 2 0 R /Type /Page >> endobj 19 0 obj << /Filter [/FlateDecode ] /Length 3161 >> stream xœ­ZYwÛ6~÷¯Àñ“|ÆAîìG}¦~¨øääϜפ” üà°i ¦ëB+Lˆ/§õïmç—/§Ý@! /Ü[Ž¥”žõåmiË3 å×+áŒÚï=¿cÜ…~O¹Çs!ülùPVî~çj”OÀYºß2ì©¥rk NÇÜŽ0ÿxÂÚE4‰¬ª‘·Á"Z~B°îc°c#«'øãëMó8fûS1£n*0S‘ £ülT$Nê:ÞûokÓ/ºq™Õé]Η”H×”W*‚9ePÏöW0b\@c ŒÅ8µ¹+›ÁÃ)ZÍ©Æ#ÉØéGøÓªÕ› °ï åÄ©æ¹c‘@[{À& sÛ0ÊÏâ{bX†U°«ØÝÆ’À§žÿ¬XâÇÉÔŒ%·sðïÞ Òt(zø‘k‚Pêj¿$«¯(µ{®N]€¼ñIÍI¤FãxÉݵñüN}gÄ¡ )×–k|)¹Ñ_”Mט+|„Ì MüóíÌ•ù,šÂêŒ úÅÞ#þì‡ÖÁAgH]µ-@>¿RĬ[Gc4J†ýý£*:t#ß÷ldîÅrîëm¥Kìž*ÁqWÂO>k/whèššq¥1·#**¢ ÷†Xqj(¬„éz%ÓoogœÞÛa å¦B’ _ìRUcõt'ãÑdÓÃĺ?±£ëØlh Ðx?zí%Å CêM’R Haº¾º=šj-ýk–ç] ¨ö7ØdÓa°¬ji÷K6)l_¼Ù+ÃÄ‘coá®mу¡¶m@ÊæÆ–eagÒ².+n‘³CzõuÔ‚ë M%©ã µˆÚ†¥º^¨-5].3ÌÙàC 1àvw0V"ŽZC£eèÚÑÆEö׾ؚª ÷¬x9î 6‹Á±Ú.3\89Kö«¥I¢`Y†Ÿ6¢®\+vÆêLAbæºP¦µ¨s´vÁŠPLÎiCÆtk"ø¢5g`úŽwÈÅ“DWâ‚ ¹ŽB"‘ÔÛ8°—'Ü#ðÀK¬F±âE\”YÍ!ßUi¥êü0€«êJz4%TÁ€àÆ¢™È7àŠ =l*'4íá&]Kвk£§v-ð“Ï͹¼k€=hy+^s¥qu¬FÅ›mUóËîwQVëtìË‹{Yƒ% !å†W©(­üj….ü\X8Q±Â7ZûKtÃçzQë•>‹iY}¿ x‘x©(÷Î Á©Hëz»FòÌ"’1HŽÀnÛõ8Á.µÆ%ÆiEBŽJL‹APw‚Hç—Î œËãuà¯ò7ñiZ~†h@JdPH*°Oì\_y„'@R ìO`ñÐñM‡G{lµ+cÚd‹‡ÝnÿϹuIê…" vã–•¦ÓqÝûW»¯Ä± D®²—ÉërÉm°òaìÒiÐMÜ>÷œK0ÐŽÑw)²Áµ/%PwÙ¡J¶Ã`Řå8xÒ-ÓÑ@> /Parent 2 0 R /Type /Page >> endobj 21 0 obj << /Filter [/FlateDecode ] /Length 1795 >> stream xœ­XÛrÚH}ç+úªð Ûèb§¶“"6¼)—½B FY!I„ð¼¯û+þÇí‘FB7‚“]§ ©gtút÷龂 þKÿw6->àï—–ïZ_ázÞÊž –ó >znõ†d æ+ü4wZ‘$¼ØÃc{2Ü î>>µå§üêO.L9Ù³}±ÁfcûËèÿذ°ó'Ì?ä.Ê’ 5SÑ«S÷OÛw`þ‰uÁDn.7˜?¶Ýgß[!…J¼¶Ä!·xâ6AÈ„Q{<|êt;PǨÉDiFYzòœõíE´dÞ¤{›Á¼žeWq]½½ŸÀ®hDkÆ^zòìõmD¾ˆ^qÖÁ_Ï03 KÑ+‡ @J4™ÛÍ—ÅJB`"Á(‘•ºÁw{³õÄkNšà—¹æ¯¦nI¤–U[× p1B×yr¿¼©wÉ''ôVÝŒñÜ-6T%²eV0žVíioƒŒW˨¸÷½Ë—'²gÇ,бœ=fG,©èKx³ŽãmtÙë=c%ì"M•˜Z‰c‚ƒAÇáãÌ)1”Zzb†è‰·Dõ˜(ÅÎñª¨”V¿w“‡éèÝûùy¢5,ݨ½ø<Õ²Z Á6uH'´íCè>¯cÞ÷±_(æE‘Ð+^!0Üâ§™¿\鬊Ú$¤Y'T|¾Æú‡Cü³µQ¶ƒLÒ05·f”…WÖUqK—ð]V!ÃF¬â}ºÞD’­Œ|…²+8;œv|L»¥Å¡+RÌÔˆeÖ—ì°óâ4€rÜÑw,ÝÕߨùKTëLŽ#îBpŸuøÝüûÛûÂæ³•;¿1Ù-.\ÖKÃ8NPbŒó\Æ®Ã|,ÉÛ-ø5[Ââ`rfGdC#”ê™Wù>H E?GeÍEãh ƒvìþ0,Dôõ[joèÄ”ë;²0BcP8‰@Ùdí ŠÓB°mò–ÒæÎÊ_ikû¢³JD7R<Ñ‘Ÿª½,)4RÏO>7Þ«éz-±J)Â{ˆ®drsL‘%¸~âö:Øò3úëòiÄóp²„]ÄV;¯{Œz¦j:zDÕã†4Û>°ŸÞÏ¡ûŸSk ‹{gÁ±þtÚ¿åÊÎKÒ(Óò­®’"ÀÅx¢©§JÚ,—4o¬U½D4P蹋Ç=‰ØçMQËû-ZÊYâ…¶x–|¼™Þ#~áITU©Ù‹æÅ³h8šßÞÌf0¼›B?/•⺠+)p¤Ëª˜÷ãþ&÷ÓÉÝì†@&×<.3–XRTÖJžçÉÇ«;+ê ¯Æc}®½ÀB[²Øv½¨AÚT«:^=ˆ>^8Q+†”•$DJo k[ŒúLÏ󸞟ù抲ÇÂzýÒ%ØxŒßŠS‰¡×Yíÿ€Û pM†Ü˜wm<‡öæ p’Å£N7=ë40e6+DÐ({ºE$½®eÕkúÒ"W½.Œ|‡tqê‘ 8È êÃ’o9&^r˜œíøAMU¥.\QÌ·ùØ ¼ò ŽˆÜÏúµ¹Ühì‘™á¨ðËß ¿Ä~’$8oþ²t!iÿuÓ¾†–¤8¹q©^ÞÌáSëSë_…+Ñendstream endobj 1 0 obj << /Pages 2 0 R /Type /Catalog >> endobj 22 0 obj << /CreationDate (D:20161004164836+00'00') /Creator (groff version 1.22.3) /ModDate (D:20161004164836+00'00') /Producer (gropdf version 1.22.3) >> endobj 5 0 obj << /BaseFont /Times-Roman /Encoding 6 0 R /Subtype /Type1 /ToUnicode 7 0 R /Type /Font >> endobj 6 0 obj << /Differences [0 /asciicircum /asciitilde /Scaron /Zcaron /scaron /zcaron /Ydieresis /trademark /quotesingle /Euro /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quoteright /parenleft /parenright /asterisk /plus /comma /hyphen /period /slash /zero /one /two /three /four /five /six /seven /eight /nine /colon /semicolon /less /equal /greater /question /at /A /B /C /D /E /F /G /H /I /J /K /L /M /N /O /P /Q /R /S /T /U /V /W /X /Y /Z /bracketleft /backslash /bracketright /circumflex /underscore /quoteleft /a /b /c /d /e /f /g /h /i /j /k /l /m /n /o /p /q /r /s /t /u /v /w /x /y /z /braceleft /bar /braceright /tilde /.notdef /quotesinglbase /guillemotleft /guillemotright /bullet /florin /fraction /perthousand /dagger /daggerdbl /endash /emdash /.notdef /fi /fl /.notdef /.notdef /dotlessi /.notdef /grave /hungarumlaut /dotaccent /breve /caron /ring /ogonek /quotedblleft /quotedblright /oe /lslash /quotedblbase /OE /Lslash /.notdef /exclamdown /cent /sterling /currency /yen /brokenbar /section /dieresis /copyright /ordfeminine /guilsinglleft /logicalnot /minus /registered /macron /degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered /cedilla /onesuperior /ordmasculine /guilsinglright /onequarter /onehalf /threequarters /questiondown /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply /Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla /egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis /ellipsis ] /Type /Encoding >> endobj 7 0 obj << /Filter [/FlateDecode ] /Length 252 >> stream xœ]PKkÃ0 ¾ûWèØŠÛÊÁ029ìÁ²ÆŽ-CcÇ9äßOv»*õøôÉ’xÓ>·Î&àÑ«ëtÄÉÏQ!ô8XÇNh«Ò-*¯e`œÈÝ2%[g<«kàŸN).°{Ò¾Ç=ãïQc´n€ÝwÓQÜÍ!\pD—àÈ„†½Êð&G^h‡VnÓr ÎVñµ„ªÄ§ë0Êkœ‚T¥ÕGõ ‰`èô^]Y½YËz*'cü=Ÿ³–Tq7Wmî’µ¿¹Ô%}3•.Cü—çÉg[—UsŒ´g¹mY0¯f®ç>dVÑ?à+‚÷endstream endobj 8 0 obj << /BaseFont /Times-Bold /Encoding 9 0 R /Subtype /Type1 /ToUnicode 7 0 R /Type /Font >> endobj 9 0 obj << /Differences [0 /asciicircum /asciitilde /Scaron /Zcaron /scaron /zcaron /Ydieresis /trademark /quotesingle /Euro /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quoteright /parenleft /parenright /asterisk /plus /comma /hyphen /period /slash /zero /one /two /three /four /five /six /seven /eight /nine /colon /semicolon /less /equal /greater /question /at /A /B /C /D /E /F /G /H /I /J /K /L /M /N /O /P /Q /R /S /T /U /V /W /X /Y /Z /bracketleft /backslash /bracketright /circumflex /underscore /quoteleft /a /b /c /d /e /f /g /h /i /j /k /l /m /n /o /p /q /r /s /t /u /v /w /x /y /z /braceleft /bar /braceright /tilde /.notdef /quotesinglbase /guillemotleft /guillemotright /bullet /florin /fraction /perthousand /dagger /daggerdbl /endash /emdash /.notdef /fi /fl /.notdef /.notdef /dotlessi /.notdef /grave /hungarumlaut /dotaccent /breve /caron /ring /ogonek /quotedblleft /quotedblright /oe /lslash /quotedblbase /OE /Lslash /.notdef /exclamdown /cent /sterling /currency /yen /brokenbar /section /dieresis /copyright /ordfeminine /guilsinglleft /logicalnot /minus /registered /macron /degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered /cedilla /onesuperior /ordmasculine /guilsinglright /onequarter /onehalf /threequarters /questiondown /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply /Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla /egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis /ellipsis ] /Type /Encoding >> endobj 10 0 obj << /BaseFont /Times-Italic /Encoding 11 0 R /Subtype /Type1 /ToUnicode 7 0 R /Type /Font >> endobj 11 0 obj << /Differences [0 /asciicircum /asciitilde /Scaron /Zcaron /scaron /zcaron /Ydieresis /trademark /quotesingle /Euro /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quoteright /parenleft /parenright /asterisk /plus /comma /hyphen /period /slash /zero /one /two /three /four /five /six /seven /eight /nine /colon /semicolon /less /equal /greater /question /at /A /B /C /D /E /F /G /H /I /J /K /L /M /N /O /P /Q /R /S /T /U /V /W /X /Y /Z /bracketleft /backslash /bracketright /circumflex /underscore /quoteleft /a /b /c /d /e /f /g /h /i /j /k /l /m /n /o /p /q /r /s /t /u /v /w /x /y /z /braceleft /bar /braceright /tilde /.notdef /quotesinglbase /guillemotleft /guillemotright /bullet /florin /fraction /perthousand /dagger /daggerdbl /endash /emdash /.notdef /fi /fl /.notdef /.notdef /dotlessi /.notdef /grave /hungarumlaut /dotaccent /breve /caron /ring /ogonek /quotedblleft /quotedblright /oe /lslash /quotedblbase /OE /Lslash /.notdef /exclamdown /cent /sterling /currency /yen /brokenbar /section /dieresis /copyright /ordfeminine /guilsinglleft /logicalnot /minus /registered /macron /degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered /cedilla /onesuperior /ordmasculine /guilsinglright /onequarter /onehalf /threequarters /questiondown /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply /Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla /egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis /ellipsis ] /Type /Encoding >> endobj 12 0 obj << /BaseFont /Courier /Encoding 13 0 R /Subtype /Type1 /ToUnicode 7 0 R /Type /Font >> endobj 13 0 obj << /Differences [0 /asciicircum /asciitilde /Scaron /Zcaron /scaron /zcaron /Ydieresis /trademark /quotesingle /Euro /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quoteright /parenleft /parenright /asterisk /plus /comma /hyphen /period /slash /zero /one /two /three /four /five /six /seven /eight /nine /colon /semicolon /less /equal /greater /question /at /A /B /C /D /E /F /G /H /I /J /K /L /M /N /O /P /Q /R /S /T /U /V /W /X /Y /Z /bracketleft /backslash /bracketright /circumflex /underscore /quoteleft /a /b /c /d /e /f /g /h /i /j /k /l /m /n /o /p /q /r /s /t /u /v /w /x /y /z /braceleft /bar /braceright /tilde /.notdef /quotesinglbase /guillemotleft /guillemotright /bullet /florin /fraction /perthousand /dagger /daggerdbl /endash /emdash /.notdef /fi /fl /.notdef /.notdef /dotlessi /.notdef /grave /hungarumlaut /dotaccent /breve /caron /ring /ogonek /quotedblleft /quotedblright /oe /lslash /quotedblbase /OE /Lslash /.notdef /exclamdown /cent /sterling /currency /yen /brokenbar /section /dieresis /copyright /ordfeminine /guilsinglleft /logicalnot /minus /registered /macron /degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered /cedilla /onesuperior /ordmasculine /guilsinglright /onequarter /onehalf /threequarters /questiondown /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply /Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla /egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis /ellipsis ] /Type /Encoding >> endobj 2 0 obj << /Count 5 /Kids [3 0 R 14 0 R 16 0 R 18 0 R 20 0 R ] /MediaBox [0 0 595 842 ] /Resources << /Font << /F16 8 0 R /F18 10 0 R /F5 5 0 R /F6 12 0 R >> /ProcSet [/PDF /Text /ImageB /ImageC /ImageI ] >> /Rotate 0 /Type /Pages >> endobj xref 0 23 0000000000 65535 f 0000015291 00000 n 0000024780 00000 n 0000000015 00000 n 0000000127 00000 n 0000015504 00000 n 0000015611 00000 n 0000017741 00000 n 0000018067 00000 n 0000018173 00000 n 0000020303 00000 n 0000020413 00000 n 0000022544 00000 n 0000022649 00000 n 0000003717 00000 n 0000003831 00000 n 0000006484 00000 n 0000006598 00000 n 0000009955 00000 n 0000010069 00000 n 0000013306 00000 n 0000013420 00000 n 0000015341 00000 n trailer << /Info 22 0 R /Root 1 0 R /Size 23 >> startxref 25030 %%EOF picocom-2.2/picocom.c000066400000000000000000001111371277475702100146230ustar00rootroot00000000000000/* vi: set sw=4 ts=4: * * picocom.c * * simple dumb-terminal program. Helps you manually configure and test * stuff like modems, devices w. serial ports etc. * * by Nick Patavalis (npat@efault.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef USE_FLOCK #include #endif #ifdef LINENOISE #include #include #endif #define _GNU_SOURCE #include #include "fdio.h" #include "split.h" #include "term.h" #ifdef LINENOISE #include "linenoise-1.0/linenoise.h" #endif /**********************************************************************/ /* parity modes names */ const char *parity_str[] = { [P_NONE] = "none", [P_EVEN] = "even", [P_ODD] = "odd", [P_MARK] = "mark", [P_SPACE] = "space", }; /* flow control modes names */ const char *flow_str[] = { [FC_NONE] = "none", [FC_RTSCTS] = "RTS/CTS", [FC_XONXOFF] = "xon/xoff", [FC_OTHER] = "other", }; /**********************************************************************/ /* control-key to printable character (lowcase) */ #define KEYC(k) ((k) | 0x60) /* printable character to control-key */ #define CKEY(c) ((c) & 0x1f) #define KEY_EXIT CKEY('x') /* exit picocom */ #define KEY_QUIT CKEY('q') /* exit picocom without reseting port */ #define KEY_PULSE CKEY('p') /* pulse DTR */ #define KEY_TOGGLE CKEY('t') /* toggle DTR */ #define KEY_BAUD CKEY('b') /* set baudrate */ #define KEY_BAUD_UP CKEY('u') /* increase baudrate (up) */ #define KEY_BAUD_DN CKEY('d') /* decrase baudrate (down) */ #define KEY_FLOW CKEY('f') /* change flowcntrl mode */ #define KEY_PARITY CKEY('y') /* change parity mode */ #define KEY_BITS CKEY('i') /* change number of databits */ #define KEY_STOP CKEY('j') /* change number of stopbits */ #define KEY_LECHO CKEY('c') /* toggle local echo */ #define KEY_STATUS CKEY('v') /* show program options */ #define KEY_HELP CKEY('h') /* show help (same as [C-k]) */ #define KEY_KEYS CKEY('k') /* show available command keys */ #define KEY_SEND CKEY('s') /* send file */ #define KEY_RECEIVE CKEY('r') /* receive file */ #define KEY_BREAK CKEY('\\') /* break */ /**********************************************************************/ /* implemented caracter mappings */ #define M_CRLF (1 << 0) /* map CR --> LF */ #define M_CRCRLF (1 << 1) /* map CR --> CR + LF */ #define M_IGNCR (1 << 2) /* map CR --> */ #define M_LFCR (1 << 3) /* map LF --> CR */ #define M_LFCRLF (1 << 4) /* map LF --> CR + LF */ #define M_IGNLF (1 << 5) /* map LF --> */ #define M_DELBS (1 << 6) /* map DEL --> BS */ #define M_BSDEL (1 << 7) /* map BS --> DEL */ #define M_NFLAGS 8 /* default character mappings */ #define M_I_DFL 0 #define M_O_DFL 0 #define M_E_DFL (M_DELBS | M_CRCRLF) /* character mapping names */ struct map_names_s { char *name; int flag; } map_names[] = { { "crlf", M_CRLF }, { "crcrlf", M_CRCRLF }, { "igncr", M_IGNCR }, { "lfcr", M_LFCR }, { "lfcrlf", M_LFCRLF }, { "ignlf", M_IGNLF }, { "delbs", M_DELBS }, { "bsdel", M_BSDEL }, /* Sentinel */ { NULL, 0 } }; int parse_map (char *s) { char *m, *t; int f, flags, i; flags = 0; while ( (t = strtok(s, ", \t")) ) { for (i=0; (m = map_names[i].name); i++) { if ( ! strcmp(t, m) ) { f = map_names[i].flag; break; } } if ( m ) flags |= f; else { flags = -1; break; } s = NULL; } return flags; } void print_map (int flags) { int i; for (i = 0; i < M_NFLAGS; i++) if ( flags & (1 << i) ) printf("%s,", map_names[i].name); printf("\n"); } /**********************************************************************/ struct { char port[128]; int baud; enum flowcntrl_e flow; enum parity_e parity; int databits; int stopbits; int lecho; int noinit; int noreset; #if defined (UUCP_LOCK_DIR) || defined (USE_FLOCK) int nolock; #endif unsigned char escape; char send_cmd[128]; char receive_cmd[128]; int imap; int omap; int emap; } opts = { .port = "", .baud = 9600, .flow = FC_NONE, .parity = P_NONE, .databits = 8, .stopbits = 1, .lecho = 0, .noinit = 0, .noreset = 0, #if defined (UUCP_LOCK_DIR) || defined (USE_FLOCK) .nolock = 0, #endif .escape = CKEY('a'), .send_cmd = "sz -vv", .receive_cmd = "rz -vv -E", .imap = M_I_DFL, .omap = M_O_DFL, .emap = M_E_DFL }; int sig_exit = 0; #define STO STDOUT_FILENO #define STI STDIN_FILENO int tty_fd; #ifndef TTY_Q_SZ #define TTY_Q_SZ 256 #endif struct tty_q { int len; unsigned char buff[TTY_Q_SZ]; } tty_q; #define TTY_RD_SZ 128 int tty_write_sz; #define TTY_WRITE_SZ_DIV 10 #define TTY_WRITE_SZ_MIN 8 #define set_tty_write_sz(baud) \ do { \ tty_write_sz = (baud) / TTY_WRITE_SZ_DIV; \ if ( tty_write_sz < TTY_WRITE_SZ_MIN ) \ tty_write_sz = TTY_WRITE_SZ_MIN; \ } while (0) /**********************************************************************/ #ifdef UUCP_LOCK_DIR /* use HDB UUCP locks .. see * for details */ char lockname[_POSIX_PATH_MAX] = ""; int uucp_lockname(const char *dir, const char *file) { char *p, *cp; struct stat sb; if ( ! dir || *dir == '\0' || stat(dir, &sb) != 0 ) return -1; /* cut-off initial "/dev/" from file-name */ p = strchr(file + 1, '/'); p = p ? p + 1 : (char *)file; /* replace '/'s with '_'s in what remains (after making a copy) */ p = cp = strdup(p); do { if ( *p == '/' ) *p = '_'; } while(*p++); /* build lockname */ snprintf(lockname, sizeof(lockname), "%s/LCK..%s", dir, cp); /* destroy the copy */ free(cp); return 0; } int uucp_lock(void) { int r, fd, pid; char buf[16]; mode_t m; if ( lockname[0] == '\0' ) return 0; fd = open(lockname, O_RDONLY); if ( fd >= 0 ) { r = read(fd, buf, sizeof(buf)); close(fd); /* if r == 4, lock file is binary (old-style) */ pid = (r == 4) ? *(int *)buf : strtol(buf, NULL, 10); if ( pid > 0 && kill((pid_t)pid, 0) < 0 && errno == ESRCH ) { /* stale lock file */ printf("Removing stale lock: %s\n", lockname); sleep(1); unlink(lockname); } else { lockname[0] = '\0'; errno = EEXIST; return -1; } } /* lock it */ m = umask(022); fd = open(lockname, O_WRONLY|O_CREAT|O_EXCL, 0666); if ( fd < 0 ) { lockname[0] = '\0'; return -1; } umask(m); snprintf(buf, sizeof(buf), "%04d\n", getpid()); write(fd, buf, strlen(buf)); close(fd); return 0; } int uucp_unlock(void) { if ( lockname[0] ) unlink(lockname); return 0; } #endif /* of UUCP_LOCK_DIR */ /**********************************************************************/ void fatal (const char *format, ...) { char *s, buf[256]; va_list args; int len; term_reset(STO); term_reset(STI); va_start(args, format); len = vsnprintf(buf, sizeof(buf), format, args); buf[sizeof(buf) - 1] = '\0'; va_end(args); s = "\r\nFATAL: "; writen_ni(STO, s, strlen(s)); writen_ni(STO, buf, len); s = "\r\n"; writen_ni(STO, s, strlen(s)); /* wait a bit for output to drain */ sleep(1); #ifdef UUCP_LOCK_DIR uucp_unlock(); #endif exit(EXIT_FAILURE); } /**********************************************************************/ #ifndef LINENOISE char * read_filename (void) { char fname[_POSIX_PATH_MAX]; int r; fd_printf(STO, "\r\n*** file: "); r = fd_readline(STI, STO, fname, sizeof(fname)); fd_printf(STO, "\r\n"); if ( r < 0 ) return NULL; else return strdup(fname); } int read_baud (void) { char baudstr[9], *ep; int baud = -1, r; do { fd_printf(STO, "\r\n*** baud: "); r = fd_readline(STI, STO, baudstr, sizeof(baudstr)); fd_printf(STO, "\r\n"); if ( r < 0 ) break; baud = strtol(baudstr, &ep, 0); if ( ! ep || *ep != '\0' || ! term_baud_ok(baud) || baud == 0 ) { fd_printf(STO, "*** Invalid baudrate!"); baud = -1; } } while (baud < 0); return baud; } #else /* LINENOISE defined */ void file_completion_cb (const char *buf, linenoiseCompletions *lc) { DIR *dirp; struct dirent *dp; char *basec, *basen, *dirc, *dirn; int baselen, dirlen; char *fullpath; struct stat filestat; basec = strdup(buf); dirc = strdup(buf); dirn = dirname(dirc); dirlen = strlen(dirn); basen = basename(basec); baselen = strlen(basen); dirp = opendir(dirn); if (dirp) { while ((dp = readdir(dirp)) != NULL) { if (strncmp(basen, dp->d_name, baselen) == 0) { /* add 2 extra bytes for possible / in middle & at end */ fullpath = (char *) malloc(strlen(dp->d_name) + dirlen + 3); strcpy(fullpath, dirn); if (fullpath[dirlen-1] != '/') strcat(fullpath, "/"); strcat(fullpath, dp->d_name); if (stat(fullpath, &filestat) == 0) { if (S_ISDIR(filestat.st_mode)) { strcat(fullpath, "/"); } linenoiseAddCompletion(lc,fullpath); } free(fullpath); } } closedir(dirp); } free(basec); free(dirc); } static char *history_file_path = NULL; void init_history (void) { char *home_directory; home_directory = getenv("HOME"); if (home_directory) { history_file_path = malloc(strlen(home_directory) + 2 + strlen(HISTFILE)); strcpy(history_file_path, home_directory); if (home_directory[strlen(home_directory)-1] != '/') { strcat(history_file_path, "/"); } strcat(history_file_path, HISTFILE); linenoiseHistoryLoad(history_file_path); } } void cleanup_history (void) { if (history_file_path) free(history_file_path); } void add_history (char *fname) { linenoiseHistoryAdd(fname); if (history_file_path) linenoiseHistorySave(history_file_path); } char * read_filename (void) { char *fname; linenoiseSetCompletionCallback(file_completion_cb); fd_printf(STO, "\r\n"); fname = linenoise("*** file: "); fd_printf(STO, "\r"); linenoiseSetCompletionCallback(NULL); if (fname != NULL) add_history(fname); return fname; } int read_baud (void) { char *baudstr, *ep; int baud = -1; do { fd_printf(STO, "\r\n"); baudstr = linenoise("*** baud: "); fd_printf(STO, "\r"); if ( baudstr == NULL ) break; baud = strtol(baudstr, &ep, 0); if ( ! ep || *ep != '\0' || ! term_baud_ok(baud) || baud == 0 ) { fd_printf(STO, "*** Invalid baudrate!"); baud = -1; } free(baudstr); } while (baud < 0); if (baudstr != NULL) add_history(baudstr); return baud; } #endif /* of ifndef LINENOISE */ /**********************************************************************/ /* maximum number of chars that can replace a single characted due to mapping */ #define M_MAXMAP 4 int do_map (char *b, int map, char c) { int n; switch (c) { case '\x7f': /* DEL mapings */ if ( map & M_DELBS ) { b[0] = '\x08'; n = 1; } else { b[0] = c; n = 1; } break; case '\x08': /* BS mapings */ if ( map & M_BSDEL ) { b[0] = '\x7f'; n = 1; } else { b[0] = c; n = 1; } break; case '\x0d': /* CR mappings */ if ( map & M_CRLF ) { b[0] = '\x0a'; n = 1; } else if ( map & M_CRCRLF ) { b[0] = '\x0d'; b[1] = '\x0a'; n = 2; } else if ( map & M_IGNCR ) { n = 0; } else { b[0] = c; n = 1; } break; case '\x0a': /* LF mappings */ if ( map & M_LFCR ) { b[0] = '\x0d'; n = 1; } else if ( map & M_LFCRLF ) { b[0] = '\x0d'; b[1] = '\x0a'; n = 2; } else if ( map & M_IGNLF ) { n = 0; } else { b[0] = c; n = 1; } break; default: b[0] = c; n = 1; break; } return n; } void map_and_write (int fd, int map, char c) { char b[M_MAXMAP]; int n; n = do_map(b, map, c); if ( n ) if ( writen_ni(fd, b, n) < n ) fatal("write to stdout failed: %s", strerror(errno)); } /**********************************************************************/ int baud_up (int baud) { return term_baud_up(baud); } int baud_down (int baud) { int nb; nb = term_baud_down(baud); if (nb == 0) nb = baud; return nb; } int flow_next (int flow) { switch(flow) { case FC_NONE: flow = FC_RTSCTS; break; case FC_RTSCTS: flow = FC_XONXOFF; break; case FC_XONXOFF: flow = FC_NONE; break; default: flow = FC_NONE; break; } return flow; } int parity_next (int parity) { switch(parity) { case P_NONE: parity = P_EVEN; break; case P_EVEN: parity = P_ODD; break; case P_ODD: parity = P_NONE; break; default: parity = P_NONE; break; } return parity; } int bits_next (int bits) { bits++; if (bits > 8) bits = 5; return bits; } int stopbits_next (int bits) { bits++; if (bits > 2) bits = 1; return bits; } void show_status (int dtr_up) { int baud, bits, stopbits, mctl; enum flowcntrl_e flow; enum parity_e parity; term_refresh(tty_fd); baud = term_get_baudrate(tty_fd, NULL); flow = term_get_flowcntrl(tty_fd); parity = term_get_parity(tty_fd); bits = term_get_databits(tty_fd); stopbits = term_get_stopbits(tty_fd); fd_printf(STO, "\r\n"); if ( baud != opts.baud ) { fd_printf(STO, "*** baud: %d (%d)\r\n", opts.baud, baud); } else { fd_printf(STO, "*** baud: %d\r\n", opts.baud); } if ( flow != opts.flow ) { fd_printf(STO, "*** flow: %s (%s)\r\n", flow_str[opts.flow], flow_str[flow]); } else { fd_printf(STO, "*** flow: %s\r\n", flow_str[opts.flow]); } if ( parity != opts.parity ) { fd_printf(STO, "*** parity: %s (%s)\r\n", parity_str[opts.parity], parity_str[parity]); } else { fd_printf(STO, "*** parity: %s\r\n", parity_str[opts.parity]); } if ( bits != opts.databits ) { fd_printf(STO, "*** databits: %d (%d)\r\n", opts.databits, bits); } else { fd_printf(STO, "*** databits: %d\r\n", opts.databits); } if ( stopbits != opts.stopbits ) { fd_printf(STO, "*** stopbits: %d (%d)\r\n", opts.stopbits, stopbits); } else { fd_printf(STO, "*** stopbits: %d\r\n", opts.stopbits); } mctl = term_get_mctl(tty_fd); if (mctl >= 0 && mctl != MCTL_UNAVAIL) { if ( ((mctl & MCTL_DTR) ? 1 : 0) == dtr_up ) fd_printf(STO, "*** dtr: %s\r\n", dtr_up ? "up" : "down"); else fd_printf(STO, "*** dtr: %s (%s)\r\n", dtr_up ? "up" : "down", (mctl & MCTL_DTR) ? "up" : "down"); fd_printf(STO, "*** mctl: "); fd_printf(STO, "DTR:%c DSR:%c DCD:%c RTS:%c CTS:%c RI:%c\r\n", (mctl & MCTL_DTR) ? '1' : '0', (mctl & MCTL_DSR) ? '1' : '0', (mctl & MCTL_DCD) ? '1' : '0', (mctl & MCTL_RTS) ? '1' : '0', (mctl & MCTL_CTS) ? '1' : '0', (mctl & MCTL_RI) ? '1' : '0'); } else { fd_printf(STO, "*** dtr: %s\r\n", dtr_up ? "up" : "down"); } } void show_keys() { #ifndef NO_HELP fd_printf(STO, "\r\n"); fd_printf(STO, "*** Picocom commands (all prefixed by [C-%c])\r\n", KEYC(opts.escape)); fd_printf(STO, "\r\n"); fd_printf(STO, "*** [C-%c] : Exit picocom\r\n", KEYC(KEY_EXIT)); fd_printf(STO, "*** [C-%c] : Exit without reseting serial port\r\n", KEYC(KEY_QUIT)); fd_printf(STO, "*** [C-%c] : Set baudrate\r\n", KEYC(KEY_BAUD)); fd_printf(STO, "*** [C-%c] : Increase baudrate (baud-up)\r\n", KEYC(KEY_BAUD_UP)); fd_printf(STO, "*** [C-%c] : Decrease baudrate (baud-down)\r\n", KEYC(KEY_BAUD_DN));; fd_printf(STO, "*** [C-%c] : Change number of databits\r\n", KEYC(KEY_BITS)); fd_printf(STO, "*** [C-%c] : Change number of stopbits\r\n", KEYC(KEY_STOP)); fd_printf(STO, "*** [C-%c] : Change flow-control mode\r\n", KEYC(KEY_FLOW)); fd_printf(STO, "*** [C-%c] : Change parity mode\r\n", KEYC(KEY_PARITY)); fd_printf(STO, "*** [C-%c] : Pulse DTR\r\n", KEYC(KEY_PULSE)); fd_printf(STO, "*** [C-%c] : Toggle DTR\r\n", KEYC(KEY_TOGGLE)); fd_printf(STO, "*** [C-%c] : Send break\r\n", KEYC(KEY_BREAK)); fd_printf(STO, "*** [C-%c] : Toggle local echo\r\n", KEYC(KEY_LECHO)); fd_printf(STO, "*** [C-%c] : Send file\r\n", KEYC(KEY_SEND)); fd_printf(STO, "*** [C-%c] : Receive file\r\n", KEYC(KEY_RECEIVE)); fd_printf(STO, "*** [C-%c] : Show port settings\r\n", KEYC(KEY_STATUS)); fd_printf(STO, "*** [C-%c] : Show this message\r\n", KEYC(KEY_HELP)); fd_printf(STO, "\r\n"); #else /* defined NO_HELP */ fd_printf(STO, "*** Help is disabled.\r\n"); #endif /* of NO_HELP */ } /**********************************************************************/ #define RUNCMD_ARGS_MAX 32 #define RUNCMD_EXEC_FAIL 126 void establish_child_signal_handlers (void) { struct sigaction dfl_action; /* Set up the structure to specify the default action. */ dfl_action.sa_handler = SIG_DFL; sigemptyset (&dfl_action.sa_mask); dfl_action.sa_flags = 0; sigaction (SIGINT, &dfl_action, NULL); sigaction (SIGTERM, &dfl_action, NULL); } int run_cmd(int fd, const char *cmd, const char *args_extra) { pid_t pid; sigset_t sigm, sigm_old; /* block signals, let child establish its own handlers */ sigemptyset(&sigm); sigaddset(&sigm, SIGTERM); sigprocmask(SIG_BLOCK, &sigm, &sigm_old); pid = fork(); if ( pid < 0 ) { sigprocmask(SIG_SETMASK, &sigm_old, NULL); fd_printf(STO, "*** cannot fork: %s ***\r\n", strerror(errno)); return -1; } else if ( pid ) { /* father: picocom */ int status, r; /* reset the mask */ sigprocmask(SIG_SETMASK, &sigm_old, NULL); /* wait for child to finish */ do { r = waitpid(pid, &status, 0); } while ( r < 0 && errno == EINTR ); /* reset terminal (back to raw mode) */ term_apply(STI, 0); /* check and report child return status */ if ( WIFEXITED(status) ) { fd_printf(STO, "\r\n*** exit status: %d ***\r\n", WEXITSTATUS(status)); return WEXITSTATUS(status); } else if ( WIFSIGNALED(status) ) { fd_printf(STO, "\r\n*** killed by signal: %d ***\r\n", WTERMSIG(status)); return -1; } else { fd_printf(STO, "\r\n*** abnormal termination: 0x%x ***\r\n", r); return -1; } } else { /* child: external program */ long fl; int argc; char *argv[RUNCMD_ARGS_MAX + 1]; int r; /* unmanage terminal, and reset it to canonical mode */ term_remove(STI); /* unmanage serial port fd, without reset */ term_erase(fd); /* set serial port fd to blocking mode */ fl = fcntl(fd, F_GETFL); fl &= ~O_NONBLOCK; fcntl(fd, F_SETFL, fl); /* connect stdin and stdout to serial port */ close(STI); close(STO); dup2(fd, STI); dup2(fd, STO); /* build command arguments vector */ argc = 0; r = split_quoted(cmd, &argc, argv, RUNCMD_ARGS_MAX); if ( r < 0 ) { fd_printf(STDERR_FILENO, "Cannot parse command\n"); exit(RUNCMD_EXEC_FAIL); } r = split_quoted(args_extra, &argc, argv, RUNCMD_ARGS_MAX); if ( r < 0 ) { fd_printf(STDERR_FILENO, "Cannot parse extra args\n"); exit(RUNCMD_EXEC_FAIL); } if ( argc < 1 ) { fd_printf(STDERR_FILENO, "No command given\n"); exit(RUNCMD_EXEC_FAIL); } argv[argc] = NULL; /* run extenral command */ fd_printf(STDERR_FILENO, "$ %s %s\n", cmd, args_extra); establish_child_signal_handlers(); sigprocmask(SIG_SETMASK, &sigm_old, NULL); execvp(argv[0], argv); fd_printf(STDERR_FILENO, "exec: %s\n", strerror(errno)); exit(RUNCMD_EXEC_FAIL); } } /**********************************************************************/ /* Process command key. Returns non-zero if command results in picocom exit, zero otherwise. */ int do_command (unsigned char c) { static int dtr_up = 0; int newbaud, newflow, newparity, newbits, newstopbits; const char *xfr_cmd; char *fname; int r; switch (c) { case KEY_EXIT: return 1; case KEY_QUIT: term_set_hupcl(tty_fd, 0); term_flush(tty_fd); term_apply(tty_fd, 1); term_erase(tty_fd); return 1; case KEY_STATUS: show_status(dtr_up); break; case KEY_HELP: case KEY_KEYS: show_keys(); break; case KEY_PULSE: fd_printf(STO, "\r\n*** pulse DTR ***\r\n"); if ( term_pulse_dtr(tty_fd) < 0 ) fd_printf(STO, "*** FAILED\r\n"); break; case KEY_TOGGLE: if ( dtr_up ) r = term_lower_dtr(tty_fd); else r = term_raise_dtr(tty_fd); if ( r >= 0 ) dtr_up = ! dtr_up; fd_printf(STO, "\r\n*** DTR: %s ***\r\n", dtr_up ? "up" : "down"); break; case KEY_BAUD: case KEY_BAUD_UP: case KEY_BAUD_DN: if ( c== KEY_BAUD) { newbaud = read_baud(); if ( newbaud < 0 ) { fd_printf(STO, "*** cannot read baudrate ***\r\n"); break; } opts.baud = newbaud; } else if (c == KEY_BAUD_UP) { opts.baud = baud_up(opts.baud); } else { opts.baud = baud_down(opts.baud); } term_set_baudrate(tty_fd, opts.baud); tty_q.len = 0; term_flush(tty_fd); term_apply(tty_fd, 1); newbaud = term_get_baudrate(tty_fd, NULL); if ( opts.baud != newbaud ) { fd_printf(STO, "\r\n*** baud: %d (%d) ***\r\n", opts.baud, newbaud); } else { fd_printf(STO, "\r\n*** baud: %d ***\r\n", opts.baud); } set_tty_write_sz(newbaud); break; case KEY_FLOW: opts.flow = flow_next(opts.flow); term_set_flowcntrl(tty_fd, opts.flow); tty_q.len = 0; term_flush(tty_fd); term_apply(tty_fd, 1); newflow = term_get_flowcntrl(tty_fd); if ( opts.flow != newflow ) { fd_printf(STO, "\r\n*** flow: %s (%s) ***\r\n", flow_str[opts.flow], flow_str[newflow]); } else { fd_printf(STO, "\r\n*** flow: %s ***\r\n", flow_str[opts.flow]); } break; case KEY_PARITY: opts.parity = parity_next(opts.parity); term_set_parity(tty_fd, opts.parity); tty_q.len = 0; term_flush(tty_fd); term_apply(tty_fd, 1); newparity = term_get_parity(tty_fd); if (opts.parity != newparity ) { fd_printf(STO, "\r\n*** parity: %s (%s) ***\r\n", parity_str[opts.parity], parity_str[newparity]); } else { fd_printf(STO, "\r\n*** parity: %s ***\r\n", parity_str[opts.parity]); } break; case KEY_BITS: opts.databits = bits_next(opts.databits); term_set_databits(tty_fd, opts.databits); tty_q.len = 0; term_flush(tty_fd); term_apply(tty_fd, 1); newbits = term_get_databits(tty_fd); if (opts.databits != newbits ) { fd_printf(STO, "\r\n*** databits: %d (%d) ***\r\n", opts.databits, newbits); } else { fd_printf(STO, "\r\n*** databits: %d ***\r\n", opts.databits); } break; case KEY_STOP: opts.stopbits = stopbits_next(opts.stopbits); term_set_stopbits(tty_fd, opts.stopbits); tty_q.len = 0; term_flush(tty_fd); term_apply(tty_fd, 1); newstopbits = term_get_stopbits(tty_fd); if (opts.stopbits != newstopbits ) { fd_printf(STO, "\r\n*** stopbits: %d (%d) ***\r\n", opts.stopbits, newstopbits); } else { fd_printf(STO, "\r\n*** stopbits: %d ***\r\n", opts.stopbits); } break; case KEY_LECHO: opts.lecho = ! opts.lecho; fd_printf(STO, "\r\n*** local echo: %s ***\r\n", opts.lecho ? "yes" : "no"); break; case KEY_SEND: case KEY_RECEIVE: xfr_cmd = (c == KEY_SEND) ? opts.send_cmd : opts.receive_cmd; if ( xfr_cmd[0] == '\0' ) { fd_printf(STO, "\r\n*** command disabled ***\r\n"); break; } fname = read_filename(); if (fname == NULL) { fd_printf(STO, "*** cannot read filename ***\r\n"); break; } run_cmd(tty_fd, xfr_cmd, fname); free(fname); break; case KEY_BREAK: term_break(tty_fd); fd_printf(STO, "\r\n*** break sent ***\r\n"); break; default: break; } return 0; } /**********************************************************************/ void loop(void) { enum { ST_COMMAND, ST_TRANSPARENT } state; fd_set rdset, wrset; int r, n; unsigned char c; tty_q.len = 0; state = ST_TRANSPARENT; while ( ! sig_exit ) { FD_ZERO(&rdset); FD_ZERO(&wrset); FD_SET(STI, &rdset); FD_SET(tty_fd, &rdset); if ( tty_q.len ) FD_SET(tty_fd, &wrset); r = select(tty_fd + 1, &rdset, &wrset, NULL, NULL); if ( r < 0 ) { if ( errno == EINTR ) continue; else fatal("select failed: %d : %s", errno, strerror(errno)); } if ( FD_ISSET(STI, &rdset) ) { /* read from terminal */ do { n = read(STI, &c, 1); } while (n < 0 && errno == EINTR); if (n == 0) { fatal("stdin closed"); } else if (n < 0) { /* is this really necessary? better safe than sory! */ if ( errno != EAGAIN && errno != EWOULDBLOCK ) fatal("read from stdin failed: %s", strerror(errno)); else goto skip_proc_STI; } switch (state) { case ST_COMMAND: if ( c == opts.escape ) { /* pass the escape character down */ if (tty_q.len + M_MAXMAP <= TTY_Q_SZ) { n = do_map((char *)tty_q.buff + tty_q.len, opts.omap, c); tty_q.len += n; if ( opts.lecho ) map_and_write(STO, opts.emap, c); } else { fd_printf(STO, "\x07"); } } else { /* process command key */ if ( do_command(c) ) /* picocom exit */ return; } state = ST_TRANSPARENT; break; case ST_TRANSPARENT: if ( c == opts.escape ) { state = ST_COMMAND; } else { if (tty_q.len + M_MAXMAP <= TTY_Q_SZ) { n = do_map((char *)tty_q.buff + tty_q.len, opts.omap, c); tty_q.len += n; if ( opts.lecho ) map_and_write(STO, opts.emap, c); } else fd_printf(STO, "\x07"); } break; default: assert(0); break; } } skip_proc_STI: if ( FD_ISSET(tty_fd, &rdset) ) { char buff_rd[TTY_RD_SZ]; char buff_map[TTY_RD_SZ * M_MAXMAP]; /* read from port */ do { n = read(tty_fd, &buff_rd, sizeof(buff_rd)); } while (n < 0 && errno == EINTR); if (n == 0) { fatal("term closed"); } else if ( n < 0 ) { if ( errno != EAGAIN && errno != EWOULDBLOCK ) fatal("read from term failed: %s", strerror(errno)); } else { int i; char *bmp = &buff_map[0]; for (i = 0; i < n; i++) { bmp += do_map(bmp, opts.imap, buff_rd[i]); } n = bmp - buff_map; if ( writen_ni(STO, buff_map, n) < n ) fatal("write to stdout failed: %s", strerror(errno)); } } if ( FD_ISSET(tty_fd, &wrset) ) { /* write to port */ int sz; sz = (tty_q.len < tty_write_sz) ? tty_q.len : tty_write_sz; do { n = write(tty_fd, tty_q.buff, sz); } while ( n < 0 && errno == EINTR ); if ( n <= 0 ) fatal("write to term failed: %s", strerror(errno)); memmove(tty_q.buff, tty_q.buff + n, tty_q.len - n); tty_q.len -= n; } } } /**********************************************************************/ void deadly_handler(int signum) { if ( ! sig_exit ) { sig_exit = 1; kill(0, SIGTERM); } } void establish_signal_handlers (void) { struct sigaction exit_action, ign_action; /* Set up the structure to specify the exit action. */ exit_action.sa_handler = deadly_handler; sigemptyset (&exit_action.sa_mask); exit_action.sa_flags = 0; /* Set up the structure to specify the ignore action. */ ign_action.sa_handler = SIG_IGN; sigemptyset (&ign_action.sa_mask); ign_action.sa_flags = 0; sigaction (SIGTERM, &exit_action, NULL); sigaction (SIGINT, &ign_action, NULL); sigaction (SIGHUP, &ign_action, NULL); sigaction (SIGQUIT, &ign_action, NULL); sigaction (SIGALRM, &ign_action, NULL); sigaction (SIGUSR1, &ign_action, NULL); sigaction (SIGUSR2, &ign_action, NULL); sigaction (SIGPIPE, &ign_action, NULL); } /**********************************************************************/ void show_usage(char *name) { #ifndef NO_HELP char *s; s = strrchr(name, '/'); s = s ? s+1 : name; printf("picocom v%s\n", VERSION_STR); printf("\nCompiled-in options:\n"); printf(" TTY_Q_SZ is %d\n", TTY_Q_SZ); #ifdef USE_HIGH_BAUD printf(" HIGH_BAUD is enabled\n"); #endif #ifdef USE_FLOCK printf(" USE_FLOCK is enabled\n"); #endif #ifdef UUCP_LOCK_DIR printf(" UUCP_LOCK_DIR is: %s\n", UUCP_LOCK_DIR); #endif #ifdef LINENOISE printf(" LINENOISE is enabled\n"); printf(" HISTFILE is: %s\n", HISTFILE); #endif #ifdef USE_CUSTOM_BAUD printf(" USE_CUSTOM_BAUD is enabled\n"); #endif printf("\nUsage is: %s [options] \n", s); printf("Options are:\n"); printf(" --aud \n"); printf(" --low x (=soft,xon/xoff) | h (=hard) | n (=none)\n"); printf(" --parit o (=odd) | e (=even) | n (=none)\n"); printf(" --atabits 5 | 6 | 7 | 8\n"); printf(" --sto

bits 1 | 2\n"); printf(" --scape \n"); printf(" --eho\n"); printf(" --nonit\n"); printf(" --noeset\n"); printf(" --noock\n"); printf(" --end-cmd \n"); printf(" --receie-cmd \n"); printf(" --imap (input mappings)\n"); printf(" --omap (output mappings)\n"); printf(" --emap (local-echo mappings)\n"); printf(" --elp\n"); printf(" is a comma-separated list of one or more of:\n"); printf(" crlf : map CR --> LF\n"); printf(" crcrlf : map CR --> CR + LF\n"); printf(" igncr : ignore CR\n"); printf(" lfcr : map LF --> CR\n"); printf(" lfcrlf : map LF --> CR + LF\n"); printf(" ignlf : ignore LF\n"); printf(" bsdel : map BS --> DEL\n"); printf(" delbs : map DEL --> BS\n"); printf(" indicates the equivalent short option.\n"); printf("Short options are prefixed by \"-\" instead of by \"--\".\n"); #else /* defined NO_HELP */ printf("Help disabled.\n"); #endif /* of NO_HELP */ } /**********************************************************************/ void parse_args(int argc, char *argv[]) { int r; static struct option longOptions[] = { {"receive-cmd", required_argument, 0, 'v'}, {"send-cmd", required_argument, 0, 's'}, {"imap", required_argument, 0, 'I' }, {"omap", required_argument, 0, 'O' }, {"emap", required_argument, 0, 'E' }, {"escape", required_argument, 0, 'e'}, {"echo", no_argument, 0, 'c'}, {"noinit", no_argument, 0, 'i'}, {"noreset", no_argument, 0, 'r'}, {"nolock", no_argument, 0, 'l'}, {"flow", required_argument, 0, 'f'}, {"baud", required_argument, 0, 'b'}, {"parity", required_argument, 0, 'y'}, {"databits", required_argument, 0, 'd'}, {"stopbits", required_argument, 0, 'p'}, {"help", no_argument, 0, 'h'}, {0, 0, 0, 0} }; r = 0; while (1) { int optionIndex = 0; int c; int map; /* no default error messages printed. */ opterr = 0; c = getopt_long(argc, argv, "hirlcv:s:r:e:f:b:y:d:p:", longOptions, &optionIndex); if (c < 0) break; switch (c) { case 's': strncpy(opts.send_cmd, optarg, sizeof(opts.send_cmd)); opts.send_cmd[sizeof(opts.send_cmd) - 1] = '\0'; break; case 'v': strncpy(opts.receive_cmd, optarg, sizeof(opts.receive_cmd)); opts.receive_cmd[sizeof(opts.receive_cmd) - 1] = '\0'; break; case 'I': map = parse_map(optarg); if (map >= 0) opts.imap = map; else { fprintf(stderr, "Invalid --imap\n"); r = -1; } break; case 'O': map = parse_map(optarg); if (map >= 0) opts.omap = map; else { fprintf(stderr, "Invalid --omap\n"); r = -1; } break; case 'E': map = parse_map(optarg); if (map >= 0) opts.emap = map; else { fprintf(stderr, "Invalid --emap\n"); r = -1; } break; case 'c': opts.lecho = 1; break; case 'i': opts.noinit = 1; break; case 'r': opts.noreset = 1; break; case 'l': #if defined (UUCP_LOCK_DIR) || defined (USE_FLOCK) opts.nolock = 1; #endif break; case 'e': opts.escape = CKEY(optarg[0]); break; case 'f': switch (optarg[0]) { case 'X': case 'x': opts.flow = FC_XONXOFF; break; case 'H': case 'h': opts.flow = FC_RTSCTS; break; case 'N': case 'n': opts.flow = FC_NONE; break; default: fprintf(stderr, "Invalid --flow: %c\n", optarg[0]); r = -1; break; } break; case 'b': opts.baud = atoi(optarg); if ( opts.baud == 0 || ! term_baud_ok(opts.baud) ) { fprintf(stderr, "Invalid --baud: %d\n", opts.baud); r = -1; } break; case 'y': switch (optarg[0]) { case 'e': opts.parity = P_EVEN; break; case 'o': opts.parity = P_ODD; break; case 'n': opts.parity = P_NONE; break; default: fprintf(stderr, "Invalid --parity: %c\n", optarg[0]); r = -1; break; } break; case 'd': switch (optarg[0]) { case '5': opts.databits = 5; break; case '6': opts.databits = 6; break; case '7': opts.databits = 7; break; case '8': opts.databits = 8; break; default: fprintf(stderr, "Invalid --databits: %c\n", optarg[0]); r = -1; break; } break; case 'p': switch (optarg[0]) { case '1': opts.stopbits = 1; break; case '2': opts.stopbits = 2; break; default: fprintf(stderr, "Invalid --stopbits: %c\n", optarg[0]); r = -1; break; } break; case 'h': show_usage(argv[0]); exit(EXIT_SUCCESS); case '?': default: fprintf(stderr, "Unrecognized option(s)\n"); r = -1; break; } if ( r < 0 ) { fprintf(stderr, "Run with '--help'.\n"); exit(EXIT_FAILURE); } } /* while */ if ( (argc - optind) < 1) { fprintf(stderr, "No port given\n"); fprintf(stderr, "Run with '--help'.\n"); exit(EXIT_FAILURE); } strncpy(opts.port, argv[optind], sizeof(opts.port) - 1); opts.port[sizeof(opts.port) - 1] = '\0'; #ifndef NO_HELP printf("picocom v%s\n", VERSION_STR); printf("\n"); printf("port is : %s\n", opts.port); printf("flowcontrol : %s\n", flow_str[opts.flow]); printf("baudrate is : %d\n", opts.baud); printf("parity is : %s\n", parity_str[opts.parity]); printf("databits are : %d\n", opts.databits); printf("stopbits are : %d\n", opts.stopbits); printf("escape is : C-%c\n", KEYC(opts.escape)); printf("local echo is : %s\n", opts.lecho ? "yes" : "no"); printf("noinit is : %s\n", opts.noinit ? "yes" : "no"); printf("noreset is : %s\n", opts.noreset ? "yes" : "no"); #if defined (UUCP_LOCK_DIR) || defined (USE_FLOCK) printf("nolock is : %s\n", opts.nolock ? "yes" : "no"); #endif printf("send_cmd is : %s\n", (opts.send_cmd[0] == '\0') ? "disabled" : opts.send_cmd); printf("receive_cmd is : %s\n", (opts.receive_cmd[0] == '\0') ? "disabled" : opts.receive_cmd); printf("imap is : "); print_map(opts.imap); printf("omap is : "); print_map(opts.omap); printf("emap is : "); print_map(opts.emap); printf("\n"); #endif /* of NO_HELP */ } /**********************************************************************/ int main(int argc, char *argv[]) { int r; parse_args(argc, argv); establish_signal_handlers(); r = term_lib_init(); if ( r < 0 ) fatal("term_init failed: %s", term_strerror(term_errno, errno)); #ifdef UUCP_LOCK_DIR if ( ! opts.nolock ) uucp_lockname(UUCP_LOCK_DIR, opts.port); if ( uucp_lock() < 0 ) fatal("cannot lock %s: %s", opts.port, strerror(errno)); #endif tty_fd = open(opts.port, O_RDWR | O_NONBLOCK | O_NOCTTY); if (tty_fd < 0) fatal("cannot open %s: %s", opts.port, strerror(errno)); #ifdef USE_FLOCK if ( ! opts.nolock ) { r = flock(tty_fd, LOCK_EX | LOCK_NB); if ( r < 0 ) fatal("cannot lock %s: %s", opts.port, strerror(errno)); } #endif if ( opts.noinit ) { r = term_add(tty_fd); } else { r = term_set(tty_fd, 1, /* raw mode. */ opts.baud, /* baud rate. */ opts.parity, /* parity. */ opts.databits, /* data bits. */ opts.stopbits, /* stop bits. */ opts.flow, /* flow control. */ 1, /* local or modem */ !opts.noreset); /* hup-on-close. */ } if ( r < 0 ) fatal("failed to add device %s: %s", opts.port, term_strerror(term_errno, errno)); r = term_apply(tty_fd, 0); if ( r < 0 ) fatal("failed to config device %s: %s", opts.port, term_strerror(term_errno, errno)); set_tty_write_sz(term_get_baudrate(tty_fd, NULL)); r = term_add(STI); if ( r < 0 ) fatal("failed to add I/O device: %s", term_strerror(term_errno, errno)); term_set_raw(STI); r = term_apply(STI, 0); if ( r < 0 ) fatal("failed to set I/O device to raw mode: %s", term_strerror(term_errno, errno)); #ifdef LINENOISE init_history(); #endif #ifndef NO_HELP fd_printf(STO, "Type [C-%c] [C-%c] to see available commands\r\n\r\n", KEYC(opts.escape), KEYC(KEY_HELP)); #endif fd_printf(STO, "Terminal ready\r\n"); loop(); #ifdef LINENOISE cleanup_history(); #endif fd_printf(STO, "\r\n"); if ( opts.noreset ) { fd_printf(STO, "Skipping tty reset...\r\n"); term_erase(tty_fd); } if ( sig_exit ) fd_printf(STO, "Picocom was killed\r\n"); else fd_printf(STO, "Thanks for using picocom\r\n"); /* wait a bit for output to drain */ sleep(1); #ifdef UUCP_LOCK_DIR uucp_unlock(); #endif return EXIT_SUCCESS; } /**********************************************************************/ /* * Local Variables: * mode:c * tab-width: 4 * c-basic-offset: 4 * End: */ picocom-2.2/split.c000066400000000000000000000152411277475702100143240ustar00rootroot00000000000000/* vi: set sw=4 ts=4: * * split.c * * Function that splits a string intro arguments with quoting. * * by Nick Patavalis (npat@efault.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA */ #include #include #include #include "split.h" /* Lexer error end-codes */ enum err_codes { ERR_OK = 0, /* no error, string lexed ok */ ERR_BS_AT_EOS, /* backslash at the end of string */ ERR_SQ_OPEN_AT_EOS, /* single-quote left open */ ERR_DQ_OPEN_AT_EOS /* double-quote left open */ }; /* Lexer states */ enum states { ST_DELIM, ST_QUOTE, ST_ARG, ST_END }; /* Special characters */ #define BS '\\' #define SQ '\'' #define DQ '\"' #define NL '\n' #define EOS '\0' #define is_delim(c) \ ( (c) == ' ' || (c) == '\t' || (c) == '\n' ) #define is_dq_escapable(c) \ ( (c) == '\\' || (c) == '\"' || (c) == '`' || (c) == '$' ) /* Short-hands used in split_quoted() */ #define push() \ do { \ char *arg; \ if ( *argc < argv_sz ) { \ *ap = '\0'; \ arg = strdup(arg_buff); \ /* !! out of mem !! */ \ if ( ! arg ) return -1; \ argv[*argc] = arg; \ (*argc)++; \ } else { \ flags |= SPLIT_DROP; \ } \ ap = &arg_buff[0]; \ } while(0) #define save() \ do { \ if (ap != ae) { \ *ap++ = *c; \ } else { \ flags |= SPLIT_TRUNC; \ } \ } while (0) int split_quoted (const char *s, int *argc, char *argv[], int argv_sz) { char arg_buff[MAX_ARG_LEN]; /* current argument buffer */ char *ap, *ae; /* arg_buff current ptr & end-guard */ const char *c; /* current input charcter ptr */ char qc; /* current quote character */ enum states state; /* current state */ enum err_codes err; /* error end-code */ int flags; /* warning flags */ ap = &arg_buff[0]; ae = &arg_buff[MAX_ARG_LEN - 1]; c = &s[0]; state = ST_DELIM; err = ERR_OK; flags = 0; qc = SQ; /* silence compiler waring */ while ( state != ST_END ) { switch (state) { case ST_DELIM: while ( is_delim(*c) ) c++; if ( *c == SQ || *c == DQ ) { qc = *c; c++; state = ST_QUOTE; break; } if ( *c == EOS ) { state = ST_END; break; } if ( *c == BS ) { c++; if ( *c == NL ) { c++; break; } if ( *c == EOS ) { state = ST_END; err = ERR_BS_AT_EOS; break; } } /* All other cases incl. character after BS */ save(); c++; state = ST_ARG; break; case ST_QUOTE: while ( *c != qc && ( *c != BS || qc == SQ ) && *c != EOS ) { save(); c++; } if ( *c == qc ) { c++; state = ST_ARG; break; } if ( *c == BS ) { assert (qc == DQ); c++; if ( *c == NL) { c++; break; } if (*c == EOS) { state = ST_END; err = ERR_BS_AT_EOS; break; } if ( ! is_dq_escapable(*c) ) { c--; save(); c++; } save(); c++; break; } if ( *c == EOS ) { state = ST_END; err = ERR_SQ_OPEN_AT_EOS; break; } assert(0); case ST_ARG: if ( *c == SQ || *c == DQ ) { qc = *c; c++; state = ST_QUOTE; break; } if ( is_delim(*c) || *c == EOS ) { push(); state = (*c == EOS) ? ST_END : ST_DELIM; c++; break; } if ( *c == BS ) { c++; if ( *c == NL ) { c++; break; } if ( *c == EOS ) { state = ST_END; err = ERR_BS_AT_EOS; break; } } /* All other cases, incl. character after BS */ save(); c++; break; default: assert(0); } } return ( err != ERR_OK ) ? -1 : flags; } /**********************************************************************/ #if 0 int main (int argc, char *argv[]) { char *my_argv[12]; int my_argc, i, r; if ( argc != 2 ) { printf("Usage is: %s: \n", argv[0]); exit(EXIT_FAILURE); } printf("String to split is: [%s]\n", argv[1]); r = split_quoted(argv[1], &my_argc, my_argv, 12); if ( r < 0 ) { printf("Spliting failed!\n"); exit(EXIT_FAILURE); } printf("Split ok. SPLIT_DROP is %s, SPLIT_TRUNC is %s\n", (r & SPLIT_DROP) ? "ON" : "off", (r & SPLIT_TRUNC) ? "ON" : "off"); for (i = 0; i < my_argc; i++) printf("%02d : [%s]\n", i, my_argv[i]); return EXIT_SUCCESS; } #endif /**********************************************************************/ /* * Local Variables: * mode:c * tab-width: 4 * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ picocom-2.2/split.h000066400000000000000000000120301277475702100143220ustar00rootroot00000000000000/* vi: set sw=4 ts=4: * * split.h * * Function that splits a string intro arguments with quoting. * * by Nick Patavalis (npat@efault.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA */ #ifndef SPLIT_H #define SPLIT_H /* Maximum single-argument length that can be dealt-with by function * split_quoted(). Longer arguments are truncated. See below. */ #define MAX_ARG_LEN 512 /* Warning flags, set by split_quoted() to its return value. */ #define SPLIT_DROP (1 << 0) /* argument had to be dropped */ #define SPLIT_TRUNC (1 << 1) /* argument had to be truncated */ /* F split_quoted * * Splits string "s" into arguments and places them in "argv". Every * argument is a heap-allocated null-terminated string that must be * freed with free(3) when no longer needed. The first argument is * placed in "argv[*argc]", the following at subsequent "argv" slots, * and "*argc" is incremented accordingly. As a result, this function * can be called multiple times to add arguments to the same argument * vector. The argument "argv_sz" is the allocated size (in number of * slots) of the supplied argument vector ("argv"). The function takes * care not to overrun it. If more arguments are present in the * input string "s", they are dropped. * * When spliting the input string intro arguments, quoting rules * very similar to the ones used by the Unix shell are used. * * The following caracters are considered special: ' ' (space), '\t' * (tab), '\n' (newline), '\' (backslash), ''' (single quote), and '"' * (double quote). All other caracters are considered normal and can * become part of an argument without escaping. * * Arguments are separated by runs of the characters: ' ' (space), * '\t', and '\n', which are considered delimiters. * * All characters beetween single quotes (')---without * exceptions---are considered normal and become part of the current * argument (but not the single quotes themselves). * * All characters between double quotes (") are considered normal and * become part of the current argument (but not the double quotes * themselves). Exception to this is the backslash character, when * followed by one of the characters '"', '\', '$', and '`'. In this * case, the backslash is removed, and the next caracter is considered * normal and becomes part of the current argument. When the backslash * is followed by newline, both the backslash and the newline are * removed. In all other cases a backslash, within double quotes, is * considered a normal character (and becomes part of the current * argument). We treat the sequences '\$' and '\`' specially (while * there is no real reason), for better unix-shell compatibility. * * Outside of single or double quotes, every backslash caracter is * removed, and the following character (with the exception of * , see below) is considered normal and becomes part of the * current argument. If, outside of quotes, a backslash precedes a * , then both the backslash and the newline are removed. * * Examples: * * a b c d --> [a] [b] [c] [d] * 'a b' c d --> [a b] [c] [d] * 'a "b"' c d --> [a "b"] [c] [d] * "a 'b'" c d --> [a 'b'] [c] [d] * a"b c" d --> [ab c] [d] * a\ b c d --> [a b] [c] [d] * \a\b c d --> [ab] [c] [d] * \a\\b \\ c d --> [a\b] [\] [c] [d] * "a\$\b" c d --> [a$\b] [c] [d] * "\a\`\"\b" c d --> [\a`"\b] [c] [d] * * Limitation: This function cannot deal with individual arguments * longer than MAX_ARG_LEN. If such an argument is encountered, it is * truncated accordingly. * * This function returns a non-negative on success, and a negative on * failure. The only causes for failure is a malformed command string * (e.g. un-balanced quotes), or the inability to allocate an argument * string. On success the value returned can be checked against the * warning flags SPLIT_DROP, and SPLIT_TRUNC. If SPLIT_DROP is set, * then a least one argument was dropped as there was no available * slot in "argv" to store it in. If SPLIT_TRUNC is set, then at least * one argument was truncated (see limitation, above). */ int split_quoted(const char *s, int *argc, char *argv[], int argv_sz); #endif /* of SPLIT_H */ /**********************************************************************/ /* * Local Variables: * mode:c * tab-width: 4 * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ picocom-2.2/term.c000066400000000000000000000631261277475702100141450ustar00rootroot00000000000000/* vi: set sw=4 ts=4: * * term.c * * General purpose terminal handling library. * * Nick Patavalis (npat@inaccessnetworks.com) * * originaly by Pantelis Antoniou (panto@intranet.gr), Nick Patavalis * * Documentation can be found in the header file "term.h". * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * * $Id$ */ #include #include #include #include #include #include /* glibc for MIPS has its own bits/termios.h which does not define * CMSPAR, so we use the value from the generic bits/termios.h */ #ifdef __linux__ #ifndef CMSPAR #define CMSPAR 010000000000 #endif #endif /* Some BSDs (and possibly other systems too) have no mark / space * parity support, and they don't define CMSPAR. Use a zero CMSPAR in * these cases. If the user tries to set P_MARK or P_SPACE he will get * P_EVEN or P_ODD instead. */ #ifndef CMSPAR #define CMSPAR 0 #endif #ifdef __linux__ #include #endif #ifdef USE_CUSTOM_BAUD /* only works for linux, recent kernels */ #include "termios2.h" #endif #include "term.h" /***************************************************************************/ static struct term_s { int init; int fd[MAX_TERMS]; struct termios origtermios[MAX_TERMS]; struct termios currtermios[MAX_TERMS]; struct termios nexttermios[MAX_TERMS]; } term; /***************************************************************************/ int term_errno; static const char * const term_err_str[] = { [TERM_EOK] = "No error", [TERM_ENOINIT] = "Framework is uninitialized", [TERM_EFULL] = "Framework is full", [TERM_ENOTFOUND] = "Filedes not in the framework", [TERM_EEXISTS] = "Filedes already in the framework", [TERM_EATEXIT] = "Cannot install atexit handler", [TERM_EISATTY] = "Filedes is not a tty", [TERM_EFLUSH] = "Cannot flush the device", [TERM_EGETATTR] = "Cannot get the device attributes", [TERM_ESETATTR] = "Cannot set the device attributes", [TERM_EBAUD] = "Invalid baud rate", [TERM_ESETOSPEED] = "Cannot set the output speed", [TERM_ESETISPEED] = "Cannot set the input speed", [TERM_EGETSPEED] = "Cannot decode speed", [TERM_EPARITY] = "Invalid parity mode", [TERM_EDATABITS] = "Invalid number of databits", [TERM_ESTOPBITS] = "Invalid number of stopbits", [TERM_EFLOW] = "Invalid flowcontrol mode", [TERM_EDTRDOWN] = "Cannot lower DTR", [TERM_EDTRUP] = "Cannot raise DTR", [TERM_EMCTL] = "Cannot get mctl status", [TERM_EDRAIN] = "Cannot drain the device", [TERM_EBREAK] = "Cannot send break sequence" }; static char term_err_buff[1024]; const char * term_strerror (int terrnum, int errnum) { const char *rval; switch(terrnum) { case TERM_EFLUSH: case TERM_EGETATTR: case TERM_ESETATTR: case TERM_ESETOSPEED: case TERM_ESETISPEED: case TERM_EDRAIN: case TERM_EBREAK: snprintf(term_err_buff, sizeof(term_err_buff), "%s: %s", term_err_str[terrnum], strerror(errnum)); rval = term_err_buff; break; case TERM_EOK: case TERM_ENOINIT: case TERM_EFULL: case TERM_ENOTFOUND: case TERM_EEXISTS: case TERM_EATEXIT: case TERM_EISATTY: case TERM_EBAUD: case TERM_EPARITY: case TERM_EDATABITS: case TERM_ESTOPBITS: case TERM_EFLOW: case TERM_EDTRDOWN: case TERM_EDTRUP: case TERM_EMCTL: snprintf(term_err_buff, sizeof(term_err_buff), "%s", term_err_str[terrnum]); rval = term_err_buff; break; default: rval = NULL; break; } return rval; } int term_perror (const char *prefix) { return fprintf(stderr, "%s %s\n", prefix, term_strerror(term_errno, errno)); } /***************************************************************************/ #define BNONE 0xFFFFFFFF struct baud_codes { int speed; speed_t code; } baud_table[] = { { 0, B0 }, { 50, B50 }, { 75, B75 }, { 110, B110 }, { 134, B134 }, { 150, B150 }, { 200, B200 }, { 300, B300 }, { 600, B600 }, { 1200, B1200 }, { 1800, B1800 }, { 2400, B2400 }, { 4800, B4800 }, { 9600, B9600 }, { 19200, B19200 }, { 38400, B38400 }, { 57600, B57600 }, { 115200, B115200 }, #ifdef HIGH_BAUD #ifdef B230400 { 230400, B230400 }, #endif #ifdef B460800 { 460800, B460800 }, #endif #ifdef B500000 { 500000, B500000 }, #endif #ifdef B576000 { 576000, B576000 }, #endif #ifdef B921600 { 921600, B921600 }, #endif #ifdef B1000000 { 1000000, B1000000 }, #endif #ifdef B1152000 { 1152000, B1152000 }, #endif #ifdef B1500000 { 1500000, B1500000 }, #endif #ifdef B2000000 { 2000000, B2000000 }, #endif #ifdef B2500000 { 2500000, B2500000 }, #endif #ifdef B3000000 { 3000000, B3000000 }, #endif #ifdef B3500000 { 3500000, B3500000 }, #endif #ifdef B4000000 { 4000000, B4000000 }, #endif #endif /* of HIGH_BAUD */ }; #define BAUD_TABLE_SZ (sizeof(baud_table) / sizeof(baud_table[0])) int term_baud_up (int baud) { int i; for (i = 0; i < BAUD_TABLE_SZ; i++) { if ( baud >= baud_table[i].speed ) continue; else { baud = baud_table[i].speed; break; } } return baud; } int term_baud_down (int baud) { int i; for (i = BAUD_TABLE_SZ - 1; i >= 0; i--) { if ( baud <= baud_table[i].speed ) continue; else { baud = baud_table[i].speed; break; } } return baud; } static speed_t Bcode(int speed) { speed_t code = BNONE; int i; for (i = 0; i < BAUD_TABLE_SZ; i++) { if ( baud_table[i].speed == speed ) { code = baud_table[i].code; break; } } return code; } static int Bspeed(speed_t code) { int speed = -1, i; for (i = 0; i < BAUD_TABLE_SZ; i++) { if ( baud_table[i].code == code ) { speed = baud_table[i].speed; break; } } return speed; } int term_baud_ok(int baud) { #ifndef USE_CUSTOM_BAUD return (Bcode(baud) != BNONE) ? 1 : 0; #else return (baud >= 0); #endif } /**************************************************************************/ static int term_find_next_free (void) { int rval, i; do { /* dummy */ if ( ! term.init ) { term_errno = TERM_ENOINIT; rval = -1; break; } for (i = 0; i < MAX_TERMS; i++) if ( term.fd[i] == -1 ) break; if ( i == MAX_TERMS ) { term_errno = TERM_EFULL; rval = -1; break; } rval = i; } while (0); return rval; } /***************************************************************************/ static int term_find (int fd) { int rval, i; do { /* dummy */ if ( ! term.init ) { term_errno = TERM_ENOINIT; rval = -1; break; } for (i = 0; i < MAX_TERMS; i++) if (term.fd[i] == fd) break; if ( i == MAX_TERMS ) { term_errno = TERM_ENOTFOUND; rval = -1; break; } rval = i; } while (0); return rval; } /***************************************************************************/ static void term_exitfunc (void) { int r, i; do { /* dummy */ if ( ! term.init ) break; for (i = 0; i < MAX_TERMS; i++) { if (term.fd[i] == -1) continue; tcflush(term.fd[i], TCIOFLUSH); do { r = tcsetattr(term.fd[i], TCSANOW, &term.origtermios[i]); } while ( r < 0 && errno == EINTR ); if ( r < 0 ) { char *tname; tname = ttyname(term.fd[i]); if ( ! tname ) tname = "UNKNOWN"; fprintf(stderr, "%s: reset failed for dev %s: %s\n", __FUNCTION__, tname, strerror(errno)); } term.fd[i] = -1; } } while (0); } /***************************************************************************/ int term_lib_init (void) { int rval, r, i; rval = 0; do { /* dummy */ if ( term.init ) { /* reset all terms back to their original settings */ for (i = 0; i < MAX_TERMS; i++) { if (term.fd[i] == -1) continue; tcflush(term.fd[i], TCIOFLUSH); do { r = tcsetattr(term.fd[i], TCSANOW, &term.origtermios[i]); } while ( r < 0 && errno == EINTR ); if ( r < 0 ) { char *tname; tname = ttyname(term.fd[i]); if ( ! tname ) tname = "UNKNOWN"; fprintf(stderr, "%s: reset failed for dev %s: %s\n", __FUNCTION__, tname, strerror(errno)); } term.fd[i] = -1; } } else { /* initialize term structure. */ for (i = 0; i < MAX_TERMS; i++) term.fd[i] = -1; if ( atexit(term_exitfunc) != 0 ) { term_errno = TERM_EATEXIT; rval = -1; break; } /* ok. term struct is now initialized. */ term.init = 1; } } while(0); return rval; } /***************************************************************************/ int term_add (int fd) { int rval, r, i; rval = 0; do { /* dummy */ i = term_find(fd); if ( i >= 0 ) { term_errno = TERM_EEXISTS; rval = -1; break; } if ( ! isatty(fd) ) { term_errno = TERM_EISATTY; rval = -1; break; } i = term_find_next_free(); if ( i < 0 ) { rval = -1; break; } r = tcgetattr(fd, &term.origtermios[i]); if ( r < 0 ) { term_errno = TERM_EGETATTR; rval = -1; break; } term.currtermios[i] = term.origtermios[i]; term.nexttermios[i] = term.origtermios[i]; term.fd[i] = fd; } while (0); return rval; } /***************************************************************************/ int term_remove(int fd) { int rval, r, i; rval = 0; do { /* dummy */ i = term_find(fd); if ( i < 0 ) { rval = -1; break; } do { /* dummy */ r = tcflush(term.fd[i], TCIOFLUSH); if ( r < 0 ) { term_errno = TERM_EFLUSH; rval = -1; break; } r = tcsetattr(term.fd[i], TCSANOW, &term.origtermios[i]); if ( r < 0 ) { term_errno = TERM_ESETATTR; rval = -1; break; } } while (0); term.fd[i] = -1; } while (0); return rval; } /***************************************************************************/ int term_erase(int fd) { int rval, i; rval = 0; do { /* dummy */ i = term_find(fd); if ( i < 0 ) { rval = -1; break; } term.fd[i] = -1; } while (0); return rval; } /***************************************************************************/ int term_replace (int oldfd, int newfd) { int rval, r, i; rval = 0; do { /* dummy */ i = term_find(oldfd); if ( i < 0 ) { rval = -1; break; } r = tcsetattr(newfd, TCSANOW, &term.currtermios[i]); if ( r < 0 ) { term_errno = TERM_ESETATTR; rval = -1; break; } r = tcgetattr(newfd, &term.currtermios[i]); if ( r < 0 ) { term_errno = TERM_EGETATTR; rval = -1; break; } term.fd[i] = newfd; } while (0); return rval; } /***************************************************************************/ int term_reset (int fd) { int rval, r, i; rval = 0; do { /* dummy */ i = term_find(fd); if ( i < 0 ) { rval = -1; break; } r = tcflush(term.fd[i], TCIOFLUSH); if ( r < 0 ) { term_errno = TERM_EFLUSH; rval = -1; break; } r = tcsetattr(term.fd[i], TCSANOW, &term.origtermios[i]); if ( r < 0 ) { term_errno = TERM_ESETATTR; rval = -1; break; } r = tcgetattr(term.fd[i], &term.currtermios[i]); if ( r < 0 ) { term_errno = TERM_EGETATTR; rval = -1; break; } term.nexttermios[i] = term.currtermios[i]; } while (0); return rval; } /***************************************************************************/ int term_revert (int fd) { int rval, i; rval = 0; do { /* dummy */ i = term_find(fd); if ( i < 0 ) { rval = -1; break; } term.nexttermios[i] = term.currtermios[i]; } while (0); return rval; } /***************************************************************************/ int term_refresh (int fd) { int rval, r, i; rval = 0; do { /* dummy */ i = term_find(fd); if ( i < 0 ) { rval = -1; break; } r = tcgetattr(fd, &term.currtermios[i]); if ( r < 0 ) { term_errno = TERM_EGETATTR; rval = -1; break; } } while (0); return rval; } /***************************************************************************/ int term_apply (int fd, int now) { int when, rval, r, i; when = now ? TCSANOW : TCSAFLUSH; rval = 0; do { /* dummy */ i = term_find(fd); if ( i < 0 ) { rval = -1; break; } r = tcsetattr(term.fd[i], when, &term.nexttermios[i]); if ( r < 0 ) { term_errno = TERM_ESETATTR; rval = -1; break; } r = tcgetattr(term.fd[i], &term.nexttermios[i]); if ( r < 0 ) { term_errno = TERM_EGETATTR; rval = -1; break; } term.currtermios[i] = term.nexttermios[i]; } while (0); return rval; } /***************************************************************************/ int term_set_raw (int fd) { int rval, i; rval = 0; do { /* dummy */ i = term_find(fd); if ( i < 0 ) { rval = -1; break; } /* BSD raw mode */ cfmakeraw(&term.nexttermios[i]); /* one byte at a time, no timer */ term.nexttermios[i].c_cc[VMIN] = 1; term.nexttermios[i].c_cc[VTIME] = 0; } while (0); return rval; } /***************************************************************************/ int term_set_baudrate (int fd, int baudrate) { int rval, r, i; speed_t spd; struct termios tio; rval = 0; do { /* dummy */ i = term_find(fd); if ( i < 0 ) { rval = -1; break; } tio = term.nexttermios[i]; spd = Bcode(baudrate); if ( spd != BNONE ) { r = cfsetospeed(&tio, spd); if ( r < 0 ) { term_errno = TERM_ESETOSPEED; rval = -1; break; } cfsetispeed(&tio, B0); } else { #ifdef USE_CUSTOM_BAUD r = cfsetospeed_custom(&tio, baudrate); if ( r < 0 ) { term_errno = TERM_ESETOSPEED; rval = -1; break; } cfsetispeed(&tio, B0); #else /* ! defined USE_CUSTOM_BAUD */ term_errno = TERM_EBAUD; rval = -1; break; #endif /* of USE_CUSTOM_BAUD */ } term.nexttermios[i] = tio; } while (0); return rval; } int term_get_baudrate (int fd, int *ispeed) { speed_t code; int i, ospeed; do { /* dummy */ i = term_find(fd); if ( i < 0 ) { ospeed = -1; break; } if ( ispeed ) { code = cfgetispeed(&term.currtermios[i]); *ispeed = Bspeed(code); #ifdef USE_CUSTOM_BAUD if ( *ispeed < 0 ) { *ispeed = cfgetispeed_custom(&term.currtermios[i]); } #endif } code = cfgetospeed(&term.currtermios[i]); ospeed = Bspeed(code); if ( ospeed < 0 ) { #ifdef USE_CUSTOM_BAUD ospeed = cfgetospeed_custom(&term.currtermios[i]); if ( ospeed < 0 ) { term_errno = TERM_EGETSPEED; } #else term_errno = TERM_EGETSPEED; #endif } } while (0); return ospeed; } /***************************************************************************/ int term_set_parity (int fd, enum parity_e parity) { int rval, i; struct termios *tiop; rval = 0; do { /* dummy */ i = term_find(fd); if ( i < 0 ) { rval = -1; break; } tiop = &term.nexttermios[i]; switch (parity) { case P_EVEN: tiop->c_cflag &= ~(PARODD | CMSPAR); tiop->c_cflag |= PARENB; break; case P_ODD: tiop->c_cflag &= ~CMSPAR; tiop->c_cflag |= PARENB | PARODD; break; case P_MARK: tiop->c_cflag |= PARENB | PARODD | CMSPAR; break; case P_SPACE: tiop->c_cflag &= ~PARODD; tiop->c_cflag |= PARENB | CMSPAR; break; case P_NONE: tiop->c_cflag &= ~(PARENB | PARODD | CMSPAR); break; default: term_errno = TERM_EPARITY; rval = -1; break; } if ( rval < 0 ) break; } while (0); return rval; } enum parity_e term_get_parity (int fd) { tcflag_t flg; int i, parity; do { /* dummy */ i = term_find(fd); if ( i < 0 ) { parity = -1; break; } flg = term.currtermios[i].c_cflag; if ( ! (flg & PARENB) ) { parity = P_NONE; } else if ( flg & CMSPAR ) { parity = (flg & PARODD) ? P_MARK : P_SPACE; } else { parity = (flg & PARODD) ? P_ODD : P_EVEN; } } while (0); return parity; } /***************************************************************************/ int term_set_databits (int fd, int databits) { int rval, i; struct termios *tiop; rval = 0; do { /* dummy */ i = term_find(fd); if ( i < 0 ) { rval = -1; break; } tiop = &term.nexttermios[i]; switch (databits) { case 5: tiop->c_cflag = (tiop->c_cflag & ~CSIZE) | CS5; break; case 6: tiop->c_cflag = (tiop->c_cflag & ~CSIZE) | CS6; break; case 7: tiop->c_cflag = (tiop->c_cflag & ~CSIZE) | CS7; break; case 8: tiop->c_cflag = (tiop->c_cflag & ~CSIZE) | CS8; break; default: term_errno = TERM_EDATABITS; rval = -1; break; } if ( rval < 0 ) break; } while (0); return rval; } int term_get_databits (int fd) { tcflag_t flg; int i, bits; do { /* dummy */ i = term_find(fd); if ( i < 0 ) { bits = -1; break; } flg = term.currtermios[i].c_cflag & CSIZE; switch (flg) { case CS5: bits = 5; break; case CS6: bits = 6; break; case CS7: bits = 7; break; case CS8: default: bits = 8; break; } } while (0); return bits; } /***************************************************************************/ int term_set_stopbits (int fd, int stopbits) { int rval, i; struct termios *tiop; rval = 0; do { /* dummy */ i = term_find(fd); if ( i < 0 ) { rval = -1; break; } tiop = &term.nexttermios[i]; switch (stopbits) { case 1: tiop->c_cflag &= ~CSTOPB; break; case 2: tiop->c_cflag |= CSTOPB; break; default: term_errno = TERM_ESTOPBITS; rval = -1; break; } if ( rval < 0 ) break; } while (0); return rval; } int term_get_stopbits (int fd) { int i, bits; do { /* dummy */ i = term_find(fd); if ( i < 0 ) { bits = -1; break; } bits = (term.currtermios[i].c_cflag & CSTOPB) ? 2 : 1; } while (0); return bits; } /***************************************************************************/ int term_set_flowcntrl (int fd, enum flowcntrl_e flowcntl) { int rval, i; struct termios *tiop; rval = 0; do { /* dummy */ i = term_find(fd); if ( i < 0 ) { rval = -1; break; } tiop = &term.nexttermios[i]; switch (flowcntl) { case FC_RTSCTS: tiop->c_cflag |= CRTSCTS; tiop->c_iflag &= ~(IXON | IXOFF | IXANY); break; case FC_XONXOFF: tiop->c_cflag &= ~(CRTSCTS); tiop->c_iflag |= IXON | IXOFF; break; case FC_NONE: tiop->c_cflag &= ~(CRTSCTS); tiop->c_iflag &= ~(IXON | IXOFF | IXANY); break; default: term_errno = TERM_EFLOW; rval = -1; break; } if ( rval < 0 ) break; } while (0); return rval; } enum flowcntrl_e term_get_flowcntrl (int fd) { int i, flow; int rtscts, xoff, xon; do { /* dummy */ i = term_find(fd); if ( i < 0 ) { flow = -1; break; } rtscts = (term.currtermios[i].c_cflag & CRTSCTS) ? 1 : 0; xoff = (term.currtermios[i].c_iflag & IXOFF) ? 1 : 0; xon = (term.currtermios[i].c_iflag & (IXON | IXANY)) ? 1 : 0; if ( rtscts && ! xoff && ! xon ) { flow = FC_RTSCTS; } else if ( ! rtscts && xoff && xon ) { flow = FC_XONXOFF; } else if ( ! rtscts && ! xoff && ! xon ) { flow = FC_NONE; } else { flow = FC_OTHER; } } while (0); return flow; } /***************************************************************************/ int term_set_local(int fd, int local) { int rval, i; struct termios *tiop; rval = 0; do { /* dummy */ i = term_find(fd); if ( i < 0 ) { rval = -1; break; } tiop = &term.nexttermios[i]; if ( local ) tiop->c_cflag |= CLOCAL; else tiop->c_cflag &= ~CLOCAL; } while (0); return rval; } /***************************************************************************/ int term_set_hupcl (int fd, int on) { int rval, i; struct termios *tiop; rval = 0; do { /* dummy */ i = term_find(fd); if ( i < 0 ) { rval = -1; break; } tiop = &term.nexttermios[i]; if ( on ) tiop->c_cflag |= HUPCL; else tiop->c_cflag &= ~HUPCL; } while (0); return rval; } /***************************************************************************/ int term_set(int fd, int raw, int baud, enum parity_e parity, int databits, int stopbits, enum flowcntrl_e fc, int local, int hup_close) { int rval, r, i, ni; struct termios tio; rval = 0; do { /* dummy */ i = term_find(fd); if ( i < 0 ) { ni = term_add(fd); if ( ni < 0 ) { rval = -1; break; } } else { ni = i; } tio = term.nexttermios[ni]; do { /* dummy */ if (raw) { r = term_set_raw(fd); if ( r < 0 ) { rval = -1; break; } } r = term_set_baudrate(fd, baud); if ( r < 0 ) { rval = -1; break; } r = term_set_parity(fd, parity); if ( r < 0 ) { rval = -1; break; } r = term_set_databits(fd, databits); if ( r < 0 ) { rval = -1; break; } r = term_set_stopbits(fd, stopbits); if ( r < 0 ) { rval = -1; break; } r = term_set_flowcntrl(fd, fc); if ( r < 0 ) { rval = -1; break; } r = term_set_local(fd, local); if ( r < 0 ) { rval = -1; break; } r = term_set_hupcl(fd, hup_close); if ( r < 0 ) { rval = -1; break; } } while (0); if ( rval < 0 ) { if ( i < 0 ) /* new addition. must be removed */ term.fd[ni] = -1; else /* just revert to previous settings */ term.nexttermios[ni] = tio; } } while (0); return rval; } /***************************************************************************/ int term_pulse_dtr (int fd) { int rval, r, i; rval = 0; do { /* dummy */ i = term_find(fd); if ( i < 0 ) { rval = -1; break; } #ifdef __linux__ { int opins = TIOCM_DTR; r = ioctl(fd, TIOCMBIC, &opins); if ( r < 0 ) { term_errno = TERM_EDTRDOWN; rval = -1; break; } sleep(1); r = ioctl(fd, TIOCMBIS, &opins); if ( r < 0 ) { term_errno = TERM_EDTRUP; rval = -1; break; } } #else { struct termios tio, tioold; r = tcgetattr(fd, &tio); if ( r < 0 ) { term_errno = TERM_ESETATTR; rval = -1; break; } tioold = tio; cfsetospeed(&tio, B0); cfsetispeed(&tio, B0); r = tcsetattr(fd, TCSANOW, &tio); if ( r < 0 ) { term_errno = TERM_ESETATTR; rval = -1; break; } sleep(1); r = tcsetattr(fd, TCSANOW, &tioold); if ( r < 0 ) { term.currtermios[i] = tio; term_errno = TERM_ESETATTR; rval = -1; break; } } #endif /* of __linux__ */ } while (0); return rval; } /***************************************************************************/ int term_raise_dtr(int fd) { int rval, r, i; rval = 0; do { /* dummy */ i = term_find(fd); if ( i < 0 ) { rval = -1; break; } #ifdef __linux__ { int opins = TIOCM_DTR; r = ioctl(fd, TIOCMBIS, &opins); if ( r < 0 ) { term_errno = TERM_EDTRUP; rval = -1; break; } } #else r = tcsetattr(fd, TCSANOW, &term.currtermios[i]); if ( r < 0 ) { /* FIXME: perhaps try to update currtermios */ term_errno = TERM_ESETATTR; rval = -1; break; } #endif /* of __linux__ */ } while (0); return rval; } /***************************************************************************/ int term_lower_dtr(int fd) { int rval, r, i; rval = 0; do { /* dummy */ i = term_find(fd); if ( i < 0 ) { rval = -1; break; } #ifdef __linux__ { int opins = TIOCM_DTR; r = ioctl(fd, TIOCMBIC, &opins); if ( r < 0 ) { term_errno = TERM_EDTRDOWN; rval = -1; break; } } #else { struct termios tio; r = tcgetattr(fd, &tio); if ( r < 0 ) { term_errno = TERM_EGETATTR; rval = -1; break; } term.currtermios[i] = tio; cfsetospeed(&tio, B0); cfsetispeed(&tio, B0); r = tcsetattr(fd, TCSANOW, &tio); if ( r < 0 ) { term_errno = TERM_ESETATTR; rval = -1; break; } } #endif /* of __linux__ */ } while (0); return rval; } /***************************************************************************/ int term_get_mctl (int fd) { int mctl, i; do { /* dummy */ i = term_find(fd); if ( i < 0 ) { mctl = -1; break; } #ifdef __linux__ { int r, pmctl; r = ioctl(fd, TIOCMGET, &pmctl); if (r < 0) { mctl = -1; break; } mctl = 0; if (pmctl & TIOCM_DTR) mctl |= MCTL_DTR; if (pmctl & TIOCM_DSR) mctl |= MCTL_DSR; if (pmctl & TIOCM_CD) mctl |= MCTL_DCD; if (pmctl & TIOCM_RTS) mctl |= MCTL_RTS; if (pmctl & TIOCM_CTS) mctl |= MCTL_CTS; if (pmctl & TIOCM_RI) mctl |= MCTL_RI; } #else mctl = MCTL_UNAVAIL; #endif } while(0); return mctl; } int term_drain(int fd) { int rval, r; rval = 0; do { /* dummy */ r = term_find(fd); if ( r < 0 ) { rval = -1; break; } do { #ifdef __BIONIC__ /* See: http://dan.drown.org/android/src/gdb/no-tcdrain */ r = ioctl(fd, TCSBRK, 1); #else r = tcdrain(fd); #endif } while ( r < 0 && errno == EINTR); if ( r < 0 ) { term_errno = TERM_EDRAIN; rval = -1; break; } } while (0); return rval; } /***************************************************************************/ int term_flush(int fd) { int rval, r; rval = 0; do { /* dummy */ r = term_find(fd); if ( r < 0 ) { rval = -1; break; } r = tcflush(fd, TCIOFLUSH); if ( r < 0 ) { rval = -1; break; } } while (0); return rval; } /***************************************************************************/ int term_break(int fd) { int rval, r; rval = 0; do { /* dummy */ r = term_find(fd); if ( r < 0 ) { rval = -1; break; } r = tcsendbreak(fd, 0); if ( r < 0 ) { term_errno = TERM_EBREAK; rval = -1; break; } } while (0); return rval; } /**************************************************************************/ /* * Local Variables: * mode:c * tab-width: 4 * c-basic-offset: 4 * End: */ picocom-2.2/term.h000066400000000000000000000563131277475702100141520ustar00rootroot00000000000000/* vi: set sw=4 ts=4: * * term.h * * Simple terminal management library. Wraps termios(3), and * simplifies the logistics required for the reliable management and * control of terminals. * * Principles of operation: * * After the library is initialized, one or more file-descriptors can * be added to (and latter removed from) the list managed by the * it. These file descriptors must be opened on terminal devices. For * every fd, the original settings of the associated terminal device * are saved by the library. These settings are restored when the fd * is removed from the framework, or at program termination [by means * of an atexit(3) handler installed by the library], or at user * request. The library maintains three structures for every fd in the * framework: The original settings structure ("origtermios"), keeping * the settings of the terminal device when the respective filedes was * added to the framework. The current settings structure * ("currtermios"), keeping the current settings of the associated * terminal device; and the next settings structure ("nexttermios") * which keeps settings to be applied to the associated terminal * device at a latter time, upon user request. The "term_set_*" * functions can be used to modify the device settings stored in the * nexttermios structure. Using functions provided by the library the * user can: Apply the nexttermios settings to the device. Revert all * changes made on nexttermios by copying the currtermios structure to * nexttermios. Reset the device, by configuring it to the original * settings, and copying origtermios to currtermios and * nexttermios. Refresh the device by rereading the current settings * from it and updating currtermios (to catch up with changes made to * the device by means outside of this framework). * * Interface summary: * * F term_lib_init - library initialization * F term_add - add a filedes to the framework * F term_remove - remove a filedes from the framework * F term_erase - remove a filedes from the framework without reset * F term_replace - replace a fd w/o affecting the settings stuctures * F term_reset - revert a device to the settings in "origtermios" * F term_apply - configure a device to the settings in "nexttermios" * F term_revert - discard "nexttermios" by copying-over "currtermios" * F term_refresh - update "currtermios" from the device * F term_set_raw - set "nexttermios" to raw mode * F term_set_baudrate - set the baudrate in "nexttermios" * F term_set_parity - set the parity mode in "nexttermios" * F term_set_databits - set the databits in "nexttermios" * F term_set_stopbits - set the stopbits in "nexttermios" * F term_set_flowcntrl - set the flowcntl mode in "nexttermios" * F term_set_hupcl - enable or disable hupcl in "nexttermios" * F term_set_local - set "nexttermios" to local or non-local mode * F term_set - set all params of "nexttermios" in a single stroke * F term_get_baudrate - return the baudrate set in "currtermios" * F term_get_parity - return the parity setting in "currtermios" * F term_get_databits - return the data-bits setting in "currtermios" * F term_get_flowcntrl - return the flow-control setting in "currtermios" * F term_pulse_dtr - pulse the DTR line a device * F term_lower_dtr - lower the DTR line of a device * F term_raise_dtr - raise the DTR line of a device * F term_get_mctl - Get modem control signals status * F term_drain - drain the output from the terminal buffer * F term_flush - discard terminal input and output queue contents * F term_break - generate a break condition on a device * F term_baud_up - return next higher baudrate * F term_baud_down - return next lower baudrate * F term_baud_ok - check if baudrate is valid * F term_strerror - return a string describing current error condition * F term_perror - print a string describing the current error condition * G term_errno - current error condition of the library * E term_errno_e - error condition codes * E parity_t - library supported parity types * E flocntrl_t - library supported folw-control modes * M MAX_TERM - maximum number of fds that can be managed * * by Nick Patavalis (npat@inaccessnetworks.com) * * originaly by Pantelis Antoniou (panto@intranet.gr), Nick Patavalis * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * * $Id: term.h,v 1.1 2003/05/07 18:00:05 npat Exp $ */ #ifndef TERM_H #define TERM_H /* M MAX_TERMS * * Maximum nuber of terminals that can be managed by the library. Keep * relatively low, since linear searches are used. Reasonable values * would be: 16, 32, 64, etc. */ #define MAX_TERMS 16 /* * E term_errno_e * * Library error-condition codes. These marked with "see errno" * correspond to system errors, so it makes sense to also check the * system's error-condition code (errno) in order to fully determine * what went wrong. * * See the error strings in "term.c" for a description of each. */ enum term_errno_e { TERM_EOK = 0, TERM_ENOINIT, TERM_EFULL, TERM_ENOTFOUND, TERM_EEXISTS, TERM_EATEXIT, TERM_EISATTY, TERM_EFLUSH, /* see errno */ TERM_EGETATTR, /* see errno */ TERM_ESETATTR, /* see errno */ TERM_EBAUD, TERM_ESETOSPEED, TERM_ESETISPEED, TERM_EGETSPEED, TERM_EPARITY, TERM_EDATABITS, TERM_ESTOPBITS, TERM_EFLOW, TERM_EDTRDOWN, TERM_EDTRUP, TERM_EMCTL, TERM_EDRAIN, /* see errno */ TERM_EBREAK }; /* E parity_e * * Parity modes supported by the library: * * P_NONE - no patiry * P_EVEN - even parity * P_ODD - odd parity * P_MARK - mark parity (parity bit always 1) * P_SPACE - space parity (parity bit always 0) */ enum parity_e { P_NONE = 0, P_EVEN, P_ODD, P_MARK, P_SPACE }; /* * E flowcntrl_e * * Flow control modes, supported by the library. * * FC_NONE - no flow control * FC_RTSCTS - RTS/CTS handshaking, also known as hardware * flow-control. * FC_XONXOFF - xon/xoff flow control. */ enum flowcntrl_e { FC_NONE = 0, FC_RTSCTS, FC_XONXOFF, FC_OTHER }; /* * C MCTL_xxx * * Modem control line bits. Used against the return value of * term_get_mctl(). */ #define MCTL_DTR (1<<1) /* O: Data Terminal Ready */ #define MCTL_DSR (1<<2) /* I: Data Set Ready */ #define MCTL_DCD (1<<3) /* I: Data Carrier Detect */ #define MCTL_RTS (1<<4) /* O: Request To Send */ #define MCTL_CTS (1<<5) /* I: Clear To Send */ #define MCTL_RI (1<<6) /* I: Ring Indicator */ #define MCTL_UNAVAIL (1<<0) /* MCTL lines (status) not available */ /***************************************************************************/ /* * G term_errno * * Keeps the current library error-condtion code */ extern int term_errno; /***************************************************************************/ /* * F term_strerror * * Return a string descibing the current library error condition. If * the error condition reflects a system error, then the respective * system-error description is appended at the end of the returned * string. The returned string points to a statically allocated buffer * that is overwritten with every call to term_strerror() * * Returns a string describing the current library (and possibly * system) error condition. */ const char *term_strerror (int terrnum, int errnum); /* * F term_perror * * Emit a description of the current library (and possibly system) * error condition to the standard-error stream. The description is * prefixed by a user-supplied string. What is actually emmited is: * * \n * * The description emitted is the string returned by term_strerror(). * * Returns the number of characters emmited to the standard-error * stream or a neagative on failure. */ int term_perror (const char *prefix); /* F term_lib_init * * Initialize the library * * Initialize the library. This function must be called before any * attemt to use the library. If this function is called and the * library is already initialized, all terminals associated with the * file-descriptors in the framework will be reset to their original * settings, and the file-descriptors will be removed from the * framework. An atexit(3) handler is installed by the library which * resets and removes all managed terminals. * * Returns negative on failure, non-negative on success. This function * will only fail if the atexit(3) handler cannot be * installed. Failure to reset a terminal to the original settings is * not considered an error. */ int term_lib_init (void); /* F term_add * * Add the filedes "fd" to the framework. The filedes must be opened * on a terminal device or else the addition will fail. The settings * of the terminal device associated with the filedes are read and * stored in the origtermios structure. * * Returns negative on failure, non-negative on success. */ int term_add (int fd); /* F term_remove * * Remove the filedes "fd" from the framework. The device associated * with the filedes is reset to its original settings (those it had * when it was added to the framework) * * Return negative on failure, non-negative on success. The filedes is * always removed form the framework even if this function returns * failure, indicating that the device reset failed. */ int term_remove (int fd); /* F term_erase * * Remove the filedes "fd" from the framework. The device associated * with the filedes is *not* reset to its original settings. * * Return negative on failure, non-negative on success. The only * reason for failure is the filedes not to be found. */ int term_erase (int fd); /* F term_replace * * Replace a managed filedes without affecting the associated settings * structures. The "newfd" takes the place of "oldfd". "oldfd" is * removed from the framework without the associated device beign * reset (it is most-likely no longer connected to a device anyway, * and reset would fail). The device associated with "newfd" is * configured with "oldfd"s current settings (stored in the * "currtermios" structure). After applying the settings to "newfd", * the "currtermios" structure is re-read from the device, so that it * corresponds to the actual device settings. * * Returns negative on failure, non-negative on success. In case of * failure "oldfd" is not removed from the framework, and no * replacement takes place. * * The usual reason to replace the filedes of a managed terminal is * because the device was closed and re-opened. This function gives * you a way to do transparent "open"s and "close"s: Before you close * a device, it has certain settings managed by the library. When you * close it and then re-open it many of these settings are lost, since * the device reverts to system-default settings. By calling * term_replace, you conceptually _maintain_ the old (pre-close) * settings to the new (post-open) filedes. */ int term_replace (int oldfd, int newfd); /* * F term_apply * * Applies the settings stored in the "nexttermios" structure * associated with the managed filedes "fd", to the respective * terminal device. It then re-reads the settings form the device and * stores them in "nexttermios". Finally it copies "nexttermios" to * "currtermios". If "now" is not zero, settings are applied * immediatelly, otherwise setting are applied after the output * buffers are drained and the input buffers are discarder. In this * sense, term_apply(fd, 0) is equivalent to: term_drain(fd); * term_flush(fd); term_apply(fd, 1); * * Returns negative on failure, non negative on success. In case of * failure the "nexttermios" and "currtermios" structures are not * affected. */ int term_apply (int fd, int now); /* * F term_revert * * Discards all the changes made to the nexttermios structure * associated with the managed filedes "fd" that have not been applied * to the device. It does this by copying currtermios to nexttermios. * * Returns negative on failure, non negative on success. Returns * failure only to indicate invalid arguments, so the return value can * be safely ignored. */ int term_revert (int fd); /* F term_reset * * Reset the terminal device associated with the managed filedes "fd" * to its "original" settings. This function applies the settings in * the "origtermios" structure to the actual device. It then reads the * settings from the device and stores them in both the "currtermios" * and "nexttermios" stuctures. * * Returns negative on failure, non-negative of success. On failure * the the "origtermios", "currtermios", and "nexttermios" stuctures * associated with the filedes remain unaffected. */ int term_reset (int fd); /* * F term_refresh * * Updates the contents of the currtermios structure associated with * the managed filedes "fd", by reading the settings from the * respective terminal device. * * Returns negative on failure, non negative on success. On failure * the currtermios structure remains unaffected. */ int term_refresh (int fd); /* F term_set_raw * * Sets the "nexttermios" structure associated with the managed * filedes "fd" to raw mode. The effective settings of the device are * not affected by this function. * * Returns negative on failure, non-negative on success. Returns * failure only to indicate invalid arguments, so the return value can * be safely ignored. * * When in raw mode, no characters are processed by the terminal * driver and there is no line-discipline or buffering. More * technically setting to raw mode means, affecting the following * terminal settings as indicated: * * -ignbrk -brkint -parmrk -istrip -inlcr -igncr -icrnl -ixon * -opost -echo -echonl -icannon -isig -iexten -csize -parenb * cs8 min=1 time=0 */ int term_set_raw (int fd); /* F term_set_baudrate * * Sets the baudrate in the "nexttermios" structure associated with * the managed filedes "fd" to "baudrate". The effective settings of * the device are not affected by this function. * * Supported baudrates: 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, * 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400 * * Returns negative on failure, non negative on success. Returns * failure only to indicate invalid arguments, so the return value can * be safely ignored. */ int term_set_baudrate (int fd, int baudrate); /* F term_set_parity * * Sets the parity mode in the "nexttermios" structure associated with * the managed filedes "fd" to "parity". The effective settings of the * device are not affected by this function. * * Supported parity modes are: p_even, p_odd, p_none. * * Returns negative on failure, non negative on success. Returns * failure only to indicate invalid arguments, so the return value can * be safely ignored. */ int term_set_parity (int fd, enum parity_e parity); /* F term_set_databits * * Sets the databits number in the "nexttermios" structure associated * with the managed filedes "fd" to "databits". The effective settings * of the device are not affected by this function. * * 5, 6, 7, and 8 databits are supported by the library. * * Returns negative on failure, non negative on success. Returns * failure only to indicate invalid arguments, so the return value can * be safely ignored. */ int term_set_databits (int fd, int databits); /* F term_set_stopbits * * Sets the stopbits number in the "nexttermios" structure associated * with the managed filedes "fd" to "stopbits". The effective settings * of the device are not affected by this function. * * 1 and 2 stopbits are supported by the library. * * Returns negative on failure, non negative on success. Returns * failure only to indicate invalid arguments, so the return value can * be safely ignored. */ int term_set_stopbits (int fd, int stopbits); /* F term_set_flowcntrl * * Sets the folwcontrol mode in the "nexttermios" structure associated * with the managed filedes "fd" to "flowcntl". The effective settings * of the device are not affected by this function. * * The following flow control modes are supportd by the library: * FC_NONE, FC_RTSCTS, FC_XONXOFF. * * Returns negative on failure, non negative on success. Returns * failure only to indicate invalid arguments, so the return value can * be safely ignored. */ int term_set_flowcntrl (int fd, enum flowcntrl_e flowcntl); /* F term_set_hupcl * * Enables ("on" = nonzero) or disables ("on" = zero) the * "HUP-on-close" setting in the "nexttermios" structure associated * with the managed filedes "fd". The effective settings of the device * are not affected by this function. * * Returns negative on failure, non negative on success. Returns * failure only to indicate invalid arguments, so the return value can * be safely ignored. */ int term_set_hupcl (int fd, int on); /* F term_set_local. * * Enables ("local" = nonzero) or disables ("local" = zero) the * "local-mode" setting in the "nexttermios" structure associated with * the managed filedes "fd". The effective settings of the device are * not affected by this function. * * Returns negative on failure, non negative on success. Returns * failure only to indicate invalid arguments, so the return value can * be safely ignored. */ int term_set_local (int fd, int local); /* F temr_set * * Sets most of the parameters in the "nexttermios" structure * associated with the managed filedes "fd". Actually sets the * following: * * Raw mode if "raw" is nonzero. * Baudrate to "baud". * Parity mode to "parity". * Flow control mode to "fc". * Enables local mode if "local" is nonzero, dis. otherwise. * Enables HUP-on-close if "hupcl" is nonzero, dis. otherwise * * The effective settings of the device are not affected by this * function. Additionally if the filedes "fd" is not managed, it is * added to the framework. * * Returns negative on failure, non negative on success. On failure * none of the settings of "nexttermios" is affected. *If* the filedes * "fd" is already in the framework, then the function returns failure * only to indicate invalid arguments, so, in this case, the return * value can be safely ignored. If the function successfully adds the * filedes to the framework, and following this it fails, then it will * remove the filedes before returning. */ int term_set (int fd, int raw, int baud, enum parity_e parity, int databits, int stopbits, enum flowcntrl_e fc, int local, int hupcl); /* F term_get_baudrate * * Reads and decodes the current baudrate settings in the * "currtermios" structure of the managed filedes "fd". * * Returns the decoded output baudrate (as bits-per-second), or -1 if * the output baudrate cannot be decoded, or if "fd" does not * correspond to a managed filedes. If "ispeed" is not NULL, it writes * the decoded input baudrate to the integer pointed-to by "ispeed"; * if the input baudrate cannot be decoded in writes -1 instead. */ int term_get_baudrate (int fd, int *ispeed); /* F term_get_parity * * Reads and decodes the current parity settings in the * "currtermios" structure of the managed filedes "fd". * * Returns one of the "enum parity_e" members, or -1 if "fd" does not * correspond to a managed filedes. */ enum parity_e term_get_parity (int fd); /* F term_get_databits * * Reads and decodes the current databits settings in the * "currtermios" structure of the managed filedes "fd". * * Returns the number of databits (5..8), or -1 if "fd" does not * correspond to a managed filedes. */ int term_get_databits (int fd); /* F term_get_stopbits * * Reads and decodes the current stopbits settings in the * "currtermios" structure of the managed filedes "fd". * * Returns the number of databits (1 or 2), or -1 if "fd" does not * correspond to a managed filedes. */ int term_get_stopbits (int fd); /* F term_get_flowcntrl * * Reads and decodes the current flow-control settings in the * "currtermios" structure of the managed filedes "fd". * * Returns one of the "enum flowcntrl_e" members, or -1 if "fd" does * not correspond to a managed filedes. */ enum flowcntrl_e term_get_flowcntrl (int fd); /* F term_pulse_dtr * * Pulses the DTR line of the device associated with the managed * filedes "fd". The DTR line is lowered for 1sec and then raised * again. * * Returns negative on failure, non negative on success. */ int term_pulse_dtr (int fd); /* F term_lower_dtr * * Lowers the DTR line of the device associated with the managed * filedes "fd". * * Returns negative on failure, non negative on success. */ int term_lower_dtr (int fd); /* F term_raise_dtr * * Raises the DTR line of the device associated with the managed * filedes "fd". * * Returns negative on failure, non negative on success. */ int term_raise_dtr (int fd); /* F term_get_mctl * * Get the status of the modem control lines of the serial port * (terminal) associated with the managed filedes "fd". * * On error (fd is not managed) return a negative. If the feature is * not available returns MCTL_UNAVAIL. Otherwise returns a word that * can be checked against the MCTL_* flags. */ int term_get_mctl (int fd); /* F term_drain * * Drains (flushes) the output queue of the device associated with the * managed filedes "fd". This functions blocks until all the contents * of output queue have been transmited. * * Returns negative on failure, non negative on success. */ int term_drain (int fd); /* F term_flush * * Discards all the contents of the input AND output queues of the * device associated with the managed filedes "fd". Although it is * called flush this functions does NOT FLUSHES the terminal * queues. It just DISCARDS their contents. The name has stuck from * the POSIX terminal call: "tcflush". * * Returns negative on failure, non negative on success. */ int term_flush (int fd); /* F term_break * * This function generates a break condition on the device associated * with the managed filedes "fd", by transmiting a stream of * zero-bits. The stream of zero-bits has a duriation typically * between 0.25 and 0.5 seconds. * * Returns negative on failure, non negative on success. */ int term_break(int fd); /***************************************************************************/ /* F term_baud_up * * Returns the next higher valid baudrate. Returns "baud" if there is * no higher valid baudrate. */ int term_baud_up (int baud); /* F term_baud_down * * Returns the next lower valid baudrate. Returns "baud" if there is * no lower valid baudrate. */ int term_baud_down (int baud); /* F term_baud_ok * * Returns non-zero if "baud" is a valid baudrate, zero otherwise. */ int term_baud_ok(int baud); /***************************************************************************/ #endif /* of TERM_H */ /***************************************************************************/ /* * Local Variables: * mode:c * tab-width: 4 * c-basic-offset: 4 * End: */ picocom-2.2/termbits2.h000066400000000000000000000132141277475702100151070ustar00rootroot00000000000000/* * termbits2.c * * Stuff that we should include from kernel sources, if we could; but * we can't. Included from "termios2.h" * * by Nick Patavalis (npat@efault.net) * * ATTENTION: Linux-specific kludge! * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA */ #ifndef TERMBITS2_H #define TERMBITS2_H #ifndef __linux__ #error "Linux specific code!" #endif /* We need tcflag_t, cc_t, speed_t, CBAUDEX, etc */ #include /* These definitions must correspond to the kernel structures as defined in: /arch//include/uapi/asm/termbits.h or /include/uapi/asm-generic/termbits.h which are the same as: /usr/include//asm/termbits.h or /usr/include/asm-generic/termbits.h Unfortunatelly, we cannot just include or or (all would do the trick) because then "struct termios" would be re-defined to the kernel version, which is not the same as the libc version. In effect, you cannot both include and because both define a "struct termios" which may or maynot be the same. We want our "struct termios" here to be the libc version (as defined in ), because that's what our callers use. As a result we cannot get the definion of "struct termios2" from the above header files, since this would also bring-in the clashing definition of the kernel version of "struct termios". If you have an idea for a better way out of this mess, I would REALLY like to hear it. I hope that soon GLIBC will pick-up termios2 and all these will be useless. Until then ... ATTENTION: For most architectures "struct termios2" and the associated constants we care about (NCCS, BOTHER, IBSHIFT) are the same. For some there are small differences, and some architectures do not support termios2 at all. I don't claim to have done a thorough job figuring out the specifics for every architecture, so your milleage may vary. In any case, if you want support for something that's missing, just copy the relevant definitions from the kernel header file in here, recompile, test, and send me a patch. */ #if defined (__alpha__) #error "Architecure has no termios2 support" #elif defined (__powerpc__) || defined (__powerpc64__) #define K_NCCS 19 /* The "old" termios is the same with termios2 for powerpc's */ struct termios2 { tcflag_t c_iflag; /* input mode flags */ tcflag_t c_oflag; /* output mode flags */ tcflag_t c_cflag; /* control mode flags */ tcflag_t c_lflag; /* local mode flags */ cc_t c_cc[K_NCCS]; /* control characters */ cc_t c_line; /* line discipline */ speed_t c_ispeed; /* input speed */ speed_t c_ospeed; /* output speed */ }; #define BOTHER 00037 #define IBSHIFT 16 /* powerpc ioctl numbers have the argument-size encoded. Make sure we use the correct structure (i.e. kernel termios, not LIBC termios) when calculating them. */ #define IOCTL_SETS _IOW('t', 20, struct termios2) #define IOCTL_SETSW _IOW('t', 21, struct termios2) #define IOCTL_SETSF _IOW('t', 22, struct termios2) #define IOCTL_GETS _IOR('t', 19, struct termios2) #elif defined (__mips__) #define K_NCCS 23 struct termios2 { tcflag_t c_iflag; /* input mode flags */ tcflag_t c_oflag; /* output mode flags */ tcflag_t c_cflag; /* control mode flags */ tcflag_t c_lflag; /* local mode flags */ cc_t c_line; /* line discipline */ cc_t c_cc[K_NCCS]; /* control characters */ speed_t c_ispeed; /* input speed */ speed_t c_ospeed; /* output speed */ }; #define BOTHER CBAUDEX #define IBSHIFT 16 #define IOCTL_SETS TCSETS2 #define IOCTL_SETSW TCSETSW2 #define IOCTL_SETSF TCSETSF2 #define IOCTL_GETS TCGETS2 #else /* All others */ #define K_NCCS 19 struct termios2 { tcflag_t c_iflag; /* input mode flags */ tcflag_t c_oflag; /* output mode flags */ tcflag_t c_cflag; /* control mode flags */ tcflag_t c_lflag; /* local mode flags */ cc_t c_line; /* line discipline */ cc_t c_cc[K_NCCS]; /* control characters */ speed_t c_ispeed; /* input speed */ speed_t c_ospeed; /* output speed */ }; #define BOTHER CBAUDEX #define IBSHIFT 16 #define IOCTL_SETS TCSETS2 #define IOCTL_SETSW TCSETSW2 #define IOCTL_SETSF TCSETSF2 #define IOCTL_GETS TCGETS2 #endif /* of architectures */ /***************************************************************************/ #endif /* of TERMBITS2_H */ /***************************************************************************/ /* * Local Variables: * mode:c * tab-width: 4 * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ picocom-2.2/termios2.c000066400000000000000000000113621277475702100147350ustar00rootroot00000000000000/* * termios2.c * * Use termios2 interface to set custom baud rates to serial ports. * * by Nick Patavalis (npat@efault.net) * * ATTENTION: Linux-specific kludge! * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA */ #ifndef __linux__ #error "Linux specific code!" #endif /* of __linux__ */ #include #include #include #include #include /* Contains the definition of the termios2 structure and some related constants that we should normally include from system headers. Unfortunatelly, we can't. See comments in "termbits2.h" for more. */ #include "termbits2.h" /* GLIBC termios use an (otherwise unused) bit in c_iflags to internally record the fact that ispeed was set to zero (which is special behavior and means "same as ospeed". We want to clear this bit before passing c_iflags back to the kernel. See: /sysdeps/unix/sysv/linux/speed.c */ #define IBAUD0 020000000000 int tc2setattr(int fd, int optional_actions, const struct termios *tios) { struct termios2 t2; int cmd; switch (optional_actions) { case TCSANOW: cmd = IOCTL_SETS; break; case TCSADRAIN: cmd = IOCTL_SETSW; break; case TCSAFLUSH: cmd = IOCTL_SETSF; break; default: errno = EINVAL; return -1; } t2.c_iflag = tios->c_iflag & ~IBAUD0; t2.c_oflag = tios->c_oflag; t2.c_cflag = tios->c_cflag; t2.c_lflag = tios->c_lflag; t2.c_line = tios->c_line; t2.c_ispeed = tios->c_ispeed; t2.c_ospeed = tios->c_ospeed; memcpy(&t2.c_cc[0], &tios->c_cc[0], K_NCCS * sizeof (cc_t)); return ioctl(fd, cmd, &t2); } int tc2getattr(int fd, struct termios *tios) { struct termios2 t2; size_t i; int r; r = ioctl(fd, IOCTL_GETS, &t2); if (r < 0) return r; tios->c_iflag = t2.c_iflag; tios->c_oflag = t2.c_oflag; tios->c_cflag = t2.c_cflag; tios->c_lflag = t2.c_lflag; tios->c_line = t2.c_line; tios->c_ispeed = t2.c_ispeed; tios->c_ospeed = t2.c_ospeed; memcpy(&tios->c_cc[0], &t2.c_cc[0], K_NCCS * sizeof (cc_t)); for (i = K_NCCS; i < NCCS; i++) tios->c_cc[i] = _POSIX_VDISABLE; return 0; } /* The termios2 interface supports separate input and output speeds. GLIBC's termios support only one terminal speed. So the standard tcsetispeed(3), actually sets the output-speed field, not the input-speed field (or does nothing if speed == B0). Use cf2setispeed if you want to set a *standard* input speed (one of the Bxxxxx speeds) that may be different from the output speed. Also if someone, somehow, has set the input speed to something other than B0, then you *must* use cf2setispeed() to change it. Using the standard cfsetispeed() obviously won't do (since it affects only the output-speed field). */ int cf2setispeed(struct termios *tios, speed_t speed) { if ( (speed & ~CBAUD) != 0 && (speed < B57600 || speed > __MAX_BAUD) ) { errno = EINVAL; return -1; } tios->c_ispeed = speed; tios->c_cflag &= ~((CBAUD | CBAUDEX) << IBSHIFT); tios->c_cflag |= (speed << IBSHIFT); return 0; } speed_t cf2getispeed(struct termios *tios) { return (tios->c_cflag >> IBSHIFT) & (CBAUD | CBAUDEX); } /* Use these to set custom input or output speeds (i.e. speeds that do not necessarily correspond to one of the Bxxx macros. */ int cf2setospeed_custom(struct termios *tios, int speed) { if ( speed <= 0 ) { errno = EINVAL; return -1; } tios->c_cflag &= ~(CBAUD | CBAUDEX); tios->c_cflag |= BOTHER; tios->c_ospeed = speed; return 0; } int cf2setispeed_custom(struct termios *tios, int speed) { if ( speed < 0 ) { errno = EINVAL; return -1; } if ( speed == 0 ) { /* Special case: ispeed == 0 means "same as ospeed". Kernel does this if it sees B0 in the "CIBAUD" field (i.e. in CBAUD << IBSHIFT) */ tios->c_cflag &= ~((CBAUD | CBAUDEX) << IBSHIFT); tios->c_cflag |= (B0 << IBSHIFT); } else { tios->c_cflag &= ~((CBAUD | CBAUDEX) << IBSHIFT); tios->c_cflag |= (BOTHER << IBSHIFT); tios->c_ispeed = speed; } return 0; } /***************************************************************************/ /* * Local Variables: * mode:c * tab-width: 4 * c-basic-offset: 4 * End: */ picocom-2.2/termios2.h000066400000000000000000000056561277475702100147530ustar00rootroot00000000000000/* * termios2.h * * Use termios2 interface to set custom baud rates to serial ports. * * by Nick Patavalis (npat@efault.net) * * ATTENTION: Linux-specific kludge! * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA */ #ifndef TERMIOS2_H #define TERMIOS2_H #include /* Replace termios functions, with termios2 functions */ #define tcsetattr tc2setattr #define tcgetattr tc2getattr #define cfsetispeed cf2setispeed #define cfgetispeed cf2getispeed /* And define these new ones */ #define cfsetospeed_custom cf2setospeed_custom #define cfsetispeed_custom cf2setispeed_custom #define cfgetospeed_custom(tiop) ((tiop)->c_ospeed) #define cfgetispeed_custom(tiop) ((tiop)->c_ispeed) /* Replacements for the standard tcsetattr(3), tcgetattr(3) * functions. Same user interface, but these use the new termios2 * kernel interface (new ioctl's) which allow custom baud-rate * setting. */ int tc2setattr(int fd, int optional_actions, const struct termios *tios); int tc2getattr(int fd, struct termios *tios); /* Replacements for the standard cfgetispeed(3), cfsetispeed(3) * functions. Use these to set / get standard *input* baudrates. You * can still use cfgetospeed(3), cfsetospeed(3) to set / get the * standard output baudrates. The new termios2 interface, unlike the * old one, supports different input and output speeds for a * device. The "speed" argument must be (and the return value will be) * one of the standard "Bxxxx" macros. If cf2getispeed() or * cfgetospeed(3) return CBAUDEX, then the respective baudrate is a * custom one. Read the "termios.c_ispeed" / "termios.c_ospeed" fields * to get the custom value (as a numeric speed). */ int cf2setispeed(struct termios *tios, speed_t speed); speed_t cf2getispeed(struct termios *tios); /* Use these to set *custom* input and output baudrates for a * device. The "speed" argument must be a numeric baudrate value * (e.g. 1234 for 1234 bps). */ int cf2setispeed_custom(struct termios *tios, int speed); int cf2setospeed_custom(struct termios *tios, int speed); /***************************************************************************/ #endif /* of TERMIOS2_H */ /***************************************************************************/ /* * Local Variables: * mode:c * tab-width: 4 * c-basic-offset: 4 * End: */ picocom-2.2/termios2.txt000066400000000000000000000151571277475702100153400ustar00rootroot00000000000000 Linux and custom serial-port baudrates (the gory details) ========================================================= Support for custom baudrate setting and reading in Linux is done through the "new" "termios2" terminal-attributes structure, and the respective ioctls: TCSETS2, TCSETSW2, TCSETSF2, and TCGETS2. The "termios2" structure is defined in: /arch//include/uapi/asm/termbits.h or /include/uapi/asm-generic/termbits.h which may have been coppied in your system headers directories as: /usr/include//asm/termbits.h or /usr/include/asm-generic/termbits.h The termios2 structure looks like this: #define NCCS 19 struct termios2 { tcflag_t c_iflag; /* input mode flags */ tcflag_t c_oflag; /* output mode flags */ tcflag_t c_cflag; /* control mode flags */ tcflag_t c_lflag; /* local mode flags */ cc_t c_line; /* line discipline */ cc_t c_cc[NCCS]; /* control characters */ speed_t c_ispeed; /* input speed */ speed_t c_ospeed; /* output speed */ }; In the same files you will also find defined some relevant macros (constants, flags, and bit-fields, for the "termios2" "c_*flag" fields). Important aside: Unfortunatelly, we cannot include the above-mentioned files in our code, since they clash badly with stuff defined in the LIBC-provided header files (where the user-visible "termios" structure is defined). Because of this clash between LIBC and the linux headers, we have to manually copy the "termios2" definition (and a few relevant constants) into our sources for the whole thing to work. This is definitely very klugy, but I can see no better way to make it work (after all, GLIBC does the same thing---replicates the kernel definitions itself---for the older "termios" interface). End aside, on with it... The new ioctls TCSETS2, TCSETSW2, TCSETSF2 pass a "termios2" structure to the kernel in order to set the tty attributes (among which the serial port's baudrate). The corresponding TCGETS2 ioctl retrieves a "termios2" structure from the kernel corresponding to the actual, effective tty settings. These ioctls are used by the "tc2setattr()" and "tc2getattr()" functions (see file "termios2.c"). These functions are passed a userspace, LIBC-defined "termios" structure (which is very similar, but not necessarily identical to the kernel's "termios2"), call the respective ioctls, and copy the relevant information to or from the "termios2" structure (as expected or returned by the kernel). The game between the kernel and the "termios2" structure, regarding how baudrate-related information is interpretted, is played like this: Bits "c_cflags & (CBAUD | CBAUDEX)" (they are sometimes called: "the CBAUD field"), together with field "c_ospeed" control the output baudrate. Bits "c_cflags & ((CBAUD | CBAUDEX) << IBSHIFT)" (they are sometimes called: "the CIBAUD field"), together with field "c_ispeed" control the input baudrate. BTW: Usually CBAUD & CBAUDEX == CBAUD. That is, CBAUDEX *is* one of the CBAUD bits. When issuing one of the TCSETS*2 ioctls, everything contained in the "termios2" structure is copied to a kernel-resident structure and the respective serial driver is notified. Upon the serial driver's request, the kernel determines the output baudrate. If the kernel sees that: c_cflag & CBAUD == BOTHER then "c_ospeed" is passed to the serial driver as the output baudrate. Otherwise "c_cflag & (CBAUD | CBAUDEX)" is matched against a table of standard baudrates (coresponding to the "Bxxxx" macros). The matching baudrate is located and passed to the serial driver as the output baudrate. You can see the respective code in "drivers/tty/tty_ioctl.c" (all files form now on relative to the linux-kernel source tree base), function "tty_termios_baud_rate()", which is what the serial drivers call to determine the output baudrate. If the driver requests it, the kernel also determines the input baudrate by checking (c_cflag >> IBSHIFT) & CBAUD. If it sees that: (c_cflag >> IBSHIFT) & CBAUD == B0 then the *output* baudrate is passed to the driver (as the input baudrate) determined as described above. If, on the other hand the kernel sees that: (c_cflag >> IBSHIFT) & CBAUD == BOTHER then "c_ispeed" is passed to the serial driver as the input baudrate. If, finaly, neither is true (i.e. the input baudate bits in "c_cflag" are neither B0, nor BOTHER), then "(c_cflag >> IBSHIFT) & (CBAUD|CBAUDEX)" is mached against the table of standard baudrates. The matching baudrate is located and passed to the serial driver as the input baudrate. You can see all these happen in "drivers/tty/tty_ioctl.c", function "tty_termios_input_baud_rate()", which is what the serial drivers call to determine the input baudrate. The serial driver, once it receives the requested baudrate values, it may choose to alter them (e.g because the requested values are not supported by the hardware). If it does so, it then passes-back to the kernel the actual, effective, baudrate values, so that the kernel can update its internal structure. As a result, the user will read the *effective* baudrate values with the next TCGETS2 ioctl (which may be different than the requested ones). The kernel updates the "termios2" structure with the effective baudrate values supplied by the serial driver by following a rather complicated procedure, which I will not describe in full detail here. The gist of it is this: If the user has requested a baudrate using one of the standard "Bxxx" values (i.e. by setting the CBAUD / CIBAUD fields in "c_cflag"), then the kernel will also try to report-back the effective baudrate as a standard "Bxxx" value (by setting the CBAUF / CIBAUD fields in "c_cflag"), *even* if it has to lie a little about the baudrate value. If lying "a little" is not enough, or if the user has requested a non-standard baudrate through "c_ispeed / c_ospeed", then the kernel will set the CBAUD / CIBAUD fields in "c_cflag" to BOTHER, and report the effective baudrate (numerically) using "c_ispeed" / "c_ospeed". Actually, the "c_ispeed" / "c_ospeed" fields are *always* updated by the kernel with the effective baudrate values, even if these values are also reported by setting the CBAUD / CIBAUD fields in "c_cflag" to one of the "Bxxx" values. The details of how the kernel updates the termios2 structure with the baudrate values supplied by the serial drivers can be seen in file "drivers/tty/tty_ioctl.c", function "tty_termios_encode_baud_rate()" which is what the serial drivers call to notify the kernel about the effective baudrate.