pax_global_header00006660000000000000000000000064150447075440014524gustar00rootroot0000000000000052 comment=222237ec402234657514f37c8ef369108478fb5a plptools-1.0.26/000077500000000000000000000000001504470754400134665ustar00rootroot00000000000000plptools-1.0.26/.editorconfig000066400000000000000000000002021504470754400161350ustar00rootroot00000000000000root = true [*] charset = utf-8 indent_style = space indent_size = 8 trim_trailing_whitespace = true insert_final_newline = true plptools-1.0.26/.github/000077500000000000000000000000001504470754400150265ustar00rootroot00000000000000plptools-1.0.26/.github/workflows/000077500000000000000000000000001504470754400170635ustar00rootroot00000000000000plptools-1.0.26/.github/workflows/c-cpp.yml000066400000000000000000000021431504470754400206100ustar00rootroot00000000000000name: C/C++ CI on: [ push, pull_request ] jobs: build: strategy: matrix: os: [ubuntu-latest, macos-latest] include: - os: ubuntu-latest env: ASAN: "yes" shell: bash - os: macos-latest shell: bash runs-on: ${{ matrix.os }} defaults: run: shell: ${{ matrix.shell }} {0} steps: - uses: actions/checkout@v4 with: { submodules: true } - name: Install dependencies (Ubuntu) if: ${{ matrix.os == 'ubuntu-latest' }} run: sudo apt-get -y install libreadline-dev pkg-config gettext autopoint libfuse-dev libattr1-dev - name: Install dependencies (macOS) if: ${{ matrix.os == 'macos-latest' }} run: | brew install coreutils readline pkg-config libtool automake gettext macfuse echo "$(brew --prefix m4)/bin:/usr/local/opt/gettext/bin" >> $GITHUB_PATH echo "LDFLAGS=-L$(brew --prefix readline)/lib" >> "$GITHUB_ENV" echo "CPPFLAGS=-I$(brew --prefix readline)/include" >> "$GITHUB_ENV" - name: Build run: ./build-aux/build.sh plptools-1.0.26/.gitignore000066400000000000000000000005301504470754400154540ustar00rootroot00000000000000*~ *.o Makefile.in Makefile .deps .libs /aclocal.m4 /autom4te.cache /libgnu /configure /config.guess /config.sub /libtool /config.status /config.status.lineno /config.cache /config.log /config.rpath /config.h /config.h.in /stamp-h.in /stamp-h1 /plptools-*.tar.gz /ABOUT-NLS /ChangeLog /compile /depcomp /missing /install-sh /ltmain.sh /INSTALL plptools-1.0.26/.gitmodules000066400000000000000000000002721504470754400156440ustar00rootroot00000000000000[submodule "gnulib"] path = gnulib url = git://git.sv.gnu.org/gnulib.git [submodule "gl-mod/bootstrap"] path = gl-mod/bootstrap url = https://github.com/gnulib-modules/bootstrap.git plptools-1.0.26/AUTHORS000066400000000000000000000000361504470754400145350ustar00rootroot00000000000000See sources and documentation.plptools-1.0.26/COPYING000066400000000000000000000432541504470754400145310ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. plptools-1.0.26/ChangeLog000066400000000000000000000054201504470754400152410ustar00rootroot00000000000000Changes since Version 0.9 See release notes for each version, and git history. Changes from Version 0.7 to 0.8 - Fixed lot of bugs, especially in nfs code - Series 3 _should_ work again. - Added support for KDE (KDE >= 2.1) - Added KDE Application "kpsion" Changes from Version 0.6 to 0.7 - Changed timezone-stuff again. With S5, now the machine-info is evaluated. This info holds the Psion's time-offset so that we are able to calculate the offset correctly, regardless of the Psion's setting. All that is done in a new class PsiTime. For S3, a Fallback is provided using an Env-Variable "PSI_TZ" which can be set to the Psion's time-offset in seconds. - Added lots of rpc related stuff: - Implemented a procfs-like subdir in plpnfsd. Processes can be examined and misc. parameters can be examined/changed using the entries in /proc/ Several parameters can now be changed on the fly: attribute-cache timeout in seconds: proc/acache (rw) debuglevel: proc/debuglevel (rw) directory-cache timeout in seconds: proc/dcache (rw) Psion's owner-info: proc/owner (ro) Unix-owner of mounted directory: proc/unixowner (rw) Stopping of plpnfsd has now changed: Old: reference /mnt/psion/exit New: echo stop > /mnt/psion/proc/exit This should be safer than accidentally referencing a file. For every process running on the psion, a subdir in proc/ is created with two files "cmd" and "args" (both ro). cmd is the process-name, args is its commandline. - Added machinfo command in plpftp for displaying lot of interesting information about S5. (E.g. battery status etc.) - Added killsave and runrestore to plpftpd. Both take a unix file as argument. killsave kills all processes on the Psion and saves restart-information to the specified file. runrestore reads a file created by killsave and starts all processes saved in this file. -> Functionality similar to the behavior of PsiWin before/after backup. Changes from Version 0.5 to 0.6: - Maintenance release: bugfixes in plpnfsd and libs which make the mounted device writable again. Changes from Version 0.4 to 0.5: - merged all stuff from Matt Gumbley's plptools-0.4-mjg5 see his README.mjg - Added a speedup-patch for plpnfsd from Rudol Koenig. This should also solve Olaf Flebbe's problems as a side effect. - Added command- and filename-completion to plpftp - Changed plpftp's default-drive to "AUTO". This triggers auto-detection of available drives and selecting the first one. - fixed various bugs in plpftp - added hash printing in plpftp. - Added new commands "ps", "kill" and "run" to plpftp (these should work at least for Series5. For Series3: INCOMPLETE) plptools-1.0.26/HISTORY000066400000000000000000000034221504470754400145530ustar00rootroot00000000000000Fritz Elfert wrote plptools, heavily based on two other packages: - p3nfsd-5.4 by Rudolf Koenig (rfkoenig@immd4.informatik.uni-erlangen.de) an nfs daemon for Psion series 3 and 5 - plp_1_7 by Philip Proudman (phil@proudman51.freeserve.co.uk) He writes: I want to thank both authors for their nice packages and for making these available under GPL which makes it possible for me to create this package (which of course is distributed under GPL as well). I modified and improved Philip's code to support more (nearly all?) rfsv32 commands and to gain speed (approx. 4 times) and stability (properly reconnects on lost serial link). Rudolf's code originally used a homegrown protocol which needs a separate program running on the Psion. I changed his code to use the socket-based client-code from Philip's rfsv program thus eliminating the need for any software beeing run on the Psion. The rfsv code and some other stuff went into a lib (both shared and static available). I also removed all Psion 3 related stuff, because a) I can't test it and b) Psion no longer supported SIBO. Support for the RPC mechanism was added in version 0.5. This support was only possible because of Alexander Thoukydides' excellent documentation: https://thoukydides.github.io/riscos-psifs/plp.html Thanks a lot for that. Psion 3 support was added by Matt Gumbley (matt@gumbley.demon.co.uk). Have fun. -Fritz Reuben Thomas has maintained plptools since version 0.13. He fixed many bugs, updated the KDE utilities to work with KDE 3, but finally removed them with the approach of KDE 4, enhanced plpftp and replaced the rather flaky plpnfsd with plpfuse. He has kept the build system working with newer versions of GNU autotools, and gradually cleaned up the code and simplified the build system. plptools-1.0.26/Makefile.am000066400000000000000000000032371504470754400155270ustar00rootroot00000000000000# Top level Makefile.am. # # This file is part of plptools. # # Copyright (C) 1999-2002 Fritz Elfert # Copyright (C) 2007-2025 Reuben Thomas # # 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 # along with this program; if not, see . ACLOCAL_AMFLAGS = -I m4 SUBDIRS = po libgnu lib ncpd plpftp plpprint sisinstall doc if BUILD_PLPFUSE SUBDIRS += plpfuse endif EXTRA_DIST = AUTHORS COPYING INSTALL NEWS README TODO HISTORY ABOUT-NLS \ etc/plptools.in \ etc/ttytap.c etc/psidump etc/psidump.1 \ etc/udev-usbserial-plptools.rules \ m4/gnulib-cache.m4 DISTCLEANFILES = etc/plptools uninstall-local: rm -f $(DESTDIR)$(initdir)/plptools install-exec-local: $(INSTALL) -d $(DESTDIR)$(initdir) $(INSTALL) $(top_builddir)/etc/plptools $(DESTDIR)$(initdir)/plptools # FIXME: Automatically revert .po files if they're actually clean # Currently, rather than run `git diff --exit-code', we simply show the # diff, and eyeball it to check there's nothing other than the datestamps on # the .po files. release: distcheck git diff && \ gh release create v$(VERSION) --title "Release v$(VERSION)" $(DIST_ARCHIVES) plptools-1.0.26/NEWS000066400000000000000000000000221504470754400141570ustar00rootroot00000000000000See release notes.plptools-1.0.26/README000077700000000000000000000000001504470754400156202README.mdustar00rootroot00000000000000plptools-1.0.26/README.md000066400000000000000000000054511504470754400147520ustar00rootroot00000000000000# plptools https://github.com/plptools/plptools/ plptools is a suite of programs for transferring files to and from EPOC (Psion) devices, as well as backing them up, installing software, and setting the clock. See below for build instructions and HISTORY for some history. plptools is free software; you can redistribute 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. See the man pages for documentation: ncpd(8), plpftp(1), sisinstall(1), plpprintd(8), and, where installed, plpfuse(8). ## Building from source To build plpfuse, the following packages are required: * GNU Make * FUSE: https://github.com/libfuse/libfuse (MacFUSE on macOS) * libattr: https://savannah.nongnu.org/projects/attr (not required on macOS or BSD) For command-line editing and history support in plpftp, Readline 4.3 or later or a compatible library is required. Providing detailed instructions on how to install these packages for different operating systems is beyond the scope of this README, but see the [GitHub CI workflow](.github/workflows/c-cpp.yml) for the necessary install and configuration steps for Ubuntu/Debian and macOS. If building from a git checkout, you’ll need the following packages installed: automake, autoconf, pkg-config. Then run: ``` ./bootstrap --skip-po ``` Some extra packages are needed; `bootstrap` will tell you what you need to install if anything is lacking. plptools uses GNU autotools, so the usual sequence of commands works: ``` ./configure make make install ``` In addition to the usual options, `configure` understands the following:
--with-serial=/dev/sometty
sets the default serial device for ncpd. Without this option, ncpd tries automagically to find a serial device.
--with-speed=baudrate
sets the default serial speed (normally 115200 baud).
--with-port=portnum
sets the default port on which ncpd listens and to which plpftp and plpfuse connect (default 7501).
--with-drive=drivespec
sets the default drive for plpftp. The default AUTO triggers a drive-scan on the psion and sets the drive to the first drive found. If you don't want that, specify C: for example.
--with-basedir=dirspec
overrides the default directory for plpftp. The default is \, which means the root directory. Note: since backslashes need to be doubled once for C escaping and once for shell escaping, this value is actually supplied as \\\\.
## Information for developers The git repository can be cloned with: git clone https://github.com/plptools/plptools.git To make a release you need gh: https://cli.github.com plptools-1.0.26/TODO000066400000000000000000000005521504470754400141600ustar00rootroot00000000000000ncpd: getty-style locking of the serial-line in a manner that it can be used with other devices/programs without stopping/starting ncpd all the time. plpprintd: Fix pattern stuff Sisinstall: - Use rpm-style arguments. - Check requisite records. - Check file crc. - Create a unique name of the residual sis file, if the provided name already exists. plptools-1.0.26/bootstrap000077500000000000000000004664041504470754400154470ustar00rootroot00000000000000#! /bin/sh ## DO NOT EDIT - This file generated from build-aux/bootstrap.in ## by inline-source v2019-02-19.15 # Bootstrap an Autotooled package from checked-out sources. # Written by Gary V. Vaughan, 2010 # Inspired by a script written by Paul Eggert. # This is free software. There is NO warranty; not even for # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # # Copyright (C) 2010-2019, 2021, 2023-2024 Bootstrap Authors # # This file is dual licensed under the terms of the MIT license # , and GPL version 2 or later # . You must apply one of # these licenses when using or redistributing this software or any of # the files within it. See the URLs above, or the file `LICENSE` # included in the Bootstrap distribution for the full license texts. # You should place a copy of this script under version control in the # top-level directory of your project. The intent is that all # customization can be done with a `bootstrap.conf` file also maintained # in your version control. # Please report bugs or propose patches to: # ## ------ ## ## Usage. ## ## ------ ## # Most GNUish projects do not keep all of the generated Autotool # files under version control, but running all of the right tools # with the right arguments, in the correct order to regenerate # all of those files in readiness for configuration and building # can be surprisingly involved! Many projects have a 'bootstrap' # script under version control to invoke Autotools and perform # other assorted book-keeping with version numbers and the like. # # This bootstrap script aims to probe the configure.ac and top # Makefile.am of your project to automatically determine what # the correct ordering and arguments are and then run the tools for # you. In order to use it, you can generate an initial standalone # script with: # # gl/build-aux/inline-source gl/build-aux/bootstrap.in > bootstrap # # You should then store than script in version control for other # developers in your project. It will give you instructions about # how to keep it up to date if the sources change. # # See gl/doc/bootstrap.texi for documentation on how to write # a bootstrap.conf to customize it for your project's # idiosyncracies. ## ================================================================== ## ## ## ## DO NOT EDIT THIS FILE, CUSTOMIZE IT USING A BOOTSTRAP.CONF ## ## ## ## ================================================================== ## ## ------------------------------- ## ## User overridable command paths. ## ## ------------------------------- ## # All uppercase denotes values stored in the environment. These # variables should generally be overridden by the user - however, we do # set them to 'true' in some parts of this script to prevent them being # called at the wrong time by other tools that we call ('autoreconf', # for example). # # We also allow 'LIBTOOLIZE', 'M4', 'SHA1SUM' and some others to be # overridden, and export the result for child processes, but they are # handled by the function 'func_find_tool' and not defaulted in this # section. : ${ACLOCAL="aclocal"} : ${AUTOCONF="autoconf"} : ${AUTOHEADER="autoheader"} : ${AUTOM4TE="autom4te"} : ${AUTOHEADER="autoheader"} : ${AUTOMAKE="automake"} : ${AUTOPOINT="autopoint"} : ${AUTORECONF="autoreconf"} : ${CMP="cmp"} : ${CONFIG_SHELL="/bin/sh"} : ${DIFF="diff"} : ${GIT="git"} : ${LN_S="ln -s"} : ${RM="rm"} export ACLOCAL export AUTOCONF export AUTOHEADER export AUTOM4TE export AUTOHEADER export AUTOMAKE export AUTOPOINT export AUTORECONF export CONFIG_SHELL ## -------------- ## ## Configuration. ## ## -------------- ## # A newline delimited list of triples of programs (that respond to # --version), the minimum version numbers required (or just '-' in the # version field if any version will be sufficient) and homepage URLs # to help locate missing packages. buildreq= # Name of a file containing instructions on installing missing packages # required in 'buildreq'. buildreq_readme=README-hacking # These are extracted from AC_INIT in configure.ac, though you can # override those values in 'bootstrap.conf' if you prefer. build_aux= macro_dir= package= package_name= package_version= package_bugreport= # These are extracted from 'gnulib-cache.m4', or else fall-back # automatically on the gnulib defaults; unless you set the values # manually in 'bootstrap.conf'. doc_base= gnulib_mk= gnulib_name= local_gl_path= source_base= tests_base= # The list of gnulib modules required at 'gnulib-tool' time. If you # check 'gnulib-cache.m4' into your repository, then this list will be # extracted automatically. gnulib_modules= # Extra gnulib files that are not in modules, which override files of # the same name installed by other bootstrap tools. gnulib_non_module_files=" build-aux/compile build-aux/install-sh build-aux/mdate-sh build-aux/texinfo.tex build-aux/depcomp build-aux/config.guess build-aux/config.sub doc/INSTALL " # Relative path to the local gnulib submodule, and url to the upstream # git repository. If you have a gnulib entry in your .gitmodules file, # these values are ignored. gnulib_path= gnulib_url= # Date from which to clone github, to avoid a full clone. gnulib_clone_since= # Additional gnulib-tool options to use. gnulib_tool_options=" --no-changelog " # bootstrap removes any macro-files that are not included by aclocal.m4, # except for files listed in this variable that are always kept. gnulib_precious=" gnulib-tool.m4 " # When truncating long commands for display, always allow at least this # many characters before truncating. min_cmd_len=160 # The command to download all .po files for a specified domain into # a specified directory. Fill in the first %s is the domain name, and # the second with the destination directory. Use rsync's -L and -r # options because the latest/%s directory and the .po files within are # all symlinks. po_download_command_format=\ "rsync --delete --exclude '*.s1' -Lrtvz \ 'translationproject.org::tp/latest/%s/' '%s'" # Other locale categories that need message catalogs. extra_locale_categories= # Additional xgettext options to use. Gnulib might provide you with an # extensive list of additional options to append to this, but gettext # 0.16.1 and newer appends them automaticaly, so you can safely ignore # the complaints from 'gnulib-tool' if your $configure_ac states: # # AM_GNU_GETTEXT_VERSION([0.16.1]) xgettext_options=" --flag=_:1:pass-c-format --flag=N_:1:pass-c-format " # Package copyright holder for gettext files. Defaults to FSF if unset. copyright_holder= # File that should exist in the top directory of a checked out hierarchy, # but not in a distribution tarball. checkout_only_file= # Whether to use copies instead of symlinks by default (if set to true, # the --copy option has no effect). copy=false # Set this to ".cvsignore .gitignore" in 'bootstrap.conf' if you want # those files to be generated in directories like 'lib/', 'm4/', and 'po/', # or set it to "auto" to make this script select what to use based # on what version control system (if any) is used in the source directory. # Or set it to "none" to ignore VCS ignore files entirely. Default is # "auto". vc_ignore= ## ------------------- ## ## External Libraries. ## ## ------------------- ## # Source required external libraries: # Set a version string for this script. scriptversion=2019-02-19.15; # UTC # General shell script boiler plate, and helper functions. # Written by Gary V. Vaughan, 2004 # This is free software. There is NO warranty; not even for # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # # Copyright (C) 2004-2019, 2021, 2023-2024 Bootstrap Authors # # This file is dual licensed under the terms of the MIT license # , and GPL version 2 or later # . You must apply one of # these licenses when using or redistributing this software or any of # the files within it. See the URLs above, or the file `LICENSE` # included in the Bootstrap distribution for the full license texts. # Please report bugs or propose patches to: # ## ------ ## ## Usage. ## ## ------ ## # Evaluate this file near the top of your script to gain access to # the functions and variables defined here: # # . `echo "$0" | ${SED-sed} 's|[^/]*$||'`/build-aux/funclib.sh # # If you need to override any of the default environment variable # settings, do that before evaluating this file. ## -------------------- ## ## Shell normalisation. ## ## -------------------- ## # Some shells need a little help to be as Bourne compatible as possible. # Before doing anything else, make sure all that help has been provided! DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in *posix*) set -o posix ;; esac fi # NLS nuisances: We save the old values in case they are required later. _G_user_locale= _G_safe_locale= for _G_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES do eval "if test set = \"\${$_G_var+set}\"; then save_$_G_var=\$$_G_var $_G_var=C export $_G_var _G_user_locale=\"$_G_var=\\\$save_\$_G_var; \$_G_user_locale\" _G_safe_locale=\"$_G_var=C; \$_G_safe_locale\" fi" done # These NLS vars are set unconditionally (bootstrap issue #24). Unset those # in case the environment reset is needed later and the $save_* variant is not # defined (see the code above). LC_ALL=C LANGUAGE=C export LANGUAGE LC_ALL # Make sure IFS has a sensible default sp=' ' nl=' ' IFS="$sp $nl" # There are apparently some systems that use ';' as a PATH separator! if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # func_unset VAR # -------------- # Portably unset VAR. # In some shells, an 'unset VAR' statement leaves a non-zero return # status if VAR is already unset, which might be problematic if the # statement is used at the end of a function (thus poisoning its return # value) or when 'set -e' is active (causing even a spurious abort of # the script in this case). func_unset () { { eval $1=; (eval unset $1) >/dev/null 2>&1 && eval unset $1 || : ; } } # Make sure CDPATH doesn't cause `cd` commands to output the target dir. func_unset CDPATH # Make sure ${,E,F}GREP behave sanely. func_unset GREP_OPTIONS ## ------------------------- ## ## Locate command utilities. ## ## ------------------------- ## # func_executable_p FILE # ---------------------- # Check that FILE is an executable regular file. func_executable_p () { test -f "$1" && test -x "$1" } # func_path_progs PROGS_LIST CHECK_FUNC [PATH] # -------------------------------------------- # Search for either a program that responds to --version with output # containing "GNU", or else returned by CHECK_FUNC otherwise, by # trying all the directories in PATH with each of the elements of # PROGS_LIST. # # CHECK_FUNC should accept the path to a candidate program, and # set $func_check_prog_result if it truncates its output less than # $_G_path_prog_max characters. func_path_progs () { _G_progs_list=$1 _G_check_func=$2 _G_PATH=${3-"$PATH"} _G_path_prog_max=0 _G_path_prog_found=false _G_save_IFS=$IFS; IFS=${PATH_SEPARATOR-:} for _G_dir in $_G_PATH; do IFS=$_G_save_IFS test -z "$_G_dir" && _G_dir=. for _G_prog_name in $_G_progs_list; do for _exeext in '' .EXE; do _G_path_prog=$_G_dir/$_G_prog_name$_exeext func_executable_p "$_G_path_prog" || continue case `"$_G_path_prog" --version 2>&1` in *GNU*) func_path_progs_result=$_G_path_prog _G_path_prog_found=: ;; *) $_G_check_func $_G_path_prog func_path_progs_result=$func_check_prog_result ;; esac $_G_path_prog_found && break 3 done done done IFS=$_G_save_IFS test -z "$func_path_progs_result" && { echo "no acceptable sed could be found in \$PATH" >&2 exit 1 } } # We want to be able to use the functions in this file before configure # has figured out where the best binaries are kept, which means we have # to search for them ourselves - except when the results are already set # where we skip the searches. # Unless the user overrides by setting SED, search the path for either GNU # sed, or the sed that truncates its output the least. test -z "$SED" && { _G_sed_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ for _G_i in 1 2 3 4 5 6 7; do _G_sed_script=$_G_sed_script$nl$_G_sed_script done echo "$_G_sed_script" 2>/dev/null | sed 99q >conftest.sed _G_sed_script= func_check_prog_sed () { _G_path_prog=$1 _G_count=0 printf 0123456789 >conftest.in while : do cat conftest.in conftest.in >conftest.tmp mv conftest.tmp conftest.in cp conftest.in conftest.nl echo '' >> conftest.nl "$_G_path_prog" -f conftest.sed conftest.out 2>/dev/null || break diff conftest.out conftest.nl >/dev/null 2>&1 || break _G_count=`expr $_G_count + 1` if test "$_G_count" -gt "$_G_path_prog_max"; then # Best one so far, save it but keep looking for a better one func_check_prog_result=$_G_path_prog _G_path_prog_max=$_G_count fi # 10*(2^10) chars as input seems more than enough test 10 -lt "$_G_count" && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out } func_path_progs "sed gsed" func_check_prog_sed "$PATH:/usr/xpg4/bin" rm -f conftest.sed SED=$func_path_progs_result } # Unless the user overrides by setting GREP, search the path for either GNU # grep, or the grep that truncates its output the least. test -z "$GREP" && { func_check_prog_grep () { _G_path_prog=$1 _G_count=0 _G_path_prog_max=0 printf 0123456789 >conftest.in while : do cat conftest.in conftest.in >conftest.tmp mv conftest.tmp conftest.in cp conftest.in conftest.nl echo 'GREP' >> conftest.nl "$_G_path_prog" -e 'GREP$' -e '-(cannot match)-' conftest.out 2>/dev/null || break diff conftest.out conftest.nl >/dev/null 2>&1 || break _G_count=`expr $_G_count + 1` if test "$_G_count" -gt "$_G_path_prog_max"; then # Best one so far, save it but keep looking for a better one func_check_prog_result=$_G_path_prog _G_path_prog_max=$_G_count fi # 10*(2^10) chars as input seems more than enough test 10 -lt "$_G_count" && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out } func_path_progs "grep ggrep" func_check_prog_grep "$PATH:/usr/xpg4/bin" GREP=$func_path_progs_result } ## ------------------------------- ## ## User overridable command paths. ## ## ------------------------------- ## # All uppercase variable names are used for environment variables. These # variables can be overridden by the user before calling a script that # uses them if a suitable command of that name is not already available # in the command search PATH. : ${CP="cp -f"} : ${ECHO="printf %s\n"} : ${EGREP="$GREP -E"} : ${FGREP="$GREP -F"} : ${LN_S="ln -s"} : ${MAKE="make"} : ${MKDIR="mkdir"} : ${MV="mv -f"} : ${RM="rm -f"} : ${SHELL="${CONFIG_SHELL-/bin/sh}"} ## -------------------- ## ## Useful sed snippets. ## ## -------------------- ## sed_dirname='s|/[^/]*$||' sed_basename='s|^.*/||' # Sed substitution that helps us do robust quoting. It backslashifies # metacharacters that are still active within double-quoted strings. sed_quote_subst='s|\([`"$\\]\)|\\\1|g' # Same as above, but do not quote variable references. sed_double_quote_subst='s/\(["`\\]\)/\\\1/g' # Sed substitution that turns a string into a regex matching for the # string literally. sed_make_literal_regex='s|[].[^$\\*\/]|\\&|g' # Sed substitution that converts a w32 file name or path # that contains forward slashes, into one that contains # (escaped) backslashes. A very naive implementation. sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g' # Re-'\' parameter expansions in output of sed_double_quote_subst that # were '\'-ed in input to the same. If an odd number of '\' preceded a # '$' in input to sed_double_quote_subst, that '$' was protected from # expansion. Since each input '\' is now two '\'s, look for any number # of runs of four '\'s followed by two '\'s and then a '$'. '\' that '$'. _G_bs='\\' _G_bs2='\\\\' _G_bs4='\\\\\\\\' _G_dollar='\$' sed_double_backslash="\ s/$_G_bs4/&\\ /g s/^$_G_bs2$_G_dollar/$_G_bs&/ s/\\([^$_G_bs]\\)$_G_bs2$_G_dollar/\\1$_G_bs2$_G_bs$_G_dollar/g s/\n//g" # require_check_ifs_backslash # --------------------------- # Check if we can use backslash as IFS='\' separator, and set # $check_ifs_backshlash_broken to ':' or 'false'. require_check_ifs_backslash=func_require_check_ifs_backslash func_require_check_ifs_backslash () { _G_save_IFS=$IFS IFS='\' _G_check_ifs_backshlash='a\\b' for _G_i in $_G_check_ifs_backshlash do case $_G_i in a) check_ifs_backshlash_broken=false ;; '') break ;; *) check_ifs_backshlash_broken=: break ;; esac done IFS=$_G_save_IFS require_check_ifs_backslash=: } ## ----------------- ## ## Global variables. ## ## ----------------- ## # Except for the global variables explicitly listed below, the following # functions in the '^func_' namespace, and the '^require_' namespace # variables initialised in the 'Resource management' section, sourcing # this file will not pollute your global namespace with anything # else. There's no portable way to scope variables in Bourne shell # though, so actually running these functions will sometimes place # results into a variable named after the function, and often use # temporary variables in the '^_G_' namespace. If you are careful to # avoid using those namespaces casually in your sourcing script, things # should continue to work as you expect. And, of course, you can freely # overwrite any of the functions or variables defined here before # calling anything to customize them. EXIT_SUCCESS=0 EXIT_FAILURE=1 EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing. EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake. # Allow overriding, eg assuming that you follow the convention of # putting '$debug_cmd' at the start of all your functions, you can get # bash to show function call trace with: # # debug_cmd='eval echo "${FUNCNAME[0]} $*" >&2' bash your-script-name debug_cmd=${debug_cmd-":"} exit_cmd=: # By convention, finish your script with: # # exit $exit_status # # so that you can set exit_status to non-zero if you want to indicate # something went wrong during execution without actually bailing out at # the point of failure. exit_status=$EXIT_SUCCESS # Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh # is ksh but when the shell is invoked as "sh" and the current value of # the _XPG environment variable is not equal to 1 (one), the special # positional parameter $0, within a function call, is the name of the # function. progpath=$0 # The name of this program. progname=`$ECHO "$progpath" |$SED "$sed_basename"` # Make sure we have an absolute progpath for reexecution: case $progpath in [\\/]*|[A-Za-z]:\\*) ;; *[\\/]*) progdir=`$ECHO "$progpath" |$SED "$sed_dirname"` progdir=`cd "$progdir" && pwd` progpath=$progdir/$progname ;; *) _G_IFS=$IFS IFS=${PATH_SEPARATOR-:} for progdir in $PATH; do IFS=$_G_IFS test -x "$progdir/$progname" && break done IFS=$_G_IFS test -n "$progdir" || progdir=`pwd` progpath=$progdir/$progname ;; esac ## ----------------- ## ## Standard options. ## ## ----------------- ## # The following options affect the operation of the functions defined # below, and should be set appropriately depending on run-time para- # meters passed on the command line. opt_dry_run=false opt_quiet=false opt_verbose=false # Categories 'all' and 'none' are always available. Append any others # you will pass as the first argument to func_warning from your own # code. warning_categories= # By default, display warnings according to 'opt_warning_types'. Set # 'warning_func' to ':' to elide all warnings, or func_fatal_error to # treat the next displayed warning as a fatal error. warning_func=func_warn_and_continue # Set to 'all' to display all warnings, 'none' to suppress all # warnings, or a space delimited list of some subset of # 'warning_categories' to display only the listed warnings. opt_warning_types=all ## -------------------- ## ## Resource management. ## ## -------------------- ## # This section contains definitions for functions that each ensure a # particular resource (a file, or a non-empty configuration variable for # example) is available, and if appropriate to extract default values # from pertinent package files. Call them using their associated # 'require_*' variable to ensure that they are executed, at most, once. # # It's entirely deliberate that calling these functions can set # variables that don't obey the namespace limitations obeyed by the rest # of this file, in order that that they be as useful as possible to # callers. # require_term_colors # ------------------- # Allow display of bold text on terminals that support it. require_term_colors=func_require_term_colors func_require_term_colors () { $debug_cmd test -t 1 && { # COLORTERM and USE_ANSI_COLORS environment variables take # precedence, because most terminfo databases neglect to describe # whether color sequences are supported. test -n "${COLORTERM+set}" && : ${USE_ANSI_COLORS="1"} if test 1 = "$USE_ANSI_COLORS"; then # Standard ANSI escape sequences tc_reset='' tc_bold=''; tc_standout='' tc_red=''; tc_green='' tc_blue=''; tc_cyan='' else # Otherwise trust the terminfo database after all. test -n "`tput sgr0 2>/dev/null`" && { tc_reset=`tput sgr0` test -n "`tput bold 2>/dev/null`" && tc_bold=`tput bold` tc_standout=$tc_bold test -n "`tput smso 2>/dev/null`" && tc_standout=`tput smso` test -n "`tput setaf 1 2>/dev/null`" && tc_red=`tput setaf 1` test -n "`tput setaf 2 2>/dev/null`" && tc_green=`tput setaf 2` test -n "`tput setaf 4 2>/dev/null`" && tc_blue=`tput setaf 4` test -n "`tput setaf 5 2>/dev/null`" && tc_cyan=`tput setaf 5` } fi } require_term_colors=: } ## ----------------- ## ## Function library. ## ## ----------------- ## # This section contains a variety of useful functions to call in your # scripts. Take note of the portable wrappers for features provided by # some modern shells, which will fall back to slower equivalents on # less featureful shells. # func_append VAR VALUE # --------------------- # Append VALUE onto the existing contents of VAR. # We should try to minimise forks, especially on Windows where they are # unreasonably slow, so skip the feature probes when bash or zsh are # being used: if test set = "${BASH_VERSION+set}${ZSH_VERSION+set}"; then : ${_G_HAVE_ARITH_OP="yes"} : ${_G_HAVE_XSI_OPS="yes"} # The += operator was introduced in bash 3.1 case $BASH_VERSION in [12].* | 3.0 | 3.0*) ;; *) : ${_G_HAVE_PLUSEQ_OP="yes"} ;; esac fi # _G_HAVE_PLUSEQ_OP # Can be empty, in which case the shell is probed, "yes" if += is # useable or anything else if it does not work. test -z "$_G_HAVE_PLUSEQ_OP" \ && (eval 'x=a; x+=" b"; test "a b" = "$x"') 2>/dev/null \ && _G_HAVE_PLUSEQ_OP=yes if test yes = "$_G_HAVE_PLUSEQ_OP" then # This is an XSI compatible shell, allowing a faster implementation... eval 'func_append () { $debug_cmd eval "$1+=\$2" }' else # ...otherwise fall back to using expr, which is often a shell builtin. func_append () { $debug_cmd eval "$1=\$$1\$2" } fi # func_append_quoted VAR VALUE # ---------------------------- # Quote VALUE and append to the end of shell variable VAR, separated # by a space. if test yes = "$_G_HAVE_PLUSEQ_OP"; then eval 'func_append_quoted () { $debug_cmd func_quote_arg pretty "$2" eval "$1+=\\ \$func_quote_arg_result" }' else func_append_quoted () { $debug_cmd func_quote_arg pretty "$2" eval "$1=\$$1\\ \$func_quote_arg_result" } fi # func_append_uniq VAR VALUE # -------------------------- # Append unique VALUE onto the existing contents of VAR, assuming # entries are delimited by the first character of VALUE. For example: # # func_append_uniq options " --another-option option-argument" # # will only append to $options if " --another-option option-argument " # is not already present somewhere in $options already (note spaces at # each end implied by leading space in second argument). func_append_uniq () { $debug_cmd eval _G_current_value='`$ECHO $'$1'`' _G_delim=`expr "$2" : '\(.\)'` case $_G_delim$_G_current_value$_G_delim in *"$2$_G_delim"*) ;; *) func_append "$@" ;; esac } # func_arith TERM... # ------------------ # Set func_arith_result to the result of evaluating TERMs. test -z "$_G_HAVE_ARITH_OP" \ && (eval 'test 2 = $(( 1 + 1 ))') 2>/dev/null \ && _G_HAVE_ARITH_OP=yes if test yes = "$_G_HAVE_ARITH_OP"; then eval 'func_arith () { $debug_cmd func_arith_result=$(( $* )) }' else func_arith () { $debug_cmd func_arith_result=`expr "$@"` } fi # func_basename FILE # ------------------ # Set func_basename_result to FILE with everything up to and including # the last / stripped. if test yes = "$_G_HAVE_XSI_OPS"; then # If this shell supports suffix pattern removal, then use it to avoid # forking. Hide the definitions single quotes in case the shell chokes # on unsupported syntax... _b='func_basename_result=${1##*/}' _d='case $1 in */*) func_dirname_result=${1%/*}$2 ;; * ) func_dirname_result=$3 ;; esac' else # ...otherwise fall back to using sed. _b='func_basename_result=`$ECHO "$1" |$SED "$sed_basename"`' _d='func_dirname_result=`$ECHO "$1" |$SED "$sed_dirname"` if test "X$func_dirname_result" = "X$1"; then func_dirname_result=$3 else func_append func_dirname_result "$2" fi' fi eval 'func_basename () { $debug_cmd '"$_b"' }' # func_dirname FILE APPEND NONDIR_REPLACEMENT # ------------------------------------------- # Compute the dirname of FILE. If nonempty, add APPEND to the result, # otherwise set result to NONDIR_REPLACEMENT. eval 'func_dirname () { $debug_cmd '"$_d"' }' # func_dirname_and_basename FILE APPEND NONDIR_REPLACEMENT # -------------------------------------------------------- # Perform func_basename and func_dirname in a single function # call: # dirname: Compute the dirname of FILE. If nonempty, # add APPEND to the result, otherwise set result # to NONDIR_REPLACEMENT. # value returned in "$func_dirname_result" # basename: Compute filename of FILE. # value retuned in "$func_basename_result" # For efficiency, we do not delegate to the functions above but instead # duplicate the functionality here. eval 'func_dirname_and_basename () { $debug_cmd '"$_b"' '"$_d"' }' # func_echo ARG... # ---------------- # Echo program name prefixed message. func_echo () { $debug_cmd _G_message=$* func_echo_IFS=$IFS IFS=$nl for _G_line in $_G_message; do IFS=$func_echo_IFS $ECHO "$progname: $_G_line" done IFS=$func_echo_IFS } # func_echo_all ARG... # -------------------- # Invoke $ECHO with all args, space-separated. func_echo_all () { $ECHO "$*" } # func_echo_infix_1 INFIX ARG... # ------------------------------ # Echo program name, followed by INFIX on the first line, with any # additional lines not showing INFIX. func_echo_infix_1 () { $debug_cmd $require_term_colors _G_infix=$1; shift _G_indent=$_G_infix _G_prefix="$progname: $_G_infix: " _G_message=$* # Strip color escape sequences before counting printable length for _G_tc in "$tc_reset" "$tc_bold" "$tc_standout" "$tc_red" "$tc_green" "$tc_blue" "$tc_cyan" do test -n "$_G_tc" && { _G_esc_tc=`$ECHO "$_G_tc" | $SED "$sed_make_literal_regex"` _G_indent=`$ECHO "$_G_indent" | $SED "s|$_G_esc_tc||g"` } done _G_indent="$progname: "`echo "$_G_indent" | $SED 's|.| |g'`" " ## exclude from sc_prohibit_nested_quotes func_echo_infix_1_IFS=$IFS IFS=$nl for _G_line in $_G_message; do IFS=$func_echo_infix_1_IFS $ECHO "$_G_prefix$tc_bold$_G_line$tc_reset" >&2 _G_prefix=$_G_indent done IFS=$func_echo_infix_1_IFS } # func_error ARG... # ----------------- # Echo program name prefixed message to standard error. func_error () { $debug_cmd $require_term_colors func_echo_infix_1 " $tc_standout${tc_red}error$tc_reset" "$*" >&2 } # func_fatal_error ARG... # ----------------------- # Echo program name prefixed message to standard error, and exit. func_fatal_error () { $debug_cmd func_error "$*" exit $EXIT_FAILURE } # func_grep EXPRESSION FILENAME # ----------------------------- # Check whether EXPRESSION matches any line of FILENAME, without output. func_grep () { $debug_cmd $GREP "$1" "$2" >/dev/null 2>&1 } # func_len STRING # --------------- # Set func_len_result to the length of STRING. STRING may not # start with a hyphen. test -z "$_G_HAVE_XSI_OPS" \ && (eval 'x=a/b/c; test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \ && _G_HAVE_XSI_OPS=yes if test yes = "$_G_HAVE_XSI_OPS"; then eval 'func_len () { $debug_cmd func_len_result=${#1} }' else func_len () { $debug_cmd func_len_result=`expr "$1" : ".*" 2>/dev/null || echo $max_cmd_len` } fi # func_mkdir_p DIRECTORY-PATH # --------------------------- # Make sure the entire path to DIRECTORY-PATH is available. func_mkdir_p () { $debug_cmd _G_directory_path=$1 _G_dir_list= if test -n "$_G_directory_path" && test : != "$opt_dry_run"; then # Protect directory names starting with '-' case $_G_directory_path in -*) _G_directory_path=./$_G_directory_path ;; esac # While some portion of DIR does not yet exist... while test ! -d "$_G_directory_path"; do # ...make a list in topmost first order. Use a colon delimited # list incase some portion of path contains whitespace. _G_dir_list=$_G_directory_path:$_G_dir_list # If the last portion added has no slash in it, the list is done case $_G_directory_path in */*) ;; *) break ;; esac # ...otherwise throw away the child directory and loop _G_directory_path=`$ECHO "$_G_directory_path" | $SED -e "$sed_dirname"` done _G_dir_list=`$ECHO "$_G_dir_list" | $SED 's|:*$||'` func_mkdir_p_IFS=$IFS; IFS=: for _G_dir in $_G_dir_list; do IFS=$func_mkdir_p_IFS # mkdir can fail with a 'File exist' error if two processes # try to create one of the directories concurrently. Don't # stop in that case! $MKDIR "$_G_dir" 2>/dev/null || : done IFS=$func_mkdir_p_IFS # Bail out if we (or some other process) failed to create a directory. test -d "$_G_directory_path" || \ func_fatal_error "Failed to create '$1'" fi } # func_mktempdir [BASENAME] # ------------------------- # Make a temporary directory that won't clash with other running # libtool processes, and avoids race conditions if possible. If # given, BASENAME is the basename for that directory. func_mktempdir () { $debug_cmd _G_template=${TMPDIR-/tmp}/${1-$progname} if test : = "$opt_dry_run"; then # Return a directory name, but don't create it in dry-run mode _G_tmpdir=$_G_template-$$ else # If mktemp works, use that first and foremost _G_tmpdir=`mktemp -d "$_G_template-XXXXXXXX" 2>/dev/null` if test ! -d "$_G_tmpdir"; then # Failing that, at least try and use $RANDOM to avoid a race _G_tmpdir=$_G_template-${RANDOM-0}$$ func_mktempdir_umask=`umask` umask 0077 $MKDIR "$_G_tmpdir" umask $func_mktempdir_umask fi # If we're not in dry-run mode, bomb out on failure test -d "$_G_tmpdir" || \ func_fatal_error "cannot create temporary directory '$_G_tmpdir'" fi $ECHO "$_G_tmpdir" } # func_normal_abspath PATH # ------------------------ # Remove doubled-up and trailing slashes, "." path components, # and cancel out any ".." path components in PATH after making # it an absolute path. func_normal_abspath () { $debug_cmd # These SED scripts presuppose an absolute path with a trailing slash. _G_pathcar='s|^/\([^/]*\).*$|\1|' _G_pathcdr='s|^/[^/]*||' _G_removedotparts=':dotsl s|/\./|/|g t dotsl s|/\.$|/|' _G_collapseslashes='s|/\{1,\}|/|g' _G_finalslash='s|/*$|/|' # Start from root dir and reassemble the path. func_normal_abspath_result= func_normal_abspath_tpath=$1 func_normal_abspath_altnamespace= case $func_normal_abspath_tpath in "") # Empty path, that just means $cwd. func_stripname '' '/' "`pwd`" func_normal_abspath_result=$func_stripname_result return ;; # The next three entries are used to spot a run of precisely # two leading slashes without using negated character classes; # we take advantage of case's first-match behaviour. ///*) # Unusual form of absolute path, do nothing. ;; //*) # Not necessarily an ordinary path; POSIX reserves leading '//' # and for example Cygwin uses it to access remote file shares # over CIFS/SMB, so we conserve a leading double slash if found. func_normal_abspath_altnamespace=/ ;; /*) # Absolute path, do nothing. ;; *) # Relative path, prepend $cwd. func_normal_abspath_tpath=`pwd`/$func_normal_abspath_tpath ;; esac # Cancel out all the simple stuff to save iterations. We also want # the path to end with a slash for ease of parsing, so make sure # there is one (and only one) here. func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$_G_removedotparts" -e "$_G_collapseslashes" -e "$_G_finalslash"` while :; do # Processed it all yet? if test / = "$func_normal_abspath_tpath"; then # If we ascended to the root using ".." the result may be empty now. if test -z "$func_normal_abspath_result"; then func_normal_abspath_result=/ fi break fi func_normal_abspath_tcomponent=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$_G_pathcar"` func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$_G_pathcdr"` # Figure out what to do with it case $func_normal_abspath_tcomponent in "") # Trailing empty path component, ignore it. ;; ..) # Parent dir; strip last assembled component from result. func_dirname "$func_normal_abspath_result" func_normal_abspath_result=$func_dirname_result ;; *) # Actual path component, append it. func_append func_normal_abspath_result "/$func_normal_abspath_tcomponent" ;; esac done # Restore leading double-slash if one was found on entry. func_normal_abspath_result=$func_normal_abspath_altnamespace$func_normal_abspath_result } # func_notquiet ARG... # -------------------- # Echo program name prefixed message only when not in quiet mode. func_notquiet () { $debug_cmd $opt_quiet || func_echo ${1+"$@"} # A bug in bash halts the script if the last line of a function # fails when set -e is in force, so we need another command to # work around that: : } # func_relative_path SRCDIR DSTDIR # -------------------------------- # Set func_relative_path_result to the relative path from SRCDIR to DSTDIR. func_relative_path () { $debug_cmd func_relative_path_result= func_normal_abspath "$1" func_relative_path_tlibdir=$func_normal_abspath_result func_normal_abspath "$2" func_relative_path_tbindir=$func_normal_abspath_result # Ascend the tree starting from libdir while :; do # check if we have found a prefix of bindir case $func_relative_path_tbindir in $func_relative_path_tlibdir) # found an exact match func_relative_path_tcancelled= break ;; $func_relative_path_tlibdir*) # found a matching prefix func_stripname "$func_relative_path_tlibdir" '' "$func_relative_path_tbindir" func_relative_path_tcancelled=$func_stripname_result if test -z "$func_relative_path_result"; then func_relative_path_result=. fi break ;; *) func_dirname $func_relative_path_tlibdir func_relative_path_tlibdir=$func_dirname_result if test -z "$func_relative_path_tlibdir"; then # Have to descend all the way to the root! func_relative_path_result=../$func_relative_path_result func_relative_path_tcancelled=$func_relative_path_tbindir break fi func_relative_path_result=../$func_relative_path_result ;; esac done # Now calculate path; take care to avoid doubling-up slashes. func_stripname '' '/' "$func_relative_path_result" func_relative_path_result=$func_stripname_result func_stripname '/' '/' "$func_relative_path_tcancelled" if test -n "$func_stripname_result"; then func_append func_relative_path_result "/$func_stripname_result" fi # Normalisation. If bindir is libdir, return '.' else relative path. if test -n "$func_relative_path_result"; then func_stripname './' '' "$func_relative_path_result" func_relative_path_result=$func_stripname_result fi test -n "$func_relative_path_result" || func_relative_path_result=. : } # func_quote_portable EVAL ARG # ---------------------------- # Internal function to portably implement func_quote_arg. Note that we still # keep attention to performance here so we as much as possible try to avoid # calling sed binary (so far O(N) complexity as long as func_append is O(1)). func_quote_portable () { $debug_cmd $require_check_ifs_backslash func_quote_portable_result=$2 # one-time-loop (easy break) while true do if $1; then func_quote_portable_result=`$ECHO "$2" | $SED \ -e "$sed_double_quote_subst" -e "$sed_double_backslash"` break fi # Quote for eval. case $func_quote_portable_result in *[\\\`\"\$]*) # Fallback to sed for $func_check_bs_ifs_broken=:, or when the string # contains the shell wildcard characters. case $check_ifs_backshlash_broken$func_quote_portable_result in :*|*[\[\*\?]*) func_quote_portable_result=`$ECHO "$func_quote_portable_result" \ | $SED "$sed_quote_subst"` break ;; esac func_quote_portable_old_IFS=$IFS for _G_char in '\' '`' '"' '$' do # STATE($1) PREV($2) SEPARATOR($3) set start "" "" func_quote_portable_result=dummy"$_G_char$func_quote_portable_result$_G_char"dummy IFS=$_G_char for _G_part in $func_quote_portable_result do case $1 in quote) func_append func_quote_portable_result "$3$2" set quote "$_G_part" "\\$_G_char" ;; start) set first "" "" func_quote_portable_result= ;; first) set quote "$_G_part" "" ;; esac done done IFS=$func_quote_portable_old_IFS ;; *) ;; esac break done func_quote_portable_unquoted_result=$func_quote_portable_result case $func_quote_portable_result in # double-quote args containing shell metacharacters to delay # word splitting, command substitution and variable expansion # for a subsequent eval. # many bourne shells cannot handle close brackets correctly # in scan sets, so we specify it separately. *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") func_quote_portable_result=\"$func_quote_portable_result\" ;; esac } # func_quotefast_eval ARG # ----------------------- # Quote one ARG (internal). This is equivalent to 'func_quote_arg eval ARG', # but optimized for speed. Result is stored in $func_quotefast_eval. if test xyes = `(x=; printf -v x %q yes; echo x"$x") 2>/dev/null`; then printf -v _GL_test_printf_tilde %q '~' if test '\~' = "$_GL_test_printf_tilde"; then func_quotefast_eval () { printf -v func_quotefast_eval_result %q "$1" } else # Broken older Bash implementations. Make those faster too if possible. func_quotefast_eval () { case $1 in '~'*) func_quote_portable false "$1" func_quotefast_eval_result=$func_quote_portable_result ;; *) printf -v func_quotefast_eval_result %q "$1" ;; esac } fi else func_quotefast_eval () { func_quote_portable false "$1" func_quotefast_eval_result=$func_quote_portable_result } fi # func_quote_arg MODEs ARG # ------------------------ # Quote one ARG to be evaled later. MODEs argument may contain zero or more # specifiers listed below separated by ',' character. This function returns two # values: # i) func_quote_arg_result # double-quoted (when needed), suitable for a subsequent eval # ii) func_quote_arg_unquoted_result # has all characters that are still active within double # quotes backslashified. Available only if 'unquoted' is specified. # # Available modes: # ---------------- # 'eval' (default) # - escape shell special characters # 'expand' # - the same as 'eval'; but do not quote variable references # 'pretty' # - request aesthetic output, i.e. '"a b"' instead of 'a\ b'. This might # be used later in func_quote to get output like: 'echo "a b"' instead # of 'echo a\ b'. This is slower than default on some shells. # 'unquoted' # - produce also $func_quote_arg_unquoted_result which does not contain # wrapping double-quotes. # # Examples for 'func_quote_arg pretty,unquoted string': # # string | *_result | *_unquoted_result # ------------+-----------------------+------------------- # " | \" | \" # a b | "a b" | a b # "a b" | "\"a b\"" | \"a b\" # * | "*" | * # z="${x-$y}" | "z=\"\${x-\$y}\"" | z=\"\${x-\$y}\" # # Examples for 'func_quote_arg pretty,unquoted,expand string': # # string | *_result | *_unquoted_result # --------------+---------------------+-------------------- # z="${x-$y}" | "z=\"${x-$y}\"" | z=\"${x-$y}\" func_quote_arg () { _G_quote_expand=false case ,$1, in *,expand,*) _G_quote_expand=: ;; esac case ,$1, in *,pretty,*|*,expand,*|*,unquoted,*) func_quote_portable $_G_quote_expand "$2" func_quote_arg_result=$func_quote_portable_result func_quote_arg_unquoted_result=$func_quote_portable_unquoted_result ;; *) # Faster quote-for-eval for some shells. func_quotefast_eval "$2" func_quote_arg_result=$func_quotefast_eval_result ;; esac } # func_quote MODEs ARGs... # ------------------------ # Quote all ARGs to be evaled later and join them into single command. See # func_quote_arg's description for more info. func_quote () { $debug_cmd _G_func_quote_mode=$1 ; shift func_quote_result= while test 0 -lt $#; do func_quote_arg "$_G_func_quote_mode" "$1" if test -n "$func_quote_result"; then func_append func_quote_result " $func_quote_arg_result" else func_append func_quote_result "$func_quote_arg_result" fi shift done } # func_stripname PREFIX SUFFIX NAME # --------------------------------- # strip PREFIX and SUFFIX from NAME, and store in func_stripname_result. # PREFIX and SUFFIX must not contain globbing or regex special # characters, hashes, percent signs, but SUFFIX may contain a leading # dot (in which case that matches only a dot). if test yes = "$_G_HAVE_XSI_OPS"; then eval 'func_stripname () { $debug_cmd # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are # positional parameters, so assign one to ordinary variable first. func_stripname_result=$3 func_stripname_result=${func_stripname_result#"$1"} func_stripname_result=${func_stripname_result%"$2"} }' else func_stripname () { $debug_cmd case $2 in .*) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%\\\\$2\$%%"`;; *) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%$2\$%%"`;; esac } fi # func_show_eval CMD [FAIL_EXP] # ----------------------------- # Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is # not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP # is given, then evaluate it. func_show_eval () { $debug_cmd _G_cmd=$1 _G_fail_exp=${2-':'} func_quote_arg pretty,expand "$_G_cmd" eval "func_notquiet $func_quote_arg_result" $opt_dry_run || { eval "$_G_cmd" _G_status=$? if test 0 -ne "$_G_status"; then eval "(exit $_G_status); $_G_fail_exp" fi } } # func_show_eval_locale CMD [FAIL_EXP] # ------------------------------------ # Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is # not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP # is given, then evaluate it. Use the saved locale for evaluation. func_show_eval_locale () { $debug_cmd _G_cmd=$1 _G_fail_exp=${2-':'} $opt_quiet || { func_quote_arg expand,pretty "$_G_cmd" eval "func_echo $func_quote_arg_result" } $opt_dry_run || { eval "$_G_user_locale $_G_cmd" _G_status=$? eval "$_G_safe_locale" if test 0 -ne "$_G_status"; then eval "(exit $_G_status); $_G_fail_exp" fi } } # func_tr_sh # ---------- # Turn $1 into a string suitable for a shell variable name. # Result is stored in $func_tr_sh_result. All characters # not in the set a-zA-Z0-9_ are replaced with '_'. Further, # if $1 begins with a digit, a '_' is prepended as well. func_tr_sh () { $debug_cmd case $1 in [0-9]* | *[!a-zA-Z0-9_]*) func_tr_sh_result=`$ECHO "$1" | $SED -e 's/^\([0-9]\)/_\1/' -e 's/[^a-zA-Z0-9_]/_/g'` ;; * ) func_tr_sh_result=$1 ;; esac } # func_verbose ARG... # ------------------- # Echo program name prefixed message in verbose mode only. func_verbose () { $debug_cmd $opt_verbose && func_echo "$*" : } # func_warn_and_continue ARG... # ----------------------------- # Echo program name prefixed warning message to standard error. func_warn_and_continue () { $debug_cmd $require_term_colors func_echo_infix_1 "${tc_red}warning$tc_reset" "$*" >&2 } # func_warning CATEGORY ARG... # ---------------------------- # Echo program name prefixed warning message to standard error. Warning # messages can be filtered according to CATEGORY, where this function # elides messages where CATEGORY is not listed in the global variable # 'opt_warning_types'. func_warning () { $debug_cmd # CATEGORY must be in the warning_categories list! case " $warning_categories " in *" $1 "*) ;; *) func_internal_error "invalid warning category '$1'" ;; esac _G_category=$1 shift case " $opt_warning_types " in *" $_G_category "*) $warning_func ${1+"$@"} ;; esac } # func_sort_ver VER1 VER2 # ----------------------- # 'sort -V' is not generally available. # Note this deviates from the version comparison in automake # in that it treats 1.5 < 1.5.0, and treats 1.4.4a < 1.4-p3a # but this should suffice as we won't be specifying old # version formats or redundant trailing .0 in bootstrap.conf. # If we did want full compatibility then we should probably # use m4_version_compare from autoconf. func_sort_ver () { $debug_cmd printf '%s\n%s\n' "$1" "$2" \ | sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n -k 5,5n -k 6,6n -k 7,7n -k 8,8n -k 9,9n } # func_lt_ver PREV CURR # --------------------- # Return true if PREV and CURR are in the correct order according to # func_sort_ver, otherwise false. Use it like this: # # func_lt_ver "$prev_ver" "$proposed_ver" || func_fatal_error "..." func_lt_ver () { $debug_cmd test "x$1" = x`func_sort_ver "$1" "$2" | $SED 1q` } # Local variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC" # time-stamp-time-zone: "UTC" # End: #! /bin/sh # A portable, pluggable option parser for Bourne shell. # Written by Gary V. Vaughan, 2010 # This is free software. There is NO warranty; not even for # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # # Copyright (C) 2010-2019, 2021, 2023-2024 Bootstrap Authors # # This file is dual licensed under the terms of the MIT license # , and GPL version 2 or later # . You must apply one of # these licenses when using or redistributing this software or any of # the files within it. See the URLs above, or the file `LICENSE` # included in the Bootstrap distribution for the full license texts. # Please report bugs or propose patches to: # # Set a version string for this script. scriptversion=2019-02-19.15; # UTC ## ------ ## ## Usage. ## ## ------ ## # This file is a library for parsing options in your shell scripts along # with assorted other useful supporting features that you can make use # of too. # # For the simplest scripts you might need only: # # #!/bin/sh # . relative/path/to/funclib.sh # . relative/path/to/options-parser # scriptversion=1.0 # func_options ${1+"$@"} # eval set dummy "$func_options_result"; shift # ...rest of your script... # # In order for the '--version' option to work, you will need to have a # suitably formatted comment like the one at the top of this file # starting with '# Written by ' and ending with '# Copyright'. # # For '-h' and '--help' to work, you will also need a one line # description of your script's purpose in a comment directly above the # '# Written by ' line, like the one at the top of this file. # # The default options also support '--debug', which will turn on shell # execution tracing (see the comment above debug_cmd below for another # use), and '--verbose' and the func_verbose function to allow your script # to display verbose messages only when your user has specified # '--verbose'. # # After sourcing this file, you can plug in processing for additional # options by amending the variables from the 'Configuration' section # below, and following the instructions in the 'Option parsing' # section further down. ## -------------- ## ## Configuration. ## ## -------------- ## # You should override these variables in your script after sourcing this # file so that they reflect the customisations you have added to the # option parser. # The usage line for option parsing errors and the start of '-h' and # '--help' output messages. You can embed shell variables for delayed # expansion at the time the message is displayed, but you will need to # quote other shell meta-characters carefully to prevent them being # expanded when the contents are evaled. usage='$progpath [OPTION]...' # Short help message in response to '-h' and '--help'. Add to this or # override it after sourcing this library to reflect the full set of # options your script accepts. usage_message="\ --debug enable verbose shell tracing -W, --warnings=CATEGORY report the warnings falling in CATEGORY [all] -v, --verbose verbosely report processing --version print version information and exit -h, --help print short or long help message and exit " # Additional text appended to 'usage_message' in response to '--help'. long_help_message=" Warning categories include: 'all' show all warnings 'none' turn off all the warnings 'error' warnings are treated as fatal errors" # Help message printed before fatal option parsing errors. fatal_help="Try '\$progname --help' for more information." ## ------------------------- ## ## Hook function management. ## ## ------------------------- ## # This section contains functions for adding, removing, and running hooks # in the main code. A hook is just a list of function names that can be # run in order later on. # func_hookable FUNC_NAME # ----------------------- # Declare that FUNC_NAME will run hooks added with # 'func_add_hook FUNC_NAME ...'. func_hookable () { $debug_cmd func_append hookable_fns " $1" } # func_add_hook FUNC_NAME HOOK_FUNC # --------------------------------- # Request that FUNC_NAME call HOOK_FUNC before it returns. FUNC_NAME must # first have been declared "hookable" by a call to 'func_hookable'. func_add_hook () { $debug_cmd case " $hookable_fns " in *" $1 "*) ;; *) func_fatal_error "'$1' does not accept hook functions." ;; esac eval func_append ${1}_hooks '" $2"' } # func_remove_hook FUNC_NAME HOOK_FUNC # ------------------------------------ # Remove HOOK_FUNC from the list of hook functions to be called by # FUNC_NAME. func_remove_hook () { $debug_cmd eval ${1}_hooks='`$ECHO "\$'$1'_hooks" |$SED "s| '$2'||"`' } # func_propagate_result FUNC_NAME_A FUNC_NAME_B # --------------------------------------------- # If the *_result variable of FUNC_NAME_A _is set_, assign its value to # *_result variable of FUNC_NAME_B. func_propagate_result () { $debug_cmd func_propagate_result_result=: if eval "test \"\${${1}_result+set}\" = set" then eval "${2}_result=\$${1}_result" else func_propagate_result_result=false fi } # func_run_hooks FUNC_NAME [ARG]... # --------------------------------- # Run all hook functions registered to FUNC_NAME. # It's assumed that the list of hook functions contains nothing more # than a whitespace-delimited list of legal shell function names, and # no effort is wasted trying to catch shell meta-characters or preserve # whitespace. func_run_hooks () { $debug_cmd case " $hookable_fns " in *" $1 "*) ;; *) func_fatal_error "'$1' does not support hook functions." ;; esac eval _G_hook_fns=\$$1_hooks; shift for _G_hook in $_G_hook_fns; do func_unset "${_G_hook}_result" eval $_G_hook '${1+"$@"}' func_propagate_result $_G_hook func_run_hooks if $func_propagate_result_result; then eval set dummy "$func_run_hooks_result"; shift fi done } ## --------------- ## ## Option parsing. ## ## --------------- ## # In order to add your own option parsing hooks, you must accept the # full positional parameter list from your hook function. You may remove # or edit any options that you action, and then pass back the remaining # unprocessed options in '_result', escaped # suitably for 'eval'. # # The '_result' variable is automatically unset # before your hook gets called; for best performance, only set the # *_result variable when necessary (i.e. don't call the 'func_quote' # function unnecessarily because it can be an expensive operation on some # machines). # # Like this: # # my_options_prep () # { # $debug_cmd # # # Extend the existing usage message. # usage_message=$usage_message' # -s, --silent don'\''t print informational messages # ' # # No change in '$@' (ignored completely by this hook). Leave # # my_options_prep_result variable intact. # } # func_add_hook func_options_prep my_options_prep # # # my_silent_option () # { # $debug_cmd # # args_changed=false # # # Note that, for efficiency, we parse as many options as we can # # recognise in a loop before passing the remainder back to the # # caller on the first unrecognised argument we encounter. # while test $# -gt 0; do # opt=$1; shift # case $opt in # --silent|-s) opt_silent=: # args_changed=: # ;; # # Separate non-argument short options: # -s*) func_split_short_opt "$_G_opt" # set dummy "$func_split_short_opt_name" \ # "-$func_split_short_opt_arg" ${1+"$@"} # shift # args_changed=: # ;; # *) # Make sure the first unrecognised option "$_G_opt" # # is added back to "$@" in case we need it later, # # if $args_changed was set to 'true'. # set dummy "$_G_opt" ${1+"$@"}; shift; break ;; # esac # done # # # Only call 'func_quote' here if we processed at least one argument. # if $args_changed; then # func_quote eval ${1+"$@"} # my_silent_option_result=$func_quote_result # fi # } # func_add_hook func_parse_options my_silent_option # # # my_option_validation () # { # $debug_cmd # # $opt_silent && $opt_verbose && func_fatal_help "\ # '--silent' and '--verbose' options are mutually exclusive." # } # func_add_hook func_validate_options my_option_validation # # You'll also need to manually amend $usage_message to reflect the extra # options you parse. It's preferable to append if you can, so that # multiple option parsing hooks can be added safely. # func_options_finish [ARG]... # ---------------------------- # Finishing the option parse loop (call 'func_options' hooks ATM). func_options_finish () { $debug_cmd func_run_hooks func_options ${1+"$@"} func_propagate_result func_run_hooks func_options_finish } # func_options [ARG]... # --------------------- # All the functions called inside func_options are hookable. See the # individual implementations for details. func_hookable func_options func_options () { $debug_cmd _G_options_quoted=false for my_func in options_prep parse_options validate_options options_finish do func_unset func_${my_func}_result func_unset func_run_hooks_result eval func_$my_func '${1+"$@"}' func_propagate_result func_$my_func func_options if $func_propagate_result_result; then eval set dummy "$func_options_result"; shift _G_options_quoted=: fi done $_G_options_quoted || { # As we (func_options) are top-level options-parser function and # nobody quoted "$@" for us yet, we need to do it explicitly for # caller. func_quote eval ${1+"$@"} func_options_result=$func_quote_result } } # func_options_prep [ARG]... # -------------------------- # All initialisations required before starting the option parse loop. # Note that when calling hook functions, we pass through the list of # positional parameters. If a hook function modifies that list, and # needs to propagate that back to rest of this script, then the complete # modified list must be put in 'func_run_hooks_result' before returning. func_hookable func_options_prep func_options_prep () { $debug_cmd # Option defaults: opt_verbose=false opt_warning_types= func_run_hooks func_options_prep ${1+"$@"} func_propagate_result func_run_hooks func_options_prep } # func_parse_options [ARG]... # --------------------------- # The main option parsing loop. func_hookable func_parse_options func_parse_options () { $debug_cmd _G_parse_options_requote=false # this just eases exit handling while test $# -gt 0; do # Defer to hook functions for initial option parsing, so they # get priority in the event of reusing an option name. func_run_hooks func_parse_options ${1+"$@"} func_propagate_result func_run_hooks func_parse_options if $func_propagate_result_result; then eval set dummy "$func_parse_options_result"; shift # Even though we may have changed "$@", we passed the "$@" array # down into the hook and it quoted it for us (because we are in # this if-branch). No need to quote it again. _G_parse_options_requote=false fi # Break out of the loop if we already parsed every option. test $# -gt 0 || break # We expect that one of the options parsed in this function matches # and thus we remove _G_opt from "$@" and need to re-quote. _G_match_parse_options=: _G_opt=$1 shift case $_G_opt in --debug|-x) debug_cmd='set -x' func_echo "enabling shell trace mode" >&2 $debug_cmd ;; --no-warnings|--no-warning|--no-warn) set dummy --warnings none ${1+"$@"} shift ;; --warnings|--warning|-W) if test $# = 0 && func_missing_arg $_G_opt; then _G_parse_options_requote=: break fi case " $warning_categories $1" in *" $1 "*) # trailing space prevents matching last $1 above func_append_uniq opt_warning_types " $1" ;; *all) opt_warning_types=$warning_categories ;; *none) opt_warning_types=none warning_func=: ;; *error) opt_warning_types=$warning_categories warning_func=func_fatal_error ;; *) func_fatal_error \ "unsupported warning category: '$1'" ;; esac shift ;; --verbose|-v) opt_verbose=: ;; --version) func_version ;; -\?|-h) func_usage ;; --help) func_help ;; # Separate optargs to long options (plugins may need this): --*=*) func_split_equals "$_G_opt" set dummy "$func_split_equals_lhs" \ "$func_split_equals_rhs" ${1+"$@"} shift ;; # Separate optargs to short options: -W*) func_split_short_opt "$_G_opt" set dummy "$func_split_short_opt_name" \ "$func_split_short_opt_arg" ${1+"$@"} shift ;; # Separate non-argument short options: -\?*|-h*|-v*|-x*) func_split_short_opt "$_G_opt" set dummy "$func_split_short_opt_name" \ "-$func_split_short_opt_arg" ${1+"$@"} shift ;; --) _G_parse_options_requote=: ; break ;; -*) func_fatal_help "unrecognised option: '$_G_opt'" ;; *) set dummy "$_G_opt" ${1+"$@"}; shift _G_match_parse_options=false break ;; esac if $_G_match_parse_options; then _G_parse_options_requote=: fi done if $_G_parse_options_requote; then # save modified positional parameters for caller func_quote eval ${1+"$@"} func_parse_options_result=$func_quote_result fi } # func_validate_options [ARG]... # ------------------------------ # Perform any sanity checks on option settings and/or unconsumed # arguments. func_hookable func_validate_options func_validate_options () { $debug_cmd # Display all warnings if -W was not given. test -n "$opt_warning_types" || opt_warning_types=" $warning_categories" func_run_hooks func_validate_options ${1+"$@"} func_propagate_result func_run_hooks func_validate_options # Bail if the options were screwed! $exit_cmd $EXIT_FAILURE } ## ----------------- ## ## Helper functions. ## ## ----------------- ## # This section contains the helper functions used by the rest of the # hookable option parser framework in ascii-betical order. # func_fatal_help ARG... # ---------------------- # Echo program name prefixed message to standard error, followed by # a help hint, and exit. func_fatal_help () { $debug_cmd eval \$ECHO \""Usage: $usage"\" eval \$ECHO \""$fatal_help"\" func_error ${1+"$@"} exit $EXIT_FAILURE } # func_help # --------- # Echo long help message to standard output and exit. func_help () { $debug_cmd func_usage_message $ECHO "$long_help_message" exit 0 } # func_missing_arg ARGNAME # ------------------------ # Echo program name prefixed message to standard error and set global # exit_cmd. func_missing_arg () { $debug_cmd func_error "Missing argument for '$1'." exit_cmd=exit } # func_split_equals STRING # ------------------------ # Set func_split_equals_lhs and func_split_equals_rhs shell variables # after splitting STRING at the '=' sign. test -z "$_G_HAVE_XSI_OPS" \ && (eval 'x=a/b/c; test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \ && _G_HAVE_XSI_OPS=yes if test yes = "$_G_HAVE_XSI_OPS" then # This is an XSI compatible shell, allowing a faster implementation... eval 'func_split_equals () { $debug_cmd func_split_equals_lhs=${1%%=*} func_split_equals_rhs=${1#*=} if test "x$func_split_equals_lhs" = "x$1"; then func_split_equals_rhs= fi }' else # ...otherwise fall back to using expr, which is often a shell builtin. func_split_equals () { $debug_cmd func_split_equals_lhs=`expr "x$1" : 'x\([^=]*\)'` func_split_equals_rhs= test "x$func_split_equals_lhs=" = "x$1" \ || func_split_equals_rhs=`expr "x$1" : 'x[^=]*=\(.*\)$'` } fi #func_split_equals # func_split_short_opt SHORTOPT # ----------------------------- # Set func_split_short_opt_name and func_split_short_opt_arg shell # variables after splitting SHORTOPT after the 2nd character. if test yes = "$_G_HAVE_XSI_OPS" then # This is an XSI compatible shell, allowing a faster implementation... eval 'func_split_short_opt () { $debug_cmd func_split_short_opt_arg=${1#??} func_split_short_opt_name=${1%"$func_split_short_opt_arg"} }' else # ...otherwise fall back to using expr, which is often a shell builtin. func_split_short_opt () { $debug_cmd func_split_short_opt_name=`expr "x$1" : 'x\(-.\)'` func_split_short_opt_arg=`expr "x$1" : 'x-.\(.*\)$'` } fi #func_split_short_opt # func_usage # ---------- # Echo short help message to standard output and exit. func_usage () { $debug_cmd func_usage_message $ECHO "Run '$progname --help |${PAGER-more}' for full usage" exit 0 } # func_usage_message # ------------------ # Echo short help message to standard output. func_usage_message () { $debug_cmd eval \$ECHO \""Usage: $usage"\" echo $SED -n 's|^# || /^Written by/{ x;p;x } h /^Written by/q' < "$progpath" echo eval \$ECHO \""$usage_message"\" } # func_version # ------------ # Echo version message to standard output and exit. # The version message is extracted from the calling file's header # comments, with leading '# ' stripped: # 1. First display the progname and version # 2. Followed by the header comment line matching /^# Written by / # 3. Then a blank line followed by the first following line matching # /^# Copyright / # 4. Immediately followed by any lines between the previous matches, # except lines preceding the intervening completely blank line. # For example, see the header comments of this file. func_version () { $debug_cmd printf '%s\n' "$progname $scriptversion" $SED -n ' /^# Written by /!b s|^# ||; p; n :fwd2blnk /./ { n b fwd2blnk } p; n :holdwrnt s|^# || s|^# *$|| /^Copyright /!{ /./H n b holdwrnt } s|\((C)\)[ 0-9,-]*[ ,-]\([1-9][0-9]* \)|\1 \2| G s|\(\n\)\n*|\1|g p; q' < "$progpath" exit $? } # Local variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-pattern: "30/scriptversion=%:y-%02m-%02d.%02H; # UTC" # time-stamp-time-zone: "UTC" # End: #! /bin/sh # Extract macro arguments from autotools input with GNU M4. # Written by Gary V. Vaughan, 2010 # # This is free software. There is NO warranty; not even for # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # # Copyright (C) 2010-2019, 2021, 2023-2024 Bootstrap Authors # # This file is dual licensed under the terms of the MIT license # , and GPL version 2 or later # . You must apply one of # these licenses when using or redistributing this software or any of # the files within it. See the URLs above, or the file `LICENSE` # included in the Bootstrap distribution for the full license texts. # Please report bugs or propose patches to: # # Make sure we've evaluated scripts we depend on. test -z "$progpath" && . `echo "$0" |${SED-sed} 's|[^/]*$||'`/funclib.sh test extract-trace = "$progname" && . `echo "$0" |${SED-sed} 's|[^/]*$||'`/options-parser # Set a version string. scriptversion=2019-02-19.15; # UTC ## ------ ## ## Usage. ## ## ------ ## # Run './extract-trace --help' for help with using this script from the # command line. # # Or source first 'options-parser' and then this file into your own # scripts in order to make use of the function and variable framework # they define, and also to avoid the overhead of forking to run this # script in its own process on every call. ## ----------------- ## ## Helper functions. ## ## ----------------- ## # This section contains the helper functions used by the rest of # 'extract-trace'. # func_autoconf_configure MAYBE-CONFIGURE-FILE # -------------------------------------------- # Ensure that MAYBE-CONFIGURE-FILE is the name of a file in the current # directory that contains an uncommented call to AC_INIT. func_autoconf_configure () { $debug_cmd _G_sed_no_comment=' s|#.*$|| s|^dnl .*$|| s| dnl .*$||' _G_ac_init= # If we were passed a genuine file, make sure it calls AC_INIT. test -f "$1" \ && _G_ac_init=`$SED "$_G_sed_no_comment" "$1" |$GREP AC_INIT` # Otherwise it is not a genuine Autoconf input file. test -n "$_G_ac_init" _G_status=$? test 0 -ne "$_G_status" \ && func_verbose "'$1' not using Autoconf" (exit $_G_status) } # func_tool_version_output CMD [FATAL-ERROR-MSG] # ---------------------------------------------- # Attempt to run 'CMD --version', discarding errors. The output can be # ignored by redirecting stdout, and this function used simply to test # whether the command exists and exits normally when passed a # '--version' argument. # When FATAL-ERROR-MSG is given, then this function will display the # message and exit if running 'CMD --version' returns a non-zero exit # status. func_tool_version_output () { $debug_cmd _G_cmd=$1 _G_fatal_error_msg=$2 # Some tools, like 'git2cl' produce thousands of lines of output # unless stdin is /dev/null - in that case we want to return # successfully without saving all of that output. Other tools, # such as 'help2man' exit with a non-zero status when stdin comes # from /dev/null, so we re-execute without /dev/null if that # happens. This means that occasionally, the output from both calls # ends up in the result, but the alternative would be to discard the # output from one call, and hope the other produces something useful. { $_G_cmd --version /dev/null _G_status=$? test 0 -ne "$_G_status" && test -n "$_G_fatal_error_msg" \ && func_fatal_error "$_G_fatal_error_msg" (exit $_G_status) } # func_tool_version_number CMD [FATAL-ERROR-MSG] # ---------------------------------------------- # Pass arguments to func_tool_version_output, but set # $func_tool_version_number_result to the last dot delimited digit string # on the first line of output. func_tool_version_number () { $debug_cmd _G_verout=`func_tool_version_output "$@"` _G_status=$? # A version number starts with a digit following a space on the first # line of output from `--version`. _G_verout=`echo "$_G_verout" |sed 1q` if test -n "$_G_verout"; then _G_vernum=`expr "$_G_verout" : '.* \([0-9][^ ]*\)'` fi if test -n "$_G_vernum"; then printf '%s\n' "$_G_vernum" else printf '%s\n' "$_G_verout" fi (exit $_G_status) } # func_find_tool ENVVAR NAMES... # ------------------------------ # Search for a required program. Use the value of ENVVAR, if set, # otherwise find the first of the NAMES that can be run (i.e., # supports --version). If found, set ENVVAR to the program name, # die otherwise. func_find_tool () { $debug_cmd _G_find_tool_envvar=$1 shift _G_find_tool_names=$@ eval "_G_find_tool_res=\$$_G_find_tool_envvar" if test -n "$_G_find_tool_res"; then _G_find_tool_error_prefix="\$$find_tool_envvar: " else _G_find_tool_res= _G_bestver= for _G_prog do _G_find_tool_save_IFS=$IFS IFS=${PATH_SEPARATOR-:} for _G_dir in $PATH; do IFS=$_G_find_tool_save_IFS _G_progpath=$_G_dir/$_G_prog test -r "$_G_progpath" && { _G_curver=`func_tool_version_number $_G_progpath` case $_G_bestver,$_G_curver in ,) # first non--version responsive prog sticks! test -n "$_G_progpath" || _G_find_tool_res=$_G_progpath ;; ,*) # first --version responsive prog beats non--version responsive! _G_find_tool_res=$_G_progpath _G_bestver=$_G_curver ;; *,*) # another --version responsive prog must be newer to beat previous one! test "x$_G_curver" = "x$_G_bestver" \ || func_lt_ver "$_G_curver" "$_G_bestver" \ || { _G_find_tool_res=$_G_progpath _G_bestver=$_G_curver } ;; esac } done IFS=$_G_find_tool_save_IFS done fi if test -n "$_G_find_tool_res"; then func_tool_version_number >/dev/null $_G_find_tool_res "\ ${_G_find_tool_error_prefix}Cannot run '$_G_find_tool_res --version'" # Make sure the result is exported to the environment for children # to use. eval "$_G_find_tool_envvar=\$_G_find_tool_res" eval "export $_G_find_tool_envvar" else func_error "\ One of these is required: $_G_find_tool_names" fi } ## -------------------- ## ## Resource management. ## ## -------------------- ## # This section contains definitions for functions that each ensure a # particular resource (a file, or a non-empty configuration variable for # example) is available, and if appropriate to extract default values # from pertinent package files. Where a variable already has a non- # empty value (as set by the package's 'bootstrap.conf'), that value is # used in preference to deriving the default. Call them using their # associated 'require_*' variable to ensure that they are executed, at # most, once. # # It's entirely deliberate that calling these functions can set # variables that don't obey the namespace limitations obeyed by the rest # of this file, in order that that they be as useful as possible to # callers. # require_configure_ac # -------------------- # Ensure that there is a 'configure.ac' or 'configure.in' file in the # current directory that contains an uncommented call to AC_INIT, and # that '$configure_ac' contains its name. require_configure_ac=func_require_configure_ac func_require_configure_ac () { $debug_cmd test -z "$configure_ac" \ && func_autoconf_configure configure.ac && configure_ac=configure.ac test -z "$configure_ac" \ && func_autoconf_configure configure.in && configure_ac=configure.in test -z "$configure_ac" \ || func_verbose "found '$configure_ac'" require_configure_ac=: } # require_gnu_m4 # -------------- # Search for GNU M4, and export it in $M4. require_gnu_m4=func_require_gnu_m4 func_require_gnu_m4 () { $debug_cmd test -n "$M4" || { # Find the first m4 binary that responds to --version. func_find_tool M4 gm4 gnum4 m4 } test -n "$M4" || func_fatal_error "\ Please install GNU M4, or 'export M4=/path/to/gnu/m4'." func_verbose "export M4='$M4'" # Make sure the search result is visible to subshells export M4 require_gnu_m4=: } ## --------------- ## ## Core functions. ## ## --------------- ## # This section contains the high level functions used when calling this # file as a script. 'func_extract_trace' is probably the only one that you # won't want to replace if you source this file into your own script. # func_extract_trace MACRO_NAMES [FILENAME]... # -------------------------------------------- # set '$func_extract_trace_result' to a colon delimited list of arguments # to any of the comma separated list of MACRO_NAMES in FILENAME. If no # FILENAME is given, then '$configure_ac' is assumed. func_extract_trace () { $debug_cmd $require_configure_ac $require_gnu_m4 _G_m4_traces=`$ECHO "--trace=$1" |$SED 's%,% --trace=%g'` _G_re_macros=`$ECHO "($1)" |$SED 's%,%|%g'` _G_macros="$1"; shift test $# -gt 0 || { set dummy $configure_ac shift } # Generate an error if the first file is missing <"$1" # Sadly, we can't use 'autom4te' tracing to extract macro arguments, # because it complains about things we want to ignore at bootstrap # time - like missing m4_include files; AC_PREREQ being newer than # the installed autoconf; and returns nothing when tracing # 'AM_INIT_AUTOMAKE' when aclocal hasn't been generated yet. # # The following tries to emulate a less persnickety version of (and # due to not having to wait for Perl startup on every invocation, # it's probably faster too): # # autom4te --language=Autoconf --trace=$my_macro:\$% "$@" # # First we give a minimal set of macro declarations to M4 to prime # it for reading Autoconf macros, while still providing some of the # functionality generally used at m4-time to supply dynamic # arguments to Autocof functions, but without following # 'm4_s?include' files. _G_mini=' dnl Initialisation. m4_changequote([,]) m4_define([m4_copy], [m4_define([$2], m4_defn([$1]))]) m4_define([m4_rename], [m4_copy([$1], [$2])m4_undefine([$1])]) dnl Replace macros which may abort m4 with a no-op variant. m4_pushdef([m4_assert]) m4_pushdef([m4_exit]) m4_pushdef([m4_fatal]) m4_pushdef([m4_m4exit]) dnl Replace macros that might break stderr of m4. m4_pushdef([m4_errprint]) m4_pushdef([m4_errprintn]) m4_pushdef([m4_include]) m4_pushdef([m4_warn]) dnl Avoid side-effects of tracing by extract-trace. m4_pushdef([m4_maketemp]) m4_pushdef([m4_mkstemp]) dnl TODO: reasons for this m4_pushdef([m4_dnl]) m4_pushdef([m4_m4wrap]) dnl Copy and rename macros not handled by "m4 --prefix". m4_define([dnl], [m4_builtin([dnl])]) m4_copy([m4_define], [m4_defun]) m4_rename([m4_ifelse], [m4_if]) m4_rename([m4_patsubst], [m4_bpatsubst]) m4_rename([m4_regexp], [m4_bregexp]) dnl "m4sugar.mini" - useful m4-time macros for dynamic arguments. dnl If we discover packages that need more m4 macros defined in dnl order to bootstrap correctly, add them here: m4_define([m4_bmatch], [m4_if([$#], 0, [], [$#], 1, [], [$#], 2, [$2], [m4_if(m4_bregexp([$1], [$2]), -1, [$0([$1], m4_shift3($@))], [$3])])]) m4_define([m4_ifndef], [m4_ifdef([$1], [$3], [$2])]) m4_define([m4_ifset], [m4_ifdef([$1], [m4_ifval(m4_defn([$1]), [$2], [$3])], [$3])]) m4_define([m4_require], [$1]) m4_define([m4_shift3], [m4_shift(m4shift(m4shift($@)))]) dnl "autoconf.mini" - things from autoconf macros we care about. m4_copy([m4_defun], [AC_DEFUN]) dnl Dummy definitions for the macros we want to trace. dnl AM_INIT_AUTOMAKE at least produces no trace without this. ' _G_save=$IFS IFS=, for _G_macro in $_G_macros; do IFS=$_G_save func_append _G_mini "AC_DEFUN([$_G_macro])$nl" done IFS=$_G_save # We discard M4's stdout, but the M4 trace output from reading our # "autoconf.mini" followed by any other files passed to this # function is then scanned by sed to transform it into a colon # delimited argument list assigned to a shell variable. _G_transform='s|#.*$||; s|^dnl .*$||; s| dnl .*$||;' # Unfortunately, alternation in regexp addresses doesn't work in at # least BSD (and hence Mac OS X) sed, so we have to append a capture # and print block for each traced macro to the sed transform script. _G_save=$IFS IFS=, for _G_macro in $_G_macros; do IFS=$_G_save func_append _G_transform ' /^m4trace: -1- '"$_G_macro"'/ { s|^m4trace: -1- '"$_G_macro"'[([]*|| s|], [[]|:|g s|[])]*$|:| s|\(.\):$|\1| p }' done IFS=$_G_save # Save the command pipeline results for further use by callers of # this function. func_extract_trace_result=`$ECHO "$_G_mini" \ |$M4 -daq --prefix $_G_m4_traces - "$@" 2>&1 1>/dev/null \ |$SED -n -e "$_G_transform"` } # func_extract_trace_first MACRO_NAMES [FILENAME]... # -------------------------------------------------- # Exactly like func_extract_trace, except that only the first argument # to the first invocation of one of the comma separated MACRO_NAMES is # returned in '$func_extract_trace_first_result'. func_extract_trace_first () { $debug_cmd func_extract_trace ${1+"$@"} func_extract_trace_first_result=`$ECHO "$func_extract_trace_result" \ |$SED -e 's|:.*$||g' -e 1q` } # func_main [ARG]... # ------------------ func_main () { $debug_cmd # Configuration. usage='$progname MACRO_NAME FILE [...]' long_help_message=' The first argument to this program is the name of an autotools macro whose arguments you want to extract by examining the files listed in the remaining arguments using the same tool that Autoconf and Automake use, GNU M4. The arguments are returned separated by colons, with each traced call on a separate line.' # Option processing. func_options "$@" eval set dummy "$func_options_result"; shift # Validate remaining non-option arguments. test $# -gt 1 \ || func_fatal_help "not enough arguments" # Pass non-option arguments to extraction function. func_extract_trace "$@" # Display results. test -n "$func_extract_trace_result" \ && $ECHO "$func_extract_trace_result" # The End. exit $EXIT_SUCCESS } ## --------------------------- ## ## Actually perform the trace. ## ## --------------------------- ## # Only call 'func_main' if this script was called directly. test extract-trace = "$progname" && func_main "$@" # Local variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-pattern: "50/scriptversion=%:y-%02m-%02d.%02H; # UTC" # time-stamp-time-zone: "UTC" # End: # Set a version string for *this* script. scriptversion=2019-03-22.11; # UTC ## ------------------- ## ## Hookable functions. ## ## ------------------- ## # After 'bootstrap.conf' has been sourced, execution proceeds by calling # 'func_bootstrap'. Wherever a function is decorated with # 'func_hookable func_name', you will find a matching 'func_run_hooks # func_name', which executes all functions added with 'func_add_hook # func_name my_func'. # # You might notice that many of these functions begin with a series of # '$require_foo' lines. See the docu-comments at the start of the # 'Resource management' section for a description of what these are. # func_bootstrap [ARG]... # ----------------------- # All the functions called inside func_bootstrap are hookable. See the # the individual implementations for details. func_bootstrap () { $debug_cmd # Save the current positional parameters to prevent them being # corrupted by calls to 'set' in 'func_init'. func_quote eval ${1+"$@"} _G_saved_positional_parameters=$func_quote_result # Initialisation. func_init # Option processing. eval func_options "$_G_saved_positional_parameters" # Post-option preparation. func_prep # Reconfigure the package. func_reconfigure # Ensure .version is up-to-date. func_update_dotversion # Finalisation. func_fini } # func_init # --------- # Any early initialisations can be hooked to this function. Consider # whether you can hook onto 'func_prep' instead, because if you hook # any slow to execute code in here, it will also add to the time before # './bootstrap --version' can respond. func_hookable func_init func_init () { $debug_cmd func_run_hooks func_init } # func_prep # --------- # Function to perform preparation for remaining bootstrap process. If # your hooked code relies on the outcome of 'func_options' hook it here # rather than to 'func_init'. # # All the functions called inside func_prep are hookable. See the # individual implementations for details. func_hookable func_prep func_prep () { $debug_cmd $require_buildtools_uptodate $require_checkout_only_file $require_gnulib_merge_changelog # Report the results of SED and GREP searches from funclib.sh. func_verbose "GREP='$GREP'" func_verbose "SED='$SED'" # fetch update files from the translation project func_update_translations func_run_hooks func_prep } # func_update_translations # ------------------------ # Update package po files and translations. func_hookable func_update_translations func_update_translations () { $debug_cmd $opt_skip_po || { test -d po && { $require_package func_update_po_files po $package || exit $? } func_run_hooks func_update_translations } } # func_reconfigure # ---------------- # Reconfigure the current package by running the appropriate autotools in a # suitable order. func_hookable func_reconfigure func_reconfigure () { $debug_cmd $require_automake_options # Automake (without 'foreign' option) requires that NEWS & README exist. case " $automake_options " in " foreign ") ;; *) func_ensure_NEWS func_ensure_README ;; esac # Ensure ChangeLog presence. if test -n "$gnulib_modules"; then func_ifcontains "$gnulib_modules" gitlog-to-changelog \ func_ensure_changelog else $require_gnulib_cache if $SED -n '/^gl_MODULES(\[/,/^])$/p' $gnulib_cache 2>/dev/null | func_grep_q gitlog-to-changelog then func_ensure_changelog fi fi # Released 'autopoint' has the tendency to install macros that have # been obsoleted in current 'gnulib', so run this before 'gnulib-tool'. func_autopoint # Autoreconf runs 'aclocal' before 'libtoolize', which causes spurious # warnings if the initial 'aclocal' is confused by the libtoolized # (or worse: out-of-date) macro directory. func_libtoolize # If you need to do anything after 'gnulib-tool' is done, but before # 'autoreconf' runs, you don't need to override this whole function, # because 'func_gnulib_tool' is hookable. func_gnulib_tool func_autoreconf func_run_hooks func_reconfigure } # func_gnulib_tool # ---------------- # Run 'gnulib-tool' to fetch gnulib modules into the current package. # # It's assumed that since you are using gnulib's 'bootstrap' script, # you're also using gnulib elsewhere in your package. If not, then # you can replace this function in 'bootstrap.conf' with: # # func_gnulib_tool () { :; } # # (although the function returns immediately if $gnulib_tool is set to # true in any case). func_hookable func_gnulib_tool func_gnulib_tool () { $debug_cmd $require_gnulib_tool $require_libtoolize test true = "$gnulib_tool" || { $require_gnulib_git_submodules # bootstrap.conf written for gnulib bootstrap expects # gnulib_tool_option_extras to which --no-changelog is appended, # but libtool bootstrap expects you to append to gnulib_tool_options # so that you can override the --no-changelog default: make sure we # support both styles so users can migrate between them easily. gnulib_tool_all_options="$gnulib_tool_options $gnulib_tool_option_extras" if test -n "$gnulib_modules"; then $require_gnulib_cache $require_gnulib_tool_base_options gnulib_mode=--import # Try not to pick up any stale values from 'gnulib-cache.m4'. rm -f "$gnulib_cache" test -n "$gnulib_tool_base_options" \ && func_append_uniq gnulib_tool_all_options " $gnulib_tool_base_options" test -n "$gnulib_mk" \ && func_append_uniq gnulib_tool_all_options " --makefile-name=$gnulib_mk" test -n "$tests_base" && { func_append_uniq gnulib_tool_all_options " --tests-base=$tests_base" func_append_uniq gnulib_tool_all_options " --with-tests" } else # 'gnulib_modules' and others are cached in 'gnulib-cache.m4': # Use 'gnulib --update' to fetch gnulib modules. gnulib_mode=--update fi # Add a sensible default libtool option to gnulib_tool_options. # The embedded echo is to squash whitespace before globbing. case `echo " "$gnulib_tool_all_options" "` in *" --no-libtool "*|*" --libtool "*) ;; *) if test true = "$LIBTOOLIZE"; then func_append_uniq gnulib_tool_all_options " --no-libtool" else func_append_uniq gnulib_tool_all_options " --libtool" fi ;; esac $opt_copy || func_append_uniq gnulib_tool_all_options " --symlink" func_append_uniq gnulib_tool_all_options " $gnulib_mode" func_append gnulib_tool_all_options " $gnulib_modules" # The embedded echo is to squash whitespace before display. gnulib_cmd=`echo $gnulib_tool $gnulib_tool_all_options` func_show_eval "$gnulib_cmd" 'exit $?' # Use 'gnulib-tool --copy-file' to install non-module files. func_install_gnulib_non_module_files } func_run_hooks func_gnulib_tool } # func_fini # --------- # Function to perform all finalisation for the bootstrap process. func_hookable func_fini func_fini () { $debug_cmd func_gettext_configuration func_clean_dangling_symlinks func_clean_unused_macros func_skip_po_recommendation func_run_hooks func_fini $require_bootstrap_uptodate func_echo "Done. Now you can run './configure'." } # func_gettext_configuration # -------------------------- # Edit configuration values into po/Makevars. func_hookable func_gettext_configuration func_gettext_configuration () { $debug_cmd $require_autopoint test true = "$AUTOPOINT" || { $require_copyright_holder $require_extra_locale_categories $require_package_bugreport # Escape xgettext options for sed Makevars generation below. # We have to delete blank lines in a separate script so that we don't # append \\\ to the penultimate line, and then delete the last empty # line, which messes up the variable substitution later in this # function. Note that adding a literal \\\ requires double escaping # here, once for the execution subshell, and again for the assignment, # which is why there are actually 12 (!!) backslashes in the script. _G_xgettext_options=`echo "$xgettext_options$nl" |$SED '/^$/d' |$SED ' $b s|$| \\\\\\\\\\\\|'` # Create gettext configuration. func_echo "Creating po/Makevars from po/Makevars.template ..." $RM -f po/Makevars $SED ' /^EXTRA_LOCALE_CATEGORIES *=/s|=.*|= '"$extra_locale_categories"'| /^COPYRIGHT_HOLDER *=/s|=.*|= '"$copyright_holder"'| /^MSGID_BUGS_ADDRESS *=/s|=.*|= '"$package_bugreport"'| /^XGETTEXT_OPTIONS *=/{ s|$| \\| a\ '"$_G_xgettext_options"' \\\ $${end_of_xgettext_options+} } s/ *$// ' po/Makevars.template >po/Makevars || exit 1 } func_run_hooks func_gettext_configuration } ## --------------- ## ## Core functions. ## ## --------------- ## # This section contains the main functions called from the 'Hookable # functions' (shown above), and are the ones you're most likely # to want to replace with your own implementations in 'bootstrap.conf'. # func_autopoint # -------------- # If this package uses gettext, then run 'autopoint'. func_autopoint () { $debug_cmd $require_autopoint test true = "$AUTOPOINT" \ || func_show_eval "$AUTOPOINT --force" 'exit $?' } # func_libtoolize # --------------- # If this package uses libtool, then run 'libtoolize'. func_libtoolize () { $debug_cmd $require_libtoolize test true = "$LIBTOOLIZE" || { _G_libtoolize_options= $opt_copy && func_append _G_libtoolize_options " --copy" $opt_force && func_append _G_libtoolize_options " --force" $opt_verbose || func_append _G_libtoolize_options " --quiet" func_show_eval "$LIBTOOLIZE$_G_libtoolize_options" 'exit $?' } } # func_gnulib_tool_copy_file SRC DEST # ----------------------------------- # Copy SRC, a path relative to the gnulib sub-tree, to DEST, a path # relative to the top-level source directory using gnulib-tool so that # any patches or replacements in $local_gl_path are applied. func_gnulib_tool_copy_file () { $debug_cmd $require_gnulib_tool $require_patch if test true = "$gnulib_tool"; then # If gnulib-tool is not available (e.g. bootstrapping in a # distribution tarball), make sure that at least we have some # version of the required file already in place. test -f "$2" || func_fatal_error "\ Can't find, copy or download '$2', a required gnulib supplied file, please provide the location of a complete 'gnulib' tree by setting 'gnulib_path' in your 'bootstrap.conf' or with the '--gnulib-srcdir' option - or else specify the location of your 'git' binary by setting 'GIT' in the environment so that a fresh 'gnulib' submodule can be cloned." else $require_gnulib_copy_cmd $gnulib_copy_cmd $1 $2 2>/dev/null || { $require_gnulib_path func_error "'$gnulib_path/$1' does not exist" return 1 } fi } # func_install_gnulib_non_module_files # ------------------------------------ # Get additional non-module files from gnulib, overriding existing files. func_install_gnulib_non_module_files () { $debug_cmd $require_build_aux $require_gnulib_tool test -n "$gnulib_non_module_files" && { maybe_exit_cmd=: for file in $gnulib_non_module_files; do case $file in */COPYING*) dest=COPYING;; */INSTALL) dest=INSTALL;; build-aux/missing) dest= func_warning settings "\ Please remove build-aux/missing from gnulib_module_files in 'bootstrap.conf', as it may clash with Automake's version." ;; build-aux/*) dest=$build_aux/`expr "$file" : 'build-aux/\(.*\)'`;; *) dest=$file;; esac # Be sure to show all copying errors before bailing out test -z "$dest" \ || func_gnulib_tool_copy_file "$file" "$dest" \ || maybe_exit_cmd="exit $EXIT_FAILURE" done $maybe_exit_cmd } } # func_ensure_changelog # --------------------- # Even with 'gitlog-to-changelog' generated ChangeLogs, automake # will not run to completion with no ChangeLog file. func_ensure_changelog () { $debug_cmd test -f ChangeLog && mv -f ChangeLog ChangeLog~ cat >ChangeLog <<'EOT' ## ---------------------- ## ## DO NOT EDIT THIS FILE! ## ## ---------------------- ## ChangeLog is generated by gitlog-to-changelog. EOT _G_message="creating dummy 'ChangeLog'" test -f ChangeLog~ \ && func_append _G_message ' (backup in ChangeLog~)' func_verbose "$_G_message" return 0 } # func_ensure_NEWS # ---------------- # Without AM_INIT_AUTOMAKE([foreign]), automake will not run to # completion with no NEWS file, even though NEWS.md or NEWS.txt # is often preferable. func_ensure_NEWS () { $debug_cmd test -f NEWS || { _G_NEWS= for _G_news in NEWS.txt NEWS.md NEWS.rst; do test -f "$_G_news" && break done test -f "$_G_news" && $LN_S $_G_news NEWS func_verbose "$LN_S $_G_news NEWS" } return 0 } # func_ensure_README # ------------------ # Without AM_INIT_AUTOMAKE([foreign]), automake will not run to # completion with no README file, even though README.md or README.txt # is often preferable. func_ensure_README () { $debug_cmd test -f README || { _G_README= for _G_readme in README.txt README.md README.rst; do test -f "$_G_readme" && break done test -f "$_G_readme" && $LN_S $_G_readme README func_verbose "$LN_S $_G_readme README" } return 0 } # func_autoreconf [SUBDIR] # ------------------------ # Being careful not to re-run 'autopoint' or 'libtoolize', and not to # try to run 'autopoint', 'libtoolize' or 'autoheader' on packages that # don't use them, defer to 'autoreconf' for execution of the remaining # autotools to bootstrap this package. # # Projects with multiple trees to reconfigure can hook another call to # this function onto func_reconfigure: # # my_autoreconf_foo () # { # func_autoreconf foo # } # func_add_hook func_reconfigure my_autoreconf_foo func_autoreconf () { $debug_cmd $require_autoheader $require_build_aux # automake and others put files in here $require_macro_dir # aclocal and others put files in here # We ran these manually already, and autoreconf won't exec ':' save_AUTOPOINT=$AUTOPOINT; AUTOPOINT=true save_LIBTOOLIZE=$LIBTOOLIZE; LIBTOOLIZE=true _G_autoreconf_options= $opt_copy || func_append _G_autoreconf_options " --symlink" $opt_force && func_append _G_autoreconf_options " --force" $opt_verbose && func_append _G_autoreconf_options " --verbose" func_show_eval "$AUTORECONF$_G_autoreconf_options --install${1+ $1}" 'exit $?' AUTOPOINT=$save_AUTOPOINT LIBTOOLIZE=$save_LIBTOOLIZE } # func_check_configuration VARNAME [CONFIGURE_MACRO] # -------------------------------------------------- # Exit with a suitable diagnostic for an important configuration change # that needs to be made before bootstrap can run correctly. func_check_configuration () { $debug_cmd $require_configure_ac eval 'test -n "$'$1'"' || { _G_error_msg="please set '$1' in 'bootstrap.conf'" if test -n "$configure_ac" && test -n "$2"; then func_append _G_error_msg " or add the following (or similar) to your '$configure_ac': $2" fi func_fatal_error "$_G_error_msg" } } # func_clean_dangling_symlinks # ---------------------------- # Remove any dangling symlink matching "*.m4" or "*.[ch]" in some # gnulib-populated directories. Such .m4 files would cause aclocal to # fail. The following requires GNU find 4.2.3 or newer. Considering # the usual portability constraints of this script, that may seem a very # demanding requirement, but it should be ok. Ignore any failure, # which is fine, since this is only a convenience to help developers # avoid the relatively unusual case where a symlinked-to .m4 file is # git-removed from gnulib between successive runs of this script. func_clean_dangling_symlinks () { $debug_cmd $require_macro_dir $require_source_base func_verbose "cleaning dangling symlinks" find "$macro_dir" "$source_base" \ -depth \( -name '*.m4' -o -name '*.[ch]' \) \ -type l -xtype l -delete > /dev/null 2>&1 } # func_clean_unused_macros # ------------------------ # Autopoint can result in over-zealously adding macros into $macro_dir # even though they are not actually used, for example tests to help # build the 'intl' directory even though you have specified # 'AM_GNU_GETTEXT([external])' in your configure.ac. This function # looks removes any macro files that can be found in gnulib, but # are not 'm4_include'd by 'aclocal.m4'. func_clean_unused_macros () { $debug_cmd $require_gnulib_path $require_macro_dir test -n "$gnulib_path" && test -f aclocal.m4 && { aclocal_m4s=`find . -name aclocal.m4 -print` # We use 'ls|grep' instead of 'ls *.m4' to avoid exceeding # command line length limits in some shells. for file in `cd "$macro_dir" && ls -1 |$GREP '\.m4$'`; do # Remove a macro file when aclocal.m4 does not m4_include it... func_grep_q 'm4_include([[]'$macro_dir/$file'])' $aclocal_m4s \ || test ! -f "$gnulib_path/m4/$file" || { # ...and there is an identical file in gnulib... if func_cmp_s "$gnulib_path/m4/$file" "$macro_dir/$file"; then # ...and it's not in the precious list ('echo' is needed # here to squash whitespace for the match expression). case " "`echo $gnulib_precious`" " in *" $file "*) ;; *) rm -f "$macro_dir/$file" func_verbose \ "removing unused gnulib file '$macro_dir/$file'" esac fi } done } } # func_skip_po_recommendation # --------------------------- # If there is a po directory, and '--skip-po' wasn't passed, let the # user know that they can use '--skip-po' on subsequent invocations. func_skip_po_recommendation () { $debug_cmd test ! -d po \ || $opt_skip_po \ || func_warning recommend "\ If your pofiles are up-to-date, you can rerun bootstrap as '$progname --skip-po' to avoid redownloading." } # func_update_dotversion # ---------------------- # Even with 'gitlog-to-changelog' generated ChangeLogs, automake # will not run to completion with no ChangeLog file. func_update_dotversion () { $debug_cmd test -f "$build_aux/git-version-gen" && { _G_message="updating .version" test -f .version && { mv .version .version~ func_append _G_message " (backup in .version~)" } func_verbose "updating .version" $build_aux/git-version-gen dummy-arg > .version } } ## -------------------- ## ## Resource management. ## ## -------------------- ## # This section contains definitions for functions that each ensure a # particular resource (a file, or a non-empty configuration variable for # example) is available, and if appropriate to extract default values # from pertinent package files. Where a variable already has a non- # empty value (as set by the package's 'bootstrap.conf'), that value is # used in preference to deriving the default. Call them using their # associated 'require_*' variable to ensure that they are executed, at # most, once. # require_gnulib_git_submodules # ----------------------------- # Initialize all git modules from $gnulib_git_submodules before we # run 'gnulib-tool'. require_gnulib_git_submodules=func_require_gnulib_git_submodules func_require_gnulib_git_submodules () { test -n "$gnulib_git_submodules" && { for _G_submodule in $gnulib_git_submodules do func_show_eval "git submodule init -- $_G_submodule" \ && func_show_eval "git submodule update -- $_G_submodule" \ || func_fatal_error "Unable to init git module '$_G_submodule'." done } require_gnulib_git_submodules=: } # require_checkout_only_file # -------------------------- # Bail out if this package only bootstraps properly from a repository # checkout. require_checkout_only_file=func_require_checkout_only_file func_require_checkout_only_file () { $debug_cmd $opt_force || { test -n "$checkout_only_file" && test ! -f "$checkout_only_file" \ && func_fatal_error "\ Bootstrapping from a non-checked-out distribution is risky. If you wish to bootstrap anyway, use the '--force' option." } require_checkout_only_file=: } # require_aclocal_amflags # ----------------------- # Ensure '$aclocal_amflags' has a sensible default, extracted from # 'Makefile.am' if necessary. require_aclocal_amflags=func_require_aclocal_amflags func_require_aclocal_amflags () { $debug_cmd $require_makefile_am _G_sed_extract_aclocal_amflags='s|#.*$|| /^[ ]*ACLOCAL_AMFLAGS[ ]*=/ { s|^.*=[ ]*\(.*\)|aclocal_amflags="\1"| p }' _G_aclocal_flags_cmd=`$SED -n "$_G_sed_extract_aclocal_amflags" \ "$makefile_am"` eval "$_G_aclocal_flags_cmd" func_verbose "ACLOCAL_AMFLAGS='$aclocal_amflags'" require_aclocal_amflags=: } # require_autoheader # ------------------ # Skip autoheader if it's not needed. require_autoheader=func_require_autoheader func_require_autoheader () { $debug_cmd test true = "$AUTOHEADER" || { func_extract_trace AC_CONFIG_HEADERS test -n "$func_extract_trace_result" \ || func_extract_trace AC_CONFIG_HEADER test -n "$func_extract_trace_result" || { AUTOHEADER=true func_verbose "export AUTOHEADER='$AUTOHEADER'" # Make sure the search result is visible to subshells export AUTOHEADER } } require_autoheader=: } # require_automake_options # ------------------------ # Extract options from AM_AUTOMAKE_INIT. require_automake_options=func_require_automake_options func_require_automake_options () { $debug_cmd func_extract_trace AM_INIT_AUTOMAKE automake_options=$func_extract_trace_result require_automake_options=: } # require_autopoint # ----------------- # Skip autopoint if it's not needed. require_autopoint=func_require_autopoint func_require_autopoint () { $debug_cmd test true = "$AUTOPOINT" || { func_extract_trace AM_GNU_GETTEXT_VERSION test -n "$func_extract_trace_result" || { AUTOPOINT=true func_verbose "export AUTOPOINT='$AUTOPOINT'" # Make sure the search result is visible to subshells export AUTOPOINT } } require_autopoint=: } # require_bootstrap_uptodate # -------------------------- # Complain if the version of bootstrap in the gnulib directory differs # from the one we are running. require_bootstrap_uptodate=func_require_bootstrap_uptodate func_require_bootstrap_uptodate () { $debug_cmd $require_build_aux _G_bootstrap_sources=" $build_aux/bootstrap.in $build_aux/extract-trace $build_aux/funclib.sh $build_aux/options-parser " _G_missing_bootstrap_sources=false for _G_src in $_G_bootstrap_sources; do test -f "$_G_src" || _G_missing_bootstrap_sources=: done if $_G_missing_bootstrap_sources; then func_warning upgrade "\ Please add bootstrap to your gnulib_modules list in 'bootstrap.conf', so that I can tell you when there are updates available." else rm -f bootstrap.new $build_aux/inline-source $build_aux/bootstrap.in > bootstrap.new if func_cmp_s "$progpath" bootstrap.new; then rm -f bootstrap.new func_verbose "bootstrap script up to date" else chmod 555 bootstrap.new func_warning upgrade "\ An updated bootstrap script has been generated for you in 'bootstrap.new'. After you've verified that you want the changes, you can update with: mv -f bootstrap.new $progname ./$progname Or you can disable this check permanently by adding the following to 'bootstrap.conf': require_bootstrap_uptodate=:" fi fi require_bootstrap_uptodate=: } # require_build_aux # ----------------- # Ensure that '$build_aux' is set, and if it doesn't already point to an # existing directory, create one. require_build_aux=func_require_build_aux func_require_build_aux () { $debug_cmd test -n "$build_aux" || { func_extract_trace_first AC_CONFIG_AUX_DIR build_aux=$func_extract_trace_first_result func_check_configuration build_aux \ "AC_CONFIG_AUX_DIR([name of a directory for build scripts])" func_verbose "build_aux='$build_aux'" } $require_vc_ignore_files # If the build_aux directory doesn't exist, create it now, and mark it # as ignored for the VCS. if test ! -d "$build_aux"; then func_show_eval "mkdir '$build_aux'" test -n "$vc_ignore_files" \ || func_insert_if_absent "$build_aux" $vc_ignore_files fi require_build_aux=: } # require_buildreq_autobuild # -------------------------- # Try to find whether the bootstrap requires autobuild. require_buildreq_autobuild=func_require_buildreq_autobuild func_require_buildreq_autobuild () { $debug_cmd $require_macro_dir test -f "$macro_dir/autobuild.m4" \ || printf '%s\n' "$buildreq" |func_grep_q '^[ ]*autobuild' \ || { func_extract_trace AB_INIT test -n "$func_extract_trace_result" && { func_append buildreq 'autobuild - http://josefsson.org/autobuild/ ' func_verbose "auto-adding 'autobuild' to build requirements" } } require_buildreq_autobuild=: } # require_buildreq_autoconf # require_buildreq_autopoint # require_buildreq_libtoolize # --------------------------- # Try to find the minimum compatible version of autoconf/libtool # required to bootstrap successfully, and add it to '$buildreq'. for tool in autoconf libtoolize autopoint; do b=$tool v=require_buildreq_${tool} f=func_$v case $tool in autoconf) m=AC_PREREQ ;; libtoolize) m=LT_PREREQ; b=libtool ;; autopoint) m=AM_GNU_GETTEXT_VERSION b=gettext ;; esac eval $v'='$f' '$f' () { $debug_cmd # The following is ignored if undefined, but might be necessary # in order for `func_find_tool` to run. ${require_'$tool'-:} printf '\''%s\n'\'' "$buildreq" |func_grep_q '\''^[ ]*'$tool\'' || { func_extract_trace '$m' _G_version=$func_extract_trace_result test -n "$_G_version" && { func_append buildreq "\ '$tool' $_G_version https://www.gnu.org/s/'$b' " func_verbose \ "auto-adding '\'$tool'-$_G_version'\'' to build requirements" } } '$v'=: } ' done # require_buildreq_automake # ------------------------- # Try to find the minimum compatible version of automake required to # bootstrap successfully, and add it to '$buildreq'. require_buildreq_automake=func_require_buildreq_automake func_require_buildreq_automake () { $debug_cmd # if automake is not already listed in $buildreq... printf '%s\n' "$buildreq" |func_grep_q automake || { func_extract_trace AM_INIT_AUTOMAKE # ...and AM_INIT_AUTOMAKE is declared... test -n "$func_extract_trace_result" && { automake_version=`$ECHO "$func_extract_trace_result" \ |$SED -e 's|[^0-9]*||' -e 's| .*$||'` test -n "$automake_version" || automake_version=- func_append buildreq "\ automake $automake_version https://www.gnu.org/s/automake " func_verbose \ "auto-adding 'automake-$automake_version' to build requirements" } } require_buildreq_automake=: } # require_buildreq_patch # ---------------------- # Automatically add a patch build-requirement if there are diff files # in $local_gl_path. require_buildreq_patch=func_require_buildreq_patch func_require_buildreq_patch () { $debug_cmd $require_local_gl_path # This ensures PATCH is set appropriately by the time # func_check_versions enforces $buildreq. $require_patch # If patch is not already listed in $buildreq... printf '%s\n' "$buildreq" |func_grep_q '^[ ]*patch' || { eval "set dummy $local_gl_path_quoted" ; shift for _G_dir do # The ugly find invocation is necessary to exit with non-zero # status for old find binaries that don't support -exec fully. if test ! -d "$_G_dir" \ || find "$_G_dir" -name "*.diff" -exec false {} \; ; then : else func_append buildreq "patch - https://www.gnu.org/s/patch$nl" break fi done } require_buildreq_patch=: } # require_buildtools_uptodate # --------------------------- # Ensure all the packages listed in BUILDREQS are available on the build # machine at the minimum versions or better. require_buildtools_uptodate=func_require_buildtools_uptodate func_require_buildtools_uptodate () { $debug_cmd $require_buildreq_autobuild $require_buildreq_autoconf $require_buildreq_automake $require_buildreq_libtoolize $require_buildreq_autopoint $require_buildreq_patch test -n "$buildreq" && { _G_error_hdr= func_check_versions $buildreq $func_check_versions_result || { test -n "$buildreq_readme" \ && test -f "$buildreq_readme" \ && _G_error_hdr="\ $buildreq_readme explains how to obtain these prerequisite programs: " func_strtable 0 11 12 36 \ "Program" "Min_version" "Homepage" $buildreq func_fatal_error "$_G_error_hdr$func_strtable_result" } } require_buildtools_uptodate=: } # require_copyright_holder # ------------------------ # Ensure there is a sensible non-empty default value in '$copyright_holder'. require_copyright_holder=func_require_copyright_holder func_require_copyright_holder () { $debug_cmd test -n "$copyright_holder" || { copyright_holder='Free Software Foundation, Inc.' func_warning settings "\ Please set copyright_holder explicitly in 'bootstrap.conf'; defaulting to '$copyright_holder'." } require_copyright_holder=: } # require_doc_base # ---------------- # Ensure doc_base has a sensible value, extracted from 'gnulib-cache.m4' # if possible, otherwise letting 'gnulib-tool' pick a default. require_doc_base=func_require_doc_base func_require_doc_base () { $debug_cmd $require_gnulib_cache test -f "$gnulib_cache" && test -z "$doc_base" && { func_extract_trace_first "gl_DOC_BASE" "$gnulib_cache" doc_base=$func_extract_trace_first_result test -n "$doc_base" && func_verbose "doc_base='$doc_base'" } require_doc_base=: } # require_dotgitmodules # --------------------- # Ensure we have a '.gitmodules' file, with appropriate 'gnulib' settings. require_dotgitmodules=func_require_dotgitmodules func_require_dotgitmodules () { $debug_cmd $require_git test true = "$GIT" || { # A gnulib entry in .gitmodules always takes precedence. _G_path=`$GIT config --file .gitmodules submodule.gnulib.path 2>/dev/null` test -n "$_G_path" || { $require_vc_ignore_files func_verbose "creating '.gitmodules'" # If the .gitmodules file doesn't exist, create it now, and mark # it as ignored for the VCS. test -n "$gnulib_path" || gnulib_path=gnulib test -n "$gnulib_url" || gnulib_url=git://git.sv.gnu.org/gnulib { echo '[submodule "gnulib"]' echo " path = $gnulib_path" echo " url = $gnulib_url" } >> .gitmodules test -n "$vc_ignore_files" \ || func_insert_if_absent ".gitmodules" $vc_ignore_files } } require_dotgitmodules=: } # require_extra_locale_categories # ------------------------------- # Ensure there is a default value in '$extra_locale_categories' require_extra_locale_categories=func_require_extra_locale_categories func_require_extra_locale_categories () { $debug_cmd # Defaults to empty, so run with whatever value may have been set in # 'bootstrap.conf'. require_extra_locale_categories=: } # require_git # ----------- # Ignore git if it's not available, or we're not in a git checkout tree. require_git=func_require_git func_require_git () { $debug_cmd $opt_skip_git && GIT=true test true = "$GIT" || { if test -d .git/.; then ($GIT --version) >/dev/null 2>&1 || GIT=true fi } func_verbose "GIT='$GIT'" require_git=: } # require_gnulib_cache # -------------------- # Ensure there is a non-empty default for '$gnulib_cache', and that it # names an existing file. require_gnulib_cache=func_require_gnulib_cache func_require_gnulib_cache () { $debug_cmd $require_macro_dir test -n "$gnulib_cache" \ || gnulib_cache=$macro_dir/gnulib-cache.m4 func_verbose "found '$gnulib_cache'" require_gnulib_cache=: } # require_gnulib_copy_cmd # ----------------------- # Only calculate the options for copying files with gnulib once. require_gnulib_copy_cmd=func_require_gnulib_copy_cmd func_require_gnulib_copy_cmd () { $debug_cmd $require_gnulib_tool $require_gnulib_tool_base_options gnulib_copy_cmd="$gnulib_tool $gnulib_tool_base_options --copy-file" $opt_copy || func_append gnulib_copy_cmd " --symlink" $opt_quiet || func_append gnulib_copy_cmd " --verbose" require_gnulib_copy_cmd=: } # require_gnulib_merge_changelog # ------------------------------ # See if we can use gnulib's git-merge-changelog merge driver. require_gnulib_merge_changelog=func_require_gnulib_merge_changelog func_require_gnulib_merge_changelog () { $debug_cmd test -f ChangeLog && { $require_git func_grep_q '^\(/\|\)ChangeLog$' .gitignore || test true = "$GIT" || { if $GIT config merge.merge-changelog.driver >/dev/null; then : elif (git-merge-changelog --version) >/dev/null 2>&1; then func_echo "initializing git-merge-changelog driver" $GIT config merge.merge-changelog.name 'GNU-style ChangeLog merge driver' $GIT config merge.merge-changelog.driver 'git-merge-changelog %O %A %B' else func_warning recommend \ "Consider installing git-merge-changelog from gnulib." fi } } require_gnulib_merge_changelog=: } # require_gnulib_mk # ----------------- # Ensure gnulib_mk has a sensible value, extracted from 'gnulib-cache.m4' # if possible, otherwise letting 'gnulib-tool' pick a default. require_gnulib_mk=func_require_gnulib_mk func_require_gnulib_mk () { $debug_cmd $require_gnulib_cache test -f "$gnulib_cache" && test -z "$gnulib_mk" && { func_extract_trace_first "gl_MAKEFILE_NAME" "$gnulib_cache" gnulib_mk=$func_extract_trace_first_result test -n "$gnulib_mk" && func_verbose "gnulib_mk='$gnulib_mk'" } require_gnulib_mk=: } # require_gnulib_name # ------------------- # Ensure gnulib_name has a sensible value, extracted from 'gnulib-cache.m4' # if possible, otherwise letting 'gnulib-tool' pick a default. require_gnulib_name=func_require_gnulib_name func_require_gnulib_name () { $debug_cmd $require_gnulib_cache test -f "$gnulib_cache" && test -z "$gnulib_name" && { func_extract_trace_first "gl_LIB" "$gnulib_cache" gnulib_name=$func_extract_trace_first_result test -n "$gnulib_name" && func_verbose "gnulib_name='$gnulib_name'" } require_gnulib_name=: } # require_gnulib_path # require_gnulib_url # ------------------- # Ensure 'gnulib_path' and 'gnulib_url' are set. require_gnulib_path=func_require_dotgitmodules_parameters require_gnulib_url=func_require_dotgitmodules_parameters func_require_dotgitmodules_parameters () { $debug_cmd $require_git test true = "$GIT" && { # If we can't find git (or if the user specified '--skip-git'), # then use an existing gnulib directory specified with # '--gnulib-srcdir' if possible. test -n "$gnulib_path" \ || test ! -x "$opt_gnulib_srcdir/gnulib-tool" \ || gnulib_path=$opt_gnulib_srcdir } $require_dotgitmodules test -f .gitmodules && { # Extract the parameters with sed, since git may be missing test -n "$gnulib_path" \ || gnulib_path=`$SED -e '/^.submodule "gnulib".$/,${ /[ ]*path *= */{ s|[ ]*||g;s|^[^=]*=||;p } } d' .gitmodules |$SED 1q` test -n "$gnulib_url" \ || gnulib_url=`$SED -e '/^.submodule "gnulib".$/,${ /[ ]*url *= */{ s|[ ]*||g;s|^[^=]*=||;p } } d' .gitmodules |$SED 1q` func_verbose "gnulib_path='$gnulib_path'" func_verbose "gnulib_url='$gnulib_url'" } require_gnulib_path=: require_gnulib_url=: } # require_gnulib_submodule # ------------------------ # Ensure that there is a current gnulib submodule at '$gnulib_path'. require_gnulib_submodule=func_require_gnulib_submodule func_require_gnulib_submodule () { $debug_cmd $require_git if test true = "$GIT"; then func_warning recommend \ "No 'git' found; imported gnulib modules may be outdated." else $require_gnulib_path $require_gnulib_url if test -f .gitmodules && test -f "$gnulib_path/gnulib-tool"; then : All present and correct. elif test -n "$opt_gnulib_srcdir"; then # Older git can't clone into an empty directory. rmdir "$gnulib_path" 2>/dev/null func_show_eval "$GIT clone --reference '$opt_gnulib_srcdir' \ '$gnulib_url' '$gnulib_path'" \ || func_fatal_error "Unable to fetch gnulib submodule." # Without --gnulib-srcdir, and no existing checked out submodule, we # create a new shallow clone of the remote gnulib repository. else trap func_cleanup_gnulib 1 2 13 15 shallow= test -n "$gnulib_clone_since" && \ $GIT clone -h 2>&1 |func_grep_q -- --shallow-since \ && shallow="--shallow-since=$gnulib_clone_since" func_show_eval "$GIT clone $shallow '$gnulib_url' '$gnulib_path'" \ func_cleanup_gnulib # FIXME: Solaris /bin/sh will try to execute '-' if any of # these signals are caught after this. trap - 1 2 13 15 fi # Make sure we've checked out the correct revision of gnulib. func_show_eval "$GIT submodule init -- $gnulib_path" \ && func_show_eval "$GIT submodule update -- $gnulib_path" \ || func_fatal_error "Unable to update gnulib submodule." fi require_gnulib_submodule=: } # require_gnulib_tool # ------------------- # Ensure that '$gnulib_tool' is set, and points to an executable file, # or else fall back to using the binary 'true' if the main gnulib # files appear to have been imported already. require_gnulib_tool=func_require_gnulib_tool func_require_gnulib_tool () { $debug_cmd test true = "$gnulib_tool" || { $require_gnulib_submodule $require_gnulib_path test -n "$gnulib_tool" \ || gnulib_tool=$gnulib_path/gnulib-tool test -x "$gnulib_tool" || { gnulib_tool=true func_warning recommend \ "No 'gnulib-tool' found; gnulib modules may be missing." } test true = "$gnulib_tool" \ || func_verbose "found '$gnulib_tool'" } require_gnulib_tool=: } # require_gnulib_tool_base_options # -------------------------------- # Ensure that '$gnulib_tool_base_options' contains all the base options # required according to user configuration from bootstrap.conf. require_gnulib_tool_base_options=func_require_gnulib_tool_base_options func_require_gnulib_tool_base_options () { $debug_cmd $require_gnulib_tool gnulib_tool_base_options= test true = "$gnulib_tool" || { # 'gnulib_modules' and others are maintained in 'bootstrap.conf': # Use 'gnulib --import' to fetch gnulib modules. $require_build_aux test -n "$build_aux" \ && func_append_uniq gnulib_tool_base_options " --aux-dir=$build_aux" $require_macro_dir test -n "$macro_dir" \ && func_append_uniq gnulib_tool_base_options " --m4-base=$macro_dir" $require_doc_base test -n "$doc_base" \ && func_append_uniq gnulib_tool_base_options " --doc-base=$doc_base" $require_gnulib_name test -n "$gnulib_name" \ && func_append_uniq gnulib_tool_base_options " --lib=$gnulib_name" $require_local_gl_path test -n "$local_gl_path" && { eval "set dummy $local_gl_path_quoted" ; shift for _G_dir do func_append_uniq gnulib_tool_base_options " --local-dir=$_G_dir" done } $require_source_base test -n "$source_base" \ && func_append_uniq gnulib_tool_base_options " --source-base=$source_base" } require_gnulib_tool_base_options=: } # require_libtoolize # ------------------ # Skip libtoolize if it's not needed. require_libtoolize=func_require_libtoolize func_require_libtoolize () { $debug_cmd # Unless we're not searching for libtool use by this package, set # LIBTOOLIZE to true if none of 'LT_INIT', 'AC_PROG_LIBTOOL' and # 'AM_PROG_LIBTOOL' are used in configure. test true = "$LIBTOOLIZE" || { func_extract_trace LT_INIT test -n "$func_extract_trace_result" || func_extract_trace AC_PROG_LIBTOOL test -n "$func_extract_trace_result" || func_extract_trace AM_PROG_LIBTOOL test -n "$func_extract_trace_result" || LIBTOOLIZE=true } test -n "$LIBTOOLIZE" || { # Find libtoolize, named glibtoolize in Mac Ports, but prefer # user-installed libtoolize to ancient glibtoolize shipped by # Apple with Mac OS X when Mac Ports is not installed. func_find_tool LIBTOOLIZE libtoolize glibtoolize } test -n "$LIBTOOLIZE" || func_fatal_error "\ Please install GNU Libtool, or 'export LIBTOOLIZE=/path/to/libtoolize'." func_verbose "export LIBTOOLIZE='$LIBTOOLIZE'" # Make sure the search result is visible to subshells export LIBTOOLIZE require_libtoolize=: } # require_local_gl_path # --------------------- # Ensure local_gl_path has a sensible value, extracted from 'gnulib-cache.m4' if # possible, otherwise letting 'gnulib-tool' pick a default. require_local_gl_path=func_require_local_gl_path func_require_local_gl_path () { $debug_cmd $require_gnulib_cache # Compat with older bootstrap versions. test -n "$local_gl_dir" && { func_warning settings "\ Please use 'local_gl_path' instead of 'local_gl_dir' in your 'bootstrap.conf' file." local_gl_path=$local_gl_dir local_gl_dir= } test -f "$gnulib_cache" && test -z "$local_gl_path" && { func_extract_trace_first "gl_LOCAL_DIR" "$gnulib_cache" local_gl_path=$func_extract_trace_first_result test -n "$local_gl_path" && func_verbose "local_gl_path='$local_gl_path'" } test -z "$local_gl_path_quoted" && test -n "$local_gl_path" && { save_IFS=$IFS set dummy # Don't use PATH_SEPARATOR here, gnulib must be fixed to store only ':' as # path separator into gnulib-cache.m4 (consistency reasons among systems). IFS=: for _G_dir in $local_gl_path do set "$@" "$_G_dir" done shift IFS=$save_IFS func_quote eval "$@" local_gl_path_quoted=$func_quote_result } require_local_gl_path=: } # require_macro_dir # ----------------- # Ensure that '$macro_dir' is set, and if it doesn't already point to an # existing directory, create one. require_macro_dir=func_require_macro_dir func_require_macro_dir () { $debug_cmd # Sometimes this is stored in 'configure.ac'. test -n "$macro_dir" || { # AC_CONFIG_MACRO_DIRS takes a space delimited list of directories, # but we only care about the first one in bootstrap. func_extract_trace_first AC_CONFIG_MACRO_DIRS macro_dir=`expr "x$func_extract_trace_first_result" : 'x\([^ ]*\)'` } test -n "$macro_dir" || { func_extract_trace_first AC_CONFIG_MACRO_DIR macro_dir=$func_extract_trace_first_result } # Otherwise we might find it in 'Makefile.am'. test -n "$macro_dir" || { $require_aclocal_amflags # Take the argument following the first '-I', if any. _G_minus_I_seen=false for _G_arg in $aclocal_amflags; do case $_G_minus_I_seen,$_G_arg in :,*) macro_dir=$_G_arg; break ;; *,-I) _G_minus_I_seen=: ;; *,-I*) macro_dir=`expr x$_G_arg : 'x-I\(.*\)$'`; break ;; esac done } func_verbose "macro_dir='$macro_dir'" func_check_configuration macro_dir \ "AC_CONFIG_MACRO_DIRS([name of a directory for configure m4 files])" $require_vc_ignore_files # If the macro_dir directory doesn't exist, create it now, and mark it # as ignored for the VCS. if test ! -d "$macro_dir"; then mkdir "$macro_dir" || func_permissions_error "$macro_dir" test -n "$vc_ignore_files" \ || func_insert_if_absent "$macro_dir" $vc_ignore_files fi require_macro_dir=: } # require_makefile_am # ------------------- # Ensure there is a 'Makefile.am' in the current directory. require_makefile_am=func_require_makefile_am func_require_makefile_am () { $debug_cmd test -n "$makefile_am" \ || makefile_am=Makefile.am <"$makefile_am" func_verbose "found '$makefile_am'" require_makefile_am=: } # require_package # --------------- # Ensure that '$package' contains a sensible default value. require_package=func_require_package func_require_package () { $debug_cmd test -n "$package" || { $require_package_name package=`echo "$package_name" \ |$SED -e 's/GNU //' \ -e 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/'` } func_verbose "package='$package'" require_package=: } # require_package_bugreport # ------------------------- # Ensure that this has a sensible value, extracted from 'configure.ac' # if appropriate (and possible!). require_package_bugreport=func_require_package_bugreport func_require_package_bugreport () { $debug_cmd func_extract_trace AC_INIT save_ifs=$IFS IFS=: set dummy $func_extract_trace_result IFS=$save_ifs shift test -n "$package_bugreport" || package_bugreport=$3 func_check_configuration package_bugreport \ "AC_INIT([$package_name], [$package_version], [bug-$package@gnu.org])" func_verbose "package_bugreport='$package_bugreport'" require_package_bugreport=: } # require_package_name # -------------------- # Ensure that this has a sensible value, extracted from 'configure.ac' # if appropriate (and possible!). require_package_name=func_require_package_name func_require_package_name () { $debug_cmd func_extract_trace AC_INIT save_ifs=$IFS IFS=: set dummy $func_extract_trace_result IFS=$save_ifs shift test -n "$package_name" || package_name=$1 func_check_configuration package_name \ "AC_INIT([name of your package], [package version number])" func_verbose "package_name='$package_name'" require_package_name=: } # require_package_version # ----------------------- # Ensure that this has a sensible value, extracted from 'configure.ac' # if appropriate (and possible!). While we might have set all the # parameters extracted from AC_INIT at once, 'package_version' in # particular is not necessarily available as early as the others, since # 'git-version-gen' is often involved, and until then we can't rely on # getting a correct version number from an AC_INIT extraction. require_package_version=func_require_package_version func_require_package_version () { $debug_cmd func_extract_trace AC_INIT save_ifs=$IFS IFS=: set dummy $func_extract_trace_result IFS=$save_ifs shift test -n "$package_version" || package_version=$2 test -n "$package_version" || { # The embedded echo is to squash whitespace before globbing. case " "`echo $gnulib_modules`" " in *" git-version-gen "*) func_fatal_error "\ cannot \$require_package_version in bootstrap.conf before func_gnulib_tool has installed the 'git-version-gen' script." ;; *) func_check_configuration package_version \ "AC_INIT([name of your package], [package version number])" ;; esac } func_verbose "package_version='$package_version'" require_package_version=: } # require_patch # ------------- # Find patch, according to the PATCH environment variable, or else # searching the user's PATH. require_patch=func_require_patch func_require_patch () { $debug_cmd test -n "$PATCH" || { # Find a patch program, preferring gpatch, which is usually better # than the vendor patch. func_find_tool PATCH gpatch patch } test -n "$PATCH" || func_fatal_error "\ Please install GNU Patch, or 'export PATCH=/path/to/gnu/patch'." func_verbose "export PATCH='$PATCH'" # Make sure the search result is visible to subshells export PATCH require_patch=: } # require_source_base # ------------------- # Ensure that source_base has a sensible value, extracted from # 'gnulib-cache.m4' if possible. require_source_base=func_require_source_base func_require_source_base () { $debug_cmd $require_gnulib_cache test -f "$gnulib_cache" && test -z "$source_base" && { func_extract_trace_first "gl_SOURCE_BASE" "$gnulib_cache" source_base=$func_extract_trace_first_result func_verbose "source_base='$source_base'" } require_source_base=: } # require_vc_ignore_files # ----------------------- # Ensure that '$vc_ignore' has been processed to list VCS ignore files # in '$vc_ignore_files' require_vc_ignore_files=func_require_vc_ignore_files func_require_vc_ignore_files () { $debug_cmd test -n "$vc_ignore" || vc_ignore=auto if test auto = "$vc_ignore" && test -z "$vc_ignore_files"; then vc_ignore_files= test -d .git && vc_ignore_files=.gitignore test -d CVS && vc_ignore_files="$vc_ignore_files .cvsignore" else vc_ignore_files=$vc_ignore fi func_verbose "vc_ignore_files='$vc_ignore_files'" require_vc_ignore_files=: } ## ----------------- ## ## Helper functions. ## ## ----------------- ## # This section contains the helper functions used by the rest of 'bootstrap'. # func_len STRING # --------------- # STRING may not start with a hyphen. if (eval 'x=123; test x${#x} = "x3"') 2>/dev/null then # This is an XSI compatible shell, allowing a faster implementation... eval 'func_len () { $debug_cmd func_len_result=${#1} }' else # ...otherwise fall back to using expr, which is often a shell builtin. func_len () { $debug_cmd func_len_result=`expr "$1" : ".*" 2>/dev/null || echo 0` } fi # func_cmp_s FILE1 FILE2 # ---------------------- # Return non-zero exit status unless FILE1 and FILE2 are identical, without # any output at all, even error messages. func_cmp_s () { $debug_cmd # This function relies on non-zero exit status, which will cause the # program to exit when running in 'set -e' mode. $CMP "$@" >/dev/null 2>&1 } # func_grep_q EXPRESSION [FILENAME..] # ----------------------------------- # Check whether EXPRESSION matches any line of any listed FILENAME, # without any output at all, even error messages. func_grep_q () { $debug_cmd # This function relies on non-zero exit status, which will cause the # program to exit when running in 'set -e' mode. $GREP "$@" >/dev/null 2>&1 } # func_ifcontains LIST MEMBER YES-CMD [NO-CMD] # -------------------------------------------- # If whitespace-separated LIST contains MEMBER then execute YES-CMD, # otherwise if NO-CMD was given, execute that. func_ifcontains () { $debug_cmd _G_wslist=$1 _G_member=$2 _G_yes_cmd=$3 _G_no_cmd=${4-":"} _G_found=false for _G_item in $_G_wslist; do test "x$_G_item" = "x$_G_member" && { _G_found=: break } done if $_G_found; then eval "$_G_yes_cmd" _G_status=$? else eval "$_G_no_cmd" _G_status=$? fi test 0 -eq "$_G_status" || exit $_G_status } # func_strpad STR WIDTH CHAR # -------------------------- # Trim STR, or pad with CHAR to force a total length of WIDTH. func_strpad () { $debug_cmd _G_width=`expr "$2" - 1` func_strpad_result=`$ECHO "$1" |$SED ' :a s|^.\{0,'"$_G_width"'\}$|&'"$3"'| ta '` } # func_strrpad STR WIDTH CHAR # --------------------------- # Trim STR, or right-justify-pad with CHAR to force a total length of # WIDTH. func_strrpad () { $debug_cmd _G_width=`expr "$2" - 1` func_strrpad_result=`$ECHO "$1" |$SED ' :a s|^.\{0,'"$_G_width"'\}$|'"$3"'&| ta '` } # func_strrow INDENT FIELD WIDTH [FIELDn WIDTHn]... # ------------------------------------------------- # Return a string containing each FIELD left justified to WIDTH, with # the whole thing indented by INDENT spaces. This function is used to # render one row of aligned columns for a table by func_strtable(). func_strrow () { $debug_cmd func_strrow_linelen=$1; shift _G_row= while test $# -gt 0; do func_strrow_linelen=`expr $func_strrow_linelen + $2` func_strpad "$1" $2 " " func_append _G_row "$func_strpad_result" shift; shift done func_strrpad "$_G_row" $func_strrow_linelen " " func_strrow_result=$func_strrpad_result } # func_strtable INDENT WIDTH1...WIDTHn HEADER1...HEADERn FIELD1...FIELDn # ---------------------------------------------------------------------- # Generate a string of newline-separated rows arranged in lined-up # columns of the given WIDTHs, with the entire table indented by INDENT # spaces. The number of columns is determined by the number of integer # valued WIDTH arguments following INDENT. The next set (i.e. a number # of arguments equal to the number of WIDTH arguments) of fields are # treated as the table's column HEADERs, and are separated from the # remainder of the table by an indented row of '-' characters. Remaining # arguments are each aligned below the next available header, wrapping # to a new row as necessary. Finally another row of '-' characters is # added to mark the end of the table. # # For example an unindented 3 column table with 2 rows of data would be # generated by this call: # # func_strtable 3 20 10 25 \ # Header1 Header2 Header3 \ # Row1Col1 Row1Col2 Row1Col3 \ # Row2Col1 Row2Col2 Row2Col3 # # returning the following string: # # " Header1 Header2 Header3 # ------------------------------------------------------- # Row1Col1 Row1Col2 Row1Col3 # Row2Col1 Row2Col2 Row2Col3 # -------------------------------------------------------" func_strtable () { $debug_cmd # Save the indent value, we'll need it for each row we render. _G_indent=$1; shift # Collect remaining numeric args into a list for reuse between # members of each row when we call func_strrow later. _G_widths=$1; shift while test 0 -lt `expr "$1" : '[1-9][0-9]*$'`; do func_append _G_widths " $1"; shift done # Extract the same number of positional parameters as there are # width elements - we'll do the header rows separately so that # we can insert a divider line. _G_header=$_G_indent for _G_width in $_G_widths; do func_append _G_header " $1 $_G_width"; shift done func_strrow $_G_header # Strip off the indent, and make a divider with '-' chars, then # reindent. _G_divider=`$ECHO "$func_strrow_result" \ |$SED 's|[^ ]|-|g :a s|- |--|g ta '` # Append the header and divider to the running result. func_append func_strtable_result "\ $func_strrow_result $_G_divider " # The remaining rows are zipped between the width values we # unwound earlier just like the header row above. while test $# -gt 0; do _G_row=$_G_indent for _G_width in $_G_widths; do func_append _G_row " $1 $_G_width"; shift done func_strrow $_G_row func_append func_strtable_result "\ $func_strrow_result " done # Mark the end of the table with a final divider line. func_append func_strtable_result "$_G_divider" } # func_internal_error ARG... # -------------------------- # Echo program name prefixed message to standard error, and exit. func_internal_error () { func_fatal_error "\ INTERNAL: " ${1+"$@"} " Please report this bug to 'bug-gnulib@gnu.org' in as much detail as possible." } # func_permissions_error FILE-OR-DIRECTORY # ---------------------------------------- # Echo program name prefixed permissions error message to standard # error, and exit. func_permissions_error () { $debug_cmd func_fatal_error "Failed to create '$1', check permissions." } # func_show_eval CMD [FAIL_EXP] # ----------------------------- # Unless opt_silent is true, then output CMD. Then, if opt_dryrun is # not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP # is given, then evaluate it. func_show_eval () { $debug_cmd $require_term_colors _G_cmd=$1 _G_fail_exp=${2-':'} ${opt_silent-'false'} || { func_quote eval $_G_cmd eval func_truncate_cmd $func_quote_result func_echo "running: $tc_bold$func_truncate_cmd_result$tc_reset" } ${opt_dry_run-'false'} || { eval "$_G_cmd" _G_status=$? test 0 -eq "$_G_status" || eval "(exit $_G_status); $_G_fail_exp" } } # func_truncate_cmd CMD [ARG]... # ------------------------------ # For unreasonably long commands (such as a gnulib-tool invocation with # the full module list for import), truncate CMD after the second non- # option ARG. func_truncate_cmd () { $debug_cmd _G_last_arg_opt_p=false func_truncate_cmd_result= set dummy "$@"; shift while test $# -gt 0; do _G_opt=$1; shift test -n "$func_truncate_cmd_result" \ && func_append func_truncate_cmd_result ' ' func_append func_truncate_cmd_result "$_G_opt" func_len "x$func_truncate_cmd_result" case $_G_opt in -*) _G_last_arg_opt_p=: ;; *) $_G_last_arg_opt_p \ || test "$min_cmd_len" -gt "$func_len_result" \ || break _G_last_arg_opt_p=false ;; esac done test $# -gt 0 && func_append func_truncate_cmd_result "..." } # func_gitignore_entries FILE... # ------------------------------ # Strip blank and comment lines to leave significant entries. func_gitignore_entries () { $debug_cmd $SED -e '/^#/d' -e '/^$/d' "$@" } # func_insert_if_absent STR FILE... # --------------------------------- # If $STR is not already on a line by itself in $FILE, insert it, at the # start. Entries are inserted at the start of the ignore list to ensure # existing entries starting with ! are not overridden. Such entries # support whilelisting exceptions after a more generic blacklist pattern. # sorting the new contents of the file and replacing $FILE with the result. func_insert_if_absent () { $debug_cmd str=$1 shift for file do test -f "$file" || touch "$file" duplicate_entries=`func_gitignore_entries "$file" |sort |uniq -d` test -n "$duplicate_entries" \ && func_error "duplicate entries in $file: " $duplicate_entries func_grep_q "^$str\$" "$file" \ || func_verbose "inserting '$str' into '$file'" linesold=`func_gitignore_entries "$file" |wc -l` linesnew=`{ $ECHO "$str"; cat "$file"; } \ |func_gitignore_entries |sort -u |wc -l` test "$linesold" -eq "$linesnew" \ || { $SED "1i\\$nl$str$nl" "$file" >"$file"T && mv "$file"T "$file"; } \ || func_permissions_error "$file" done } # func_get_version APP # -------------------- # echo the version number (if any) of APP, which is looked up along your # PATH. func_get_version () { $debug_cmd _G_app=$1 # Rather than uncomment the sed script in-situ, strip the comments # programatically before passing the result to $SED for evaluation. sed_get_version=`$ECHO '# extract version within line s|.*[v ]\{1,\}\([0-9]\{1,\}\.[.a-z0-9-]*\).*|\1| t done # extract version at start of line s|^\([0-9]\{1,\}\.[.a-z0-9-]*\).*|\1| t done d :done # the following essentially does s|5.005|5.5| s|\.0*\([1-9]\)|.\1|g p q' \ |$SED '/^[ ]*#.*$/d'` func_tool_version_output $_G_app >/dev/null _G_status=$? test 0 -ne "$_G_status" \ || $_G_app --version 2>&1 |$SED -n "$sed_get_version" (exit $_G_status) } # func_check_tool APP # ------------------- # Search PATH for an executable at APP. func_check_tool () { $debug_cmd func_check_tool_result= case $1 in *[\\/]*) test -x "$1" && func_check_tool_result=$1 ;; *) save_IFS=$IFS IFS=${PATH_SEPARATOR-:} for _G_check_tool_path in $PATH; do IFS=$save_IFS if test -x "$_G_check_tool_path/$1"; then func_check_tool_result=$_G_check_tool_path/$1 break fi done IFS=$save_IFS ;; esac } # func_check_versions APP1 VER1 URL1 ...[APPN VERN URLN] # ------------------------------------------------------ func_check_versions () { $debug_cmd func_check_versions_result=: while test $# -gt 0; do _G_app=$1; shift _G_reqver=$1; shift _G_url=$1; shift # Diagnose bad buildreq formatting. case $_G_url in [a-z]*://*) ;; # looks like a url *) func_fatal_error "\ '$_G_url' from the buildreq table in 'bootstrap.conf' does not look like the URL for downloading $_G_app. Please ensure that buildreq is a strict newline delimited list of triples; 'program min-version url'." ;; esac # Honor $APP variables ($TAR, $AUTOCONF, etc.) _G_appvar=`echo $_G_app |tr '[a-z-]' '[A-Z_]'` test TAR = "$_G_appvar" && _G_appvar=AMTAR eval "_G_app=\${$_G_appvar-$_G_app}" # Fail if no version specified, but the program can't be found. if test x- = "x$_G_reqver"; then func_check_tool $_G_app if test -z "$func_check_tool_result"; then func_error "Prerequisite '$_G_app' not not found. Please install it, or 'export $_G_appvar=/path/to/$_G_app'." func_check_versions_result=false else func_verbose "found '$func_check_tool_result' for $_G_appvar." fi else _G_instver=`func_get_version $_G_app` # Fail if --version didn't work. if test -z "$_G_instver"; then func_error "Prerequisite '$_G_app' not found. Please install it, or 'export $_G_appvar=/path/to/$_G_app'." func_check_versions_result=false # Fail if a newer version than what we have is required. else func_verbose "found '$_G_app' version $_G_instver." case $_G_reqver in =*) # If $buildreq version starts with '=', version must # match the installed program exactly. test "x$_G_reqver" = "x=$_G_instver" || { func_error "\ '$_G_app' version == $_G_instver is too old 'exactly $_G_app-$_G_reqver is required" func_check_versions_result=false } ;; *) # Otherwise, anything that is not older is a match. func_lt_ver "$_G_reqver" "$_G_instver" || { func_error "\ '$_G_app' version == $_G_instver is too old '$_G_app' version >= $_G_reqver is required" func_check_versions_result=false } ;; esac fi fi done } # func_cleanup_gnulib # ------------------- # Recursively delete everything below the path in the global variable # GNULIB_PATH. func_cleanup_gnulib () { $debug_cmd _G_status=$? $RM -fr "$gnulib_path" exit $_G_status } # func_download_po_files SUBDIR DOMAIN # ------------------------------------ func_download_po_files () { $debug_cmd func_echo "getting translations into $1 for $2..." _G_cmd=`printf "$po_download_command_format" "$2" "$1"` eval "$_G_cmd" } # func_update_po_files PO_DIR DOMAIN # ---------------------------------- # Mirror .po files to $po_dir/.reference and copy only the new # or modified ones into $po_dir. Also update $po_dir/LINGUAS. # Note po files that exist locally only are left in $po_dir but will # not be included in LINGUAS and hence will not be distributed. func_update_po_files () { $debug_cmd # Directory containing primary .po files. # Overwrite them only when we're sure a .po file is new. _G_po_dir=$1 _G_domain=$2 # Mirror *.po files into this dir. # Usually contains *.s1 checksum files. _G_ref_po_dir=$_G_po_dir/.reference test -d "$_G_ref_po_dir" || mkdir $_G_ref_po_dir || return func_download_po_files $_G_ref_po_dir $_G_domain \ && ls "$_G_ref_po_dir"/*.po 2>/dev/null \ |$SED -e 's|.*/||' -e 's|\.po$||' > "$_G_po_dir/LINGUAS" || return # Find sha1sum, named gsha1sum on MacPorts, and shasum on MacOS 10.6+. func_find_tool SHA1SUM sha1sum gsha1sum shasum sha1 test -n "$SHA1SUM" || func_fatal_error "\ Please install GNU Coreutils, or 'export SHA1SUM=/path/to/sha1sum'." _G_langs=`cd $_G_ref_po_dir && echo *.po|$SED 's|\.po||g'` test '*' = "$_G_langs" && _G_langs=x for _G_po in $_G_langs; do case $_G_po in x) continue;; esac _G_new_po=$_G_ref_po_dir/$_G_po.po _G_cksum_file=$_G_ref_po_dir/$_G_po.s1 if ! test -f "$_G_cksum_file" || ! test -f "$_G_po_dir/$_G_po.po" || ! $SHA1SUM -c "$_G_cksum_file" \ < "$_G_new_po" > /dev/null; then echo "updated $_G_po_dir/$_G_po.po..." cp "$_G_new_po" "$_G_po_dir/$_G_po.po" \ && $SHA1SUM < "$_G_new_po" > "$_G_cksum_file" || return fi done } ## --------------- ## ## Option parsing. ## ## --------------- ## # Hook in the functions to make sure our own options are parsed during # the option parsing loop. usage='$progpath [OPTION]...' # Short help message in response to '-h'. Add to this in 'bootstrap.conf' # if you accept any additional options. usage_message="Common Bootstrap Options: -c, --copy copy files instead of creating symbolic links. --debug enable verbose shell tracing -n, --dry-run print commands rather than running them -f, --force attempt to bootstrap even if the sources seem not to have been checked out. --gnulib-srcdir=DIRNAME specify a local directory where gnulib sources reside. Use this if you already have the gnulib sources on your machine, and don't want to waste your bandwidth downloading them again. Defaults to \$GNULIB_SRCDIR. --no-warnings equivalent to '-Wnone' --skip-git do not fetch files from remote repositories --skip-po do not download po files. -v, --verbose verbosely report processing --version print version information and exit -W, --warnings=CATEGORY report the warnings falling in CATEGORY [all] -h, --help print short or long help message and exit " # Additional text appended to 'usage_message' in response to '--help'. long_help_message=$long_help_message" 'recommend' show warnings about missing recommended packages 'settings' show warnings about missing '$progname.conf' settings 'upgrade' show warnings about out-dated files If the file '$progname.conf' exists in the same directory as this script, its contents are read as shell variables to configure the bootstrap. For build prerequisites, environment variables like \$AUTOCONF and \$AMTAR are honored. Running without arguments will suffice in most cases. " # Warning categories used by 'bootstrap', append others if you use them # in your 'bootstrap.conf'. warning_categories='recommend settings upgrade' # bootstrap_options_prep [ARG]... # ------------------------------- # Preparation for options parsed by Bootstrap. bootstrap_options_prep () { $debug_cmd # Option defaults: opt_copy=${copy-'false'} opt_dry_run=false opt_force=false opt_gnulib_srcdir=$GNULIB_SRCDIR opt_skip_git=false opt_skip_po=false # Pass back the list of options we consumed. func_quote eval ${1+"$@"} bootstrap_options_prep_result=$func_quote_result } func_add_hook func_options_prep bootstrap_options_prep # bootstrap_parse_options [ARG]... # -------------------------------- # Provide handling for Bootstrap specific options. bootstrap_parse_options () { $debug_cmd # Perform our own loop to consume as many options as possible in # each iteration. while test $# -gt 0; do _G_opt=$1 shift case $_G_opt in --dry-run|--dryrun|-n) opt_dry_run=: ;; --copy|-c) opt_copy=: ;; --force|-f) opt_force=: ;; --gnulib-srcdir) test $# = 0 && func_missing_arg $_G_opt && break opt_gnulib_srcdir=$1 shift ;; --skip-git|--no-git) opt_skip_git=: ;; --skip-po|--no-po) opt_skip_po=: ;; # Separate non-argument short options: -c*|-f*|-n*) func_split_short_opt "$_G_opt" set dummy "$func_split_short_opt_name" \ "-$func_split_short_opt_arg" ${1+"$@"} shift ;; *) set dummy "$_G_opt" ${1+"$@"}; shift; break ;; esac done # save modified positional parameters for caller func_quote eval ${1+"$@"} bootstrap_parse_options_result=$func_quote_result } func_add_hook func_parse_options bootstrap_parse_options # bootstrap_validate_options [ARG]... # ----------------------------------- # Perform any sanity checks on option settings and/or unconsumed # arguments. bootstrap_validate_options () { $debug_cmd # Validate options. test $# -gt 0 \ && func_fatal_help "too many arguments" # Pass back the (empty) list of unconsumed options. func_quote eval ${1+"$@"} bootstrap_validate_options_result=$func_quote_result } func_add_hook func_validate_options bootstrap_validate_options ## -------------------------------------------------- ## ## Source package customisations in 'bootstrap.conf'. ## ## -------------------------------------------------- ## # Override the default configuration, if necessary. # Make sure that bootstrap.conf is sourced from the current directory # if we were invoked as "sh bootstrap". case $0 in */*) test -r "$0.conf" && . "$0.conf" ;; *) test -r "$0.conf" && . ./"$0.conf" ;; esac ## ------------------------------- ## ## Actually perform the bootstrap. ## ## ------------------------------- ## func_bootstrap ${1+"$@"} # The End. exit ${exit_status-$EXIT_SUCCESS} # Local variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-pattern: "500/scriptversion=%:y-%02m-%02d.%02H; # UTC" # time-stamp-time-zone: "UTC" # End: plptools-1.0.26/bootstrap.conf000066400000000000000000000074371504470754400163650ustar00rootroot00000000000000# bootstrap.conf # # Copyright (c) 2024 Reuben Thomas # # This file is part of plptools. # # 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 3, 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, see . ## -------------- ## ## Configuration. ## ## -------------- ## # File that should exist relative to the top directory of a checked out # hierarchy, but not in a distribution tarball. checkout_only_file=.gitignore # List of programs, minimum versions, and software urls required to # bootstrap, maintain and release. # Build prerequisites buildreq=' git 1.5.5 https://git-scm.com gpg 1.4.11 https://www.gnupg.org perl 5.5 https://perl.com tar - https://www.gnu.org/s/tar pkg-config - https://www.freedesktop.org/wiki/Software/pkg-config/ ' # Non-default gnulib directory options. source_base=libgnu local_gl_path=gl:gl-mod/bootstrap gnulib_git_submodules=gl-mod/bootstrap gnulib_clone_since=2024-02-26 # Additional gnulib-tool options to use. gnulib_tool_options=' --no-libtool --no-changelog ' # gnulib modules used by this package. gnulib_modules=' bootstrap fnmatch getcwd hostent ignore-value largefile locale manywarnings pthread socket servent string-buffer strsep xalloc xvasprintf yesno ' # Extra gnulib files that are not in modules, which override files of # the same name installed by other bootstrap tools. gnulib_non_module_files="$gnulib_non_module_files"' ' ## -------------------- ## ## Resource management. ## ## -------------------- ## # require_source_base # ------------------- # Ensure any source_base setting in this file or `gnulib-cache.m4` # is used for $source_base. If both are the empty string before # call this function, $source_base will still be the empty string # afterwards. Use ${source_base-lib} if you are looking at files # inside the gnulib directory. require_source_base=plptools_require_source_base plptools_require_source_base () { $debug_cmd test -n "$source_base" || { $require_gnulib_cache $require_macro_dir func_extract_trace "gl_SOURCE_BASE" "$gnulib_cache" source_base="$func_extract_trace_result" test -n "$source_base" && func_verbose "source_base='$source_base'" } require_source_base=: } # Copyright holder copyright_holder="plptools developers" ## --------------- ## ## Hook functions. ## ## --------------- ## # plptools_ignore_gnulib_ignore # ---------------------------- # gnulib-tool updates m4/.gitignore and lib/.gitignore, and keeping # generated files under version control does not make sense. Since # lib is entirely ignored, we only need to prepopulate the m4 ignore # files with generated files not tracked by gnulib-tool. plptools_ignore_gnulib_ignore () { $debug_cmd $require_macro_dir if test -f "$macro_dir/.gitignore" ; then : else func_verbose "creating initial \`$macro_dir/.gitignore'" cat > $macro_dir/.gitignore <<\EOF # files created by bootstrap, but that gnulib doesn't track *~ /.gitignore /gnulib-comp.m4 EOF fi } func_add_hook func_prep plptools_ignore_gnulib_ignore # Local variables: # mode: shell-script # sh-indentation: 2 # End: plptools-1.0.26/build-aux/000077500000000000000000000000001504470754400153605ustar00rootroot00000000000000plptools-1.0.26/build-aux/.gitignore000066400000000000000000000002711504470754400173500ustar00rootroot00000000000000/bootstrap.in /extract-trace /funclib.sh /inline-source /options-parser /compile /config.guess /config.rpath /config.sub /depcomp /install-sh /ltmain.sh /mdate-sh /missing /texinfo.tex plptools-1.0.26/build-aux/build.sh000077500000000000000000000007301504470754400170160ustar00rootroot00000000000000#!/bin/bash # Build for CI. # Written by Reuben Thomas 2021-2024. # This file is in the public domain. set -e ./bootstrap --skip-po if [[ "$ASAN" == "yes" ]]; then CONFIGURE_ARGS+=(CFLAGS="-g3 -fsanitize=address -fsanitize=undefined" CXXFLAGS="-g3 -fsanitize=address -fsanitize=undefined" LDFLAGS="-fsanitize=address -fsanitize=undefined") fi ./configure "${CONFIGURE_ARGS[@]}" make V=1 make distcheck || ( cat ./plptools-*/_build/sub/tests/test-suite.log; exit 1 ) plptools-1.0.26/configure.ac000066400000000000000000000135541504470754400157640ustar00rootroot00000000000000# configure.ac # # This file is part of plptools. # # Copyright (C) 1999-2002 Fritz Elfert # Copyright (C) 2007-2025 Reuben Thomas # # 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 # along with this program; if not, see . AC_PREREQ([2.71]) AC_INIT([plptools],[1.0.26],[plptools-developers@lists.sourceforge.net]) AC_CANONICAL_TARGET AC_CONFIG_AUX_DIR([build-aux]) AM_INIT_AUTOMAKE m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) dnl checks for programs AC_PROG_INSTALL AC_PROG_CXX AC_PROG_CC gl_EARLY AX_CHECK_GNU_MAKE(,[AC_MSG_ERROR([GNU make is required])]) gl_INIT dnl Basic settings AC_USE_SYSTEM_EXTENSIONS AC_CONFIG_HEADERS([config.h]) LT_INIT dnl Extra warnings with GCC AC_ARG_ENABLE([gcc-warnings], [AS_HELP_STRING([--disable-gcc-warnings], [turn off lots of GCC warnings])], [case $enableval in yes|no) ;; *) AC_MSG_ERROR([bad value $enableval for gcc-warnings option]) ;; esac gl_gcc_warnings=$enableval], [gl_gcc_warnings=yes] ) if test "$gl_gcc_warnings" = yes; then dnl Set up the list of undesired warnings. nw= nw="$nw -Wsystem-headers" # Don’t let system headers trigger warnings gl_MANYWARN_ALL_GCC([warnings]) dnl Enable all GCC warnings not in this list. gl_MANYWARN_COMPLEMENT([warnings], [$warnings], [$nw]) for w in $warnings; do gl_WARN_ADD([$w]) done AC_LANG_PUSH([C++]) gl_MANYWARN_ALL_GCC([cxx_warnings]) dnl Enable all G++ warnings not in this list. gl_MANYWARN_COMPLEMENT([cxx_warnings], [$cxx_warnings], [$nw]) for w in $cxx_warnings; do gl_WARN_ADD([$w]) done AC_LANG_POP dnl Add an extra warning gl_WARN_ADD([-Wconversion]) # When compiling with GCC, prefer -isystem to -I when including system # include files, to avoid generating useless diagnostics for the files. ISYSTEM='-isystem ' else ISYSTEM='-I' fi AC_SUBST([ISYSTEM]) dnl NLS support ALL_LINGUAS="de sv" AM_GNU_GETTEXT([external]) AM_GNU_GETTEXT_VERSION([0.19.6]) dnl readline and history for plpftp MIN_LIBREADLINE_VERSION=4.3 AX_LIB_READLINE([$MIN_LIBREADLINE_VERSION]) if test "$ax_cv_lib_readline_version_ok" != "yes"; then AC_MSG_ERROR([readline version $MIN_LIBREADLINE_VERSION or later is required]) fi # FUSE and libattr for plpfuse PKG_CHECK_MODULES([FUSE], [fuse >= 2.6], [enable_fuse=yes], [enable_fuse=no]) AC_CHECK_HEADER(attr/attributes.h) AC_CHECK_HEADERS(attr/xattr.h) AM_CONDITIONAL(BUILD_PLPFUSE, test x$enable_fuse = xyes) dnl special options for customization AC_ARG_WITH(serial, [ --with-serial=DEV override default serial line], [ DDEV="$withval" AC_MSG_RESULT(Overriding default serial device: $DDEV) ], [ DDEV="/dev/ttyS0" AC_MSG_RESULT(Using default serial device: $DDEV)] ) AC_DEFINE_UNQUOTED(DDEV,"$DDEV",[Define this to your serial device node]) AC_SUBST(DDEV) AC_ARG_WITH(speed, [ --with-speed=SPEED override default serial speed [auto]], [ if "$withval" = "auto" ; then DSNAME=auto DSPEED=-1 else DSPEED="$withval" DSNAME="$withval" fi AC_MSG_RESULT(Overriding serial speed: $DSNAME) ], [ DSPEED=-1 DSNAME=auto AC_MSG_RESULT(Using default serial speed: auto) ] ) AC_DEFINE_UNQUOTED(DSPEED,$DSPEED,[Define this to your serial device speed]) AC_DEFINE_UNQUOTED(DSNAME,$DSNAME, [Define this to your serial device speed alias]) AC_SUBST(DSPEED) AC_SUBST(DSNAME) AC_ARG_WITH(port, [ --with-port=PORT override default port [7501]], [ DPORT="$withval" AC_MSG_RESULT(Overriding port: $DPORT) ], [ DPORT=7501 AC_MSG_RESULT(Using default port: $DPORT) ] ) AC_DEFINE_UNQUOTED(DPORT,$DPORT, [Define this to the TCP port ncpd should listen on]) AC_SUBST(DPORT) AC_ARG_WITH(drive, [ --with-drive=DRIVE override default EPOC drive [AUTO]], [ DDRIVE="$withval" AC_MSG_RESULT(Overriding drive: $DDRIVE) ], [ DDRIVE='AUTO' AC_MSG_RESULT(Using default EPOC drive: $DDRIVE) ] ) AC_DEFINE_UNQUOTED(DDRIVE,"$DDRIVE", [Define this to your default drive on your EPOC device]) AC_ARG_WITH(basedir, [ --with-basedir=DIR override default EPOC directory [\\\\]], [ DBASEDIR="$withval" AC_MSG_RESULT(Overriding directory: $DBASEDIR) ], [ DBASEDIR='\\' AC_MSG_RESULT(Using default EPOC directory: $DBASEDIR) ] ) AC_DEFINE_UNQUOTED(DBASEDIR,"$DBASEDIR", [Define this to your default directory on your EPOC device]) AC_ARG_WITH(initdir, [ --with-initdir=DIR override default init dir [PREFIX/etc/rc.d/init.d]], [ initdir="$withval" AC_MSG_RESULT(Overriding initdir: $initdir) ], [ initdir="${sysconfdir}/rc.d/init.d" AC_MSG_RESULT(Using default init dir: $initdir) ] ) AC_SUBST(initdir) LIB_PLP='$(top_builddir)/lib/libplp.la' AC_SUBST(LIB_PLP) dnl Extra definitions for config.h AH_BOTTOM([ /* Select version of FUSE API */ #define FUSE_USE_VERSION 26 ]) dnl Create files AC_CONFIG_FILES( Makefile po/Makefile.in lib/Makefile libgnu/Makefile ncpd/Makefile plpftp/Makefile plpfuse/Makefile plpprint/Makefile plpprint/prolog.ps sisinstall/Makefile doc/Makefile etc/plptools doc/ncpd.man doc/plpfuse.man doc/plpftp.man doc/sisinstall.man doc/plpprintd.man ) AC_OUTPUT plptools-1.0.26/doc/000077500000000000000000000000001504470754400142335ustar00rootroot00000000000000plptools-1.0.26/doc/.gitignore000066400000000000000000000000211504470754400162140ustar00rootroot00000000000000/*.man /*.8 /*.1 plptools-1.0.26/doc/Makefile.am000066400000000000000000000024331504470754400162710ustar00rootroot00000000000000# doc/Makefile.am # # This file is part of plptools. # # Copyright (C) 2000-2002 Fritz Elfert # Copyright (C) 2007-2014 Reuben Thomas # # 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 # along with this program; if not, see . EXTRA_DIST = ncpd.man.in plpfuse.man.in plpftp.man.in sisinstall.man.in \ plpprintd.man.in man_MANS = ncpd.8 plpftp.1 sisinstall.1 plpprintd.8 if BUILD_PLPFUSE man_MANS += plpfuse.8 endif edit = sed \ -e 's|@MANDATE@|'`git log --pretty=format:"%ad" --date=short -1 $<.in`'|g' \ -e 's|@pkgdatadir[@]|$(pkgdatadir)|g' %.1: %.man Makefile rm -f $@ $@.tmp $(edit) $< >$@.tmp mv $@.tmp $@ %.8: %.man Makefile rm -f $@ $@.tmp $(edit) $< >$@.tmp mv $@.tmp $@ distclean-local: rm -f *.1 *.8 plptools-1.0.26/doc/ncpd.man.in000066400000000000000000000047571504470754400162760ustar00rootroot00000000000000.\" Manual page for ncpd .\" .\" Process this file with .\" groff -man -Tascii ncpd.8 for ASCII output, or .\" groff -man -Tps ncpd.8 for Postscript output .\" .TH ncpd 8 "@MANDATE@" "plptools @VERSION@" "System administration commands" .SH NAME ncpd \- Daemon which handles the serial link to a Psion .SH SYNOPSIS .B ncpd .B [-V] .B [-h] .BI "[-v " log-class ] .B [-d] .B [-e] .BI "[-p [" host ":]" port ] .BI "[-s " device ] .BI "[-b " baud-rate ] .BI [ long-options ] .SH DESCRIPTION ncpd is the daemon which handles the serial link to your Psion. It listens at port @DPORT@ for local connections and provides basic PLP/NCP services for plpfuse and plpftp and other front-ends. It auto-connects to the psion, even after unplugging/switching off therefore it can run all the time if you can dedicate a serial device to it. .SH OPTIONS .TP .B \-V, --version Display the version and exit .TP .B \-h, --help Display a short help text and exit. .TP .B \-e, --autoexit Exit automatically if the device is disconnected. .TP .BI "\-v, --verbose=" log-class Increase the logging level of the program. the possible values for log-class are: .RS .TP .I nl Set NCP debug level to LOG .TP .I nd Set NCP debug level to DEBUG .TP .I ll Set Link debug level to LOG .TP .I ld Set Link debug level to DEBUG .TP .I pl Set Packet debug level to LOG .TP .I pd Set Packet debug level to DEBUG .TP .I ph Set Packet debug level to HANDSHAKE .TP .I m Set overall debug level to verbose .TP .I all Turn on all the above logging on. .RE .TP .B "\-d, --dontfork" Do not background the daemon. .TP .BI "\-p, --port=[" host: ] port Specify the port to listen on - by default the value for the host is 127.0.0.1 and the value for the port is looked up in /etc/services using the key .B psion/tcp. If it is not found there, a default value of @DPORT@ is used. .TP .BI "\-s, --serial=" device Specify the serial device to use to connect to the Psion - this defaults to @DDEV@ .TP .BI "\-b, --baudrate=" baud-rate Specify the baud rate to use for the serial connection. If the word .B auto is specified, ncpd cycles through baud-rates of 115200, 57600, 38400, 19200 and 9600 baud. Default setting is @DSNAME@. .SH SEE ALSO plpfuse(8), plpprintd(8), plpftp(1), sisinstall(1) .SH AUTHOR Fritz Elfert .br Heavily based on p3nfsd by Rudolf Koenig (rfkoenig@immd4.informatik.uni-erlangen.de) and plp_1_7 by Philip Proudman (phil@proudman51.freeserve.co.uk) .br Patches from Matt Gumbley (matt@gumbley.demon.co.uk) .br Man page by John Lines (john+plpman@paladin.demon.co.uk) plptools-1.0.26/doc/plpftp.man.in000066400000000000000000000030041504470754400166370ustar00rootroot00000000000000.\" Manual page for plpftp .\" .\" Process this file with .\" groff -man -Tascii plpftp.1 for ASCII output, or .\" groff -man -Tps plpftp.1 for Postscript output .\" .TH plpftp 1 "@MANDATE@" "plptools @VERSION@" "User commands" .SH NAME plpftp \- FTP-like program for manipulating files on the Psion. .SH SYNOPSIS .B plpftp .B [-h] .B [-V] .BI "[-p [" host :] port ] .BI [ long-options ] .BI "[ " FTP-command " [" parameters ]] .SH DESCRIPTION plpftp provides an FTP style interface for the psion. It requires the ncpd to be running already to provide access to the serial port. plpftp has online help. To see the available commands start the program and enter "help". .SH OPTIONS .TP .B \-V, --version Display the version and exit .TP .B \-h, --help Display a short help text and exit. .TP .BI "\-p, --port=[" host :] port Specify the host and port to connect to (e.g. The port where ncpd is listening on) - by default the host is 127.0.0.1 and the port is looked up in /etc/services. If it is not found there, a builtin value of @DPORT@ is used. .TP .I FTP-command parameters Allows you to specify an plpftp command on the command line. If specified, plpftp enters non interactive mode and terminates after executing the command. .SH SEE ALSO ncpd(8), plpfuse(8), plpprintd(8), sisinstall(1) .SH AUTHOR Fritz Elfert .br Heavily based on p3nfsd by Rudolf Koenig (rfkoenig@immd4.informatik.uni-erlangen.de) and plp_1_7 by Philip Proudman (phil@proudman51.freeserve.co.uk) .br Man page by John Lines (john+plpman@paladin.demon.co.uk) plptools-1.0.26/doc/plpfuse.man.in000066400000000000000000000061111504470754400170120ustar00rootroot00000000000000.\" Manual page for plpfuse .\" .\" Process this file with .\" groff -man -Tascii plpfuse.8 for ASCII output, or .\" groff -man -Tps plpfuse.8 for Postscript output .\" .TH plpfuse 8 "@MANDATE@" "plptools @VERSION@" "System administration commands" .SH NAME plpfuse \- Daemon to mount an EPOC device as a file system .SH SYNOPSIS .B plpfuse .B [-V] .B [-d] .B [-h] .BI "[-p [" HOST :] PORT ] .BI [ LONG-OPTIONS ] .BI MOUNTPOINT .SH DESCRIPTION plpfuse is a file system which provides file system access to your EPOC device. It mounts the EPOC device's file systems in your computer's file system. Like the other front-ends, this program auto-reconnects after a link failure, so you can keep the EPOC device mounted all the time, even when it is not connected. Due to Rudolf Koenig's clever error handling, you don't need to worry about blocked I/O processes if the psion isn't available. You will simply get a "device not configured" error, when accessing a file on a previously connected psion which has been disconnected. After that, the mount point will appear with the drives missing. As soon as the psion is connected again, the subdirectories will reappear (possibly with a few seconds' delay). EPOC file attributes are mapped as follows: readable on the EPOC device is mapped to user-readable on UNIX; read-only is inverted and mapped to user-writable; system, hidden and archived are mapped to the extended user attribute .B user.psion as the single characters `s', `h' and `a'. The extended attribute can therefore be up to three characters long. An attempt to read or write any other extended attribute will give an error. .SH OPTIONS .TP .B \-V, --version Display the version and exit .TP .B \-h, --help Display a short help text and exit. .TP .B \-d, --debug Produce debugging logs. Can be specified more than once to increase the debug level (up to 3 times). .TP .BI "\-p, --port=[" host :] port Specify the host and port to connect to (e.g. the port where ncpd is listening on) - by default the host is 127.0.0.1 and the port is looked up in /etc/services. If it is not found there, a fall-back builtin of .I @DPORT@. .SH BUGS Because UNIX file names are simply byte strings, if your EPOC device uses a different character set from the computer to which it is connected, which is highly likely, then characters which are differently encoded between the two characters sets will not translate between the two systems. it is usually safe to use 7-bit ASCII characters, avoiding colon (invalid on EPOC) and slash (invalid on UNIX). This problem may be fixed in future. .SH SEE ALSO ncpd(8), plpprintd(8), plpftp(1), sisinstall(1), fusermount(1) .SH AUTHOR Reuben Thomas, based on plpnfsd by Fritz Elfert, and FUSE example code by Miklos Szeredi (miklos@szeredi.hu). .br plpnfsd itself was heavily based on p3nfsd by Rudolf Koenig (rfkoenig@immd4.informatik.uni-erlangen.de) and plp_1_7 by Philip Proudman (phil@proudman51.freeserve.co.uk), with patches from Matt Gumbley (matt@gumbley.demon.co.uk). .br Man page by Reuben Thomas , based on the man page for plpnfsd by John Lines (john+plpman@paladin.demon.co.uk). plptools-1.0.26/doc/plpprintd.man.in000066400000000000000000000042771504470754400173630ustar00rootroot00000000000000.\" Manual page for plpprintd .\" .\" Process this file with .\" groff -man -Tascii plpprintd.1 for ASCII output, or .\" groff -man -Tps plpprintd.1 for Postscript output .\" .TH plpprintd 8 "@MANDATE@" "plptools @VERSION@" "System administration commands" .SH NAME plpprintd \- Daemon for printing via PC from a Psion. .SH SYNOPSIS .B plpprintd .B [-d] .B [-h] .B [-v] .B [-V] .BI [-s " spooldir" ] .BI [-c " print-cmd" ] .BI "[-p [" host :] port ] .BI [ long-options ] .SH DESCRIPTION plpprintd provides a print server for a Psion, connected to the PC. Using plpprintd, you can use the Psion's "Print via PC" feature to access any printer, available on your PC. When receiving a print job, the data is converted to Postscript\*[R] and then send to a printer. .SH OPTIONS .TP .B \-d, --debug Enable additional debug messages and stay in foreground (do not fork). .TP .B \-h, --help Display a short help text and exit. .TP .B \-v, --verbose Increase verbosity .TP .B \-V, --version Display the version and exit .TP .BI "\-s, --spooldir=" dir Specify a directory to use for temporary spool files. If this option is missing, a builtin default .I /var/spool/plpprint is used. .TP .BI "\-c, --printcmd=" cmd Specify a command for actually sending a print job to the printer. The specified command must receive the data from its standard input. If this option is missing, a builtin default .I lpr \-Ppsion is used. .TP .BI "\-p, --port=[" host :] port Specify the host and port to connect to (e.g. The port where ncpd is listening on) - by default the host is 127.0.0.1 and the port is looked up in /etc/services. If it is not found there, a builtin value of @DPORT@ is used. .TP .SH FILES .TP @pkgdatadir@/prolog.ps contains the Postscript(\*R) dictionary, needed for printing the jobs. .TP @pkgdatadir@/fontmap specifies the font mapping to be used. This file is used to convert the Psion's font names into valid Postscript(\*R) font names. Its format is described at the beginning of it. If a Psion font name is received which can not be found in this file, a builtin default Courier is used. .SH BUGS Using patterns in diagrams gives ugly results. .SH SEE ALSO ncpd(8), plpfuse(8), plpftp(1), sisinstall(1) .SH AUTHOR Fritz Elfert plptools-1.0.26/doc/sisinstall.man.in000066400000000000000000000024671504470754400175330ustar00rootroot00000000000000.\" Manual page for sisinstall .\" .\" Process this file with .\" groff -man -Tascii sisinstall.1 for ASCII output, or .\" groff -man -Tps sisinstall.1 for Postscript output .\" .TH sisinstall 1 "@MANDATE@" "plptools @VERSION@" "User commands" .SH NAME sisinstall \- simple command line program for installing packaged programs or libraries on the Psion. .SH SYNOPSIS .B sisinstall .B [-h] .B [-V] .B [-n] .BI "[-v " level ] .BI [ long-options ] .B FILE .SH DESCRIPTION sisinstall installs a packaged program or library from a sis file to a Psion machine. It requires the ncpd to be running already to provide access to the Psion machine over the serial port. .SH OPTIONS .TP .B \-V, --version Display the version and exit .TP .B \-h, --help Display a short help text and exit. .TP .B \-n, --dryryn Just parse the sis file, don't touch the Psion machine. .TP .BI "\-v, --verbose=" level Specify the log level. Level 0 is the default, and is pretty quiet. Level 1 provides detailed information on the data that is read. Level 2 is rather verbose, and is mostly meant for debugging. .SH BUGS Ignores dependency records in the sis file. .SH SEE ALSO ncpd(8), plpfuse(8), plpprintd(8), plpftp(1) .SH AUTHOR Written by Daniel Brahneborg (basic@chello.se). .br Psion communication based on plpftp by Fritz Elfert (felfert@to.com). plptools-1.0.26/etc/000077500000000000000000000000001504470754400142415ustar00rootroot00000000000000plptools-1.0.26/etc/.gitignore000066400000000000000000000000121504470754400162220ustar00rootroot00000000000000/plptools plptools-1.0.26/etc/plptools.in000077500000000000000000000050261504470754400164530ustar00rootroot00000000000000#!/bin/sh # # plptools Starts ncpd/plpfuse. # # chkconfig: 2345 45 10 # description: This facility enables connectivity to an EPOC PDA. # # This file is part of plptools. # # Copyright (C) 1999-2002 Fritz Elfert # Copyright (C) 2007-2008 Reuben Thomas # # 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 # along with this program; if not, see . # Source function library. . /etc/rc.d/init.d/functions [ -f @prefix@/sbin/ncpd ] || exit 0 [ -f @prefix@/sbin/plpfuse ] || exit 0 [ -f @prefix@/sbin/plpprintd ] || exit 0 [ -f /etc/sysconfig/plptools ] || exit 0 . /etc/sysconfig/plptools start() { echo "Starting EPOC support ..." RETVAL=0 if test "$START_NCPD" = "yes" ; then echo -n " ncpd: " daemon @prefix@/sbin/ncpd $NCPD_ARGS RETVAL=$? echo fi if [ $RETVAL -eq 0 ] ; then if test "$START_PLPFUSE" = "yes" ; then echo -n " plpfuse: " daemon @prefix@/sbin/plpfuse $PLPFUSE_ARGS RETVAL=$? echo fi fi if [ $RETVAL -eq 0 ] ; then if test "$START_PLPPRINTD" = "yes" ; then echo -n " plpprintd: " daemon @prefix@/sbin/plpprintd $PLPPRINTD_ARGS RETVAL=$? echo fi fi [ $RETVAL -eq 0 ] && touch /var/lock/subsys/plptools return $RETVAL } stop() { echo "Stopping EPOC support ..." RETVAL=0 if test "$START_PLPPRINTD" = "yes" ; then echo -n " plpprintd: " killproc plpprintd echo fi if test "$START_PLPFUSE" = "yes" ; then echo -n " plpfuse: " fusermount -u /mnt/epoc echo fi if test "$START_NCPD" = "yes" ; then echo -n " ncpd: " killproc ncpd RETVAL=$? echo fi [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/plptools return $RETVAL } restart() { stop start } # See how we were called. case "$1" in start) start ;; stop) stop ;; status) status ncpd status plpprintd ;; restart|reload) restart ;; condrestart) test -f /var/lock/subsys/plptools && restart || : ;; *) echo "Usage: plptools {start|stop|status|restart|reload|condrestart}" exit 1 esac exit $? plptools-1.0.26/etc/psidump000066400000000000000000000206701504470754400156520ustar00rootroot00000000000000#!/bin/bash # psidump - backup EPOC files # Copyright (C) 2001 Alain Trembleau # # 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 # along with this program; if not, see . # # This does an incremental backup of the C:, D: and E: drives (if they exist) # The Z: (ROM) drive can also be backed up. # The -g option is a gnu tar specific option which will not necessarily # work with other versions of tar. # The tar files created may not work with other versions of tar. # # Note: --listed-incremental=SNAPSHOT-FILE = -g SNAPSHOT-FILE # # Function definitions function usage () { OPTIND=1 while getopts ":d" option; do case $option in d) # Detailed output detail=1 ;; esac done echo "Version $VERSION" echo "Usage : psidump [-fivV] [-D ] [-m ] [-d ]" if [[ $detail ]]; then echo "Options:" echo " d Unix dump directory" echo " D EPOC disks to be backed up" echo " f forces a full backup" echo " i perform an incremental backup" echo " m EPOC mount point" echo " v verbose output" echo " V display the version and exit" fi exit 1 } function create_dirs () { # If the required directories do not exist, then create them. # Local variable declarations local localdir # Grab arguments localdir=$1 if [[ ! -d "$localdir" ]] ; then mkdir "$localdir" fi if [[ ! -d "$localdir"/etc ]] ; then mkdir "$localdir"/etc fi if [[ ! -d "$localdir"/etc ]] ; then mkdir "$localdir"/etc fi if [[ ! -d "$localdir"/backups ]] ; then mkdir "$localdir"/backups fi if [[ ! -d "$localdir"/backups/tarfiles ]] ; then mkdir "$localdir"/backups/tarfiles fi if [[ ! -f "$localdir"/etc/epoctab ]] ; then cat > "$localdir"/etc/epoctab << END1 # epoctab # # This file is a table of the different EPOC machines which have connected # to this machine. The first entry is the machine id, and the second # entry is the machine name. # Note: There MUST be a TAB between the two fields! # END1 fi } function process_drives () { # Determine the drives on the machine, and process one by one # Local variable declarations local localdir local psidir local full_backup local machine_name local dump_drive_list local re_drive_list # Grab arguments localdir=$1 psidir=$2 full_backup=$3 machine_name=$4 dump_drive_list=$5 # Turn "CDE" into "C|D|E" re_drive_list=`echo $dump_drive_list | sed -e 's/./&|/g' -e 's/|$//'` # Determine the current drives on the machine to be backed up drives=` plpftp devs | sed -e '1d' | awk '$1~/'$re_drive_list'/ { print $1 }' ` # Dump file timestamp now=`date +%Y%m%d.%H%M` # Do the dump and loads echo "Backing up Machine: $machine_name" for drive in $drives do drive_name=`plpftp devs | awk '$1=="'$drive'" { print $3 }'` echo "Backing up Drive: $drive Name: $drive_name" if [[ -n $drive_name ]] ; then dump_drive "$localdir" "$psidir" $full_backup "$machine_name" \ "$drive:" "$drive_name" "$now" restore_drive "$localdir" "$machine_name" "$drive_name" "$now" fi echo "Drive $drive backed up." done } function dump_drive () { # Incremental backup of the specified drive # Local variable declarations local localdir local psidir local full_backup local machine_name local drive local drive_name local timestamp # Grab arguments localdir=$1 psidir=$2 full_backup=$3 machine_name=$4 drive=$5 drive_name=$6 timestamp=$7 # If doing a full backup, remove dumpinfo file. if [[ $full_backup == 1 ]] ; then rm "$localdir"/etc/dumpinfo."$machine_name"."$drive_name" fi tar -c -g "$localdir"/etc/dumpinfo."$machine_name"."$drive_name" -v -z \ -f "$localdir"/backups/tarfiles/epoc."$machine_name"."$drive_name".$timestamp.tgz \ -C "$psidir"/$drive \ . # . \ # 2>&1 | tee -a "$localdir"/etc/dumplog # When testing, add this to the above tar command to speed up dumps # --exclude ./System \ } function restore_drive () { # Untar the files into the appropriate directory # Local variable declarations local localdir local machine_name local drive_name local timestamp # Grab arguments localdir=$1 machine_name=$2 drive_name=$3 timestamp=$4 # Need to check whether the appropriate directories exist. # If not, create them. if [[ ! -d "$localdir"/backups/"$machine_name"/"$drive_name" ]] ; then mkdir "$localdir"/backups/"$machine_name"/"$drive_name" fi # Now restore the files to an appropriate location. tar -x -g $localdir/etc/dumpinfo."$machine_name"."$drive_name" -v -z \ -C $localdir/backups/"$machine_name"/"$drive_name" \ -f $localdir/backups/tarfiles/epoc."$machine_name"."$drive_name".$timestamp.tgz # | tee -a "$localdir"/etc/dumplog } ## Main Program # Default settings psidir="/mnt/epoc" localdir="$HOME/epoc" dump_drive_list="CDE" full_backup=0 verbose=0 while getopts ":d:D:fim:vV" option; do case $option in d) # Set the local dump directory localdir=$OPTARG ;; D) # Specify the disks to be backed up dump_drive_list=$OPTARG ;; f) # Force full backup full_backup=1 ;; i) # Perform an incremental backup full_backup=0 ;; m) # Set the mount point where the EPOC files are to be found psidir=$OPTARG ;; v) # Turn on verbose logging verbose=1 ;; V) # Display version and exit usage ;; ?) # Invalid option OPTIND=1 usage -d ;; esac done shift $((OPTIND - 1)) # Check arguments if [[ $# != 0 ]]; then usage fi # Check whether all the appropriate directories and files exist create_dirs "$localdir" # Determine which EPOC device machine_id=` plpftp machinfo | awk -F: '$1==" Machine UID" { print $2 }' | sed -e 's/ //g' ` machine_name=` grep -v "^#" $HOME/epoc/etc/epoctab | awk -F"\t" '$1=="'$machine_id'" {print $2}' ` # Deal with the possibility of no name entry if [[ -z $machine_name ]] ; then echo "This is the first time this device has been backed up" echo "on this machine." echo "Please enter a machine name - eg: My Psion" read -p "Machine name: " machine_name # Should have a check to make sure this name hasn't already been used. echo "#" >> $localdir/etc/epoctab echo "$machine_id $machine_name" >> $localdir/etc/epoctab fi if [[ ! -d "$localdir"/backups/"$machine_name" ]] ; then mkdir "$localdir"/backups/"$machine_name" fi # Create new entry in log file #echo "==================================================" >> "$localdir"/etc/dumplog #echo "== Starting backup ===============================" >> "$localdir"/etc/dumplog #echo "==================================================" >> "$localdir"/etc/dumplog # Shut down all processes running on the EPOC machine echo killsave \"$localdir/etc/proclist.$machine_name\" | plpftp # plpftp >> "$localdir"/etc/dumplog # Determine the drives on the machine, and process one by one process_drives "$localdir" "$psidir" $full_backup "$machine_name" $dump_drive_list # Restart all the stopped processes echo runrestore \"$localdir/etc/proclist.$machine_name\" | plpftp # plpftp >> "$localdir"/etc/dumplog # This is a hardwired hack due to Macro5 not restarting correctly if [[ `grep -l "^Macro5" $localdir/etc/proclist."$machine_name"` ]] ; then echo "Manually restarting Macro5" echo run 'C:\System\Apps\Macro5\Macro5.app' | plpftp # plpftp >> "$localdir"/etc/dumplog fi plptools-1.0.26/etc/psidump.1000066400000000000000000000053471504470754400160150ustar00rootroot00000000000000.\" Manual page for psidump .TH psidump 1 "2001/1/15" "psidump 0.1" "System Administration" .SH NAME psidump \- This is an example of a sample man page .SH SYNOPSIS .B psidump .B [-Vvfi] .BI "[-D " disk ] .BI "[-m " mount_dir ] .BI "[-d " dump_dir ] .SH DESCRIPTION .I psidump is a command to back up files from an EPOC device to your PC. By default, it will only backup those files that have changed since it was last backed up. All backups are stored in their own separate files, and a directory is maintained which contains the latest version of all files on the system. .SH OPTIONS .TP .B \-V Display the version and exit .TP .B \-v Produce verbose logging output. .TP .B \-f This option forces a full back up to be done, rather than just backing up the files that have changed since the last backup. .TP .B \-i Performs an incremental back up - only backs up the files that have changed since the last backup. This is the default. .TP .BI "\-D " disk(s) Specify the EPOC disks to be backed up. This defaults to the 'C' and 'D' drives. For reasons of completeness, the 'Z' (ROM drive) can also be specified for backup (but there probably isn't any need to - unless you plan to do unspeakable things to the insides of your PDA!). .TP .BI "\-m " mount_dir Specify the mount directory. This defaults to the directory specified is your plptools configuration - usually /mnt/epoc. .TP .BI "\-d " dump_dir Specify the dump directory. This defaults to $HOME/epoc/backups. .SH ENVIRONMENT .TP $HOME/epoc Default directory where configuration and dump information is stored. .TP $HOME/epoc/etc Configuration information about your PDA(s) is stored here. .TP $HOME/epoc/backups All backups are stored under this directory. .SH FILES All files that are used are located in $HOME/epoc. The directories are: .TP $HOME/epoc/etc/plptab This file is a table of the different EPOC machines which have connected to this machine. The first entry is the machine id, and the second entry is the machine name. Note that there must be a between the two fields! Any lines starting with a '#' are comments. .TP $HOME/epoc/backups Executable files .TP $HOME/epoc/man Man pages .SH AUTHOR Alain Trembleau .SH COPYRIGHT Copyright (C) 2001 Alain Trembleau. .I psidump is open source software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. .I psidump is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. plptools-1.0.26/etc/ttytap.c000066400000000000000000000051071504470754400157350ustar00rootroot00000000000000/* plpfuse/Makefile.am * * This file is part of plptools. * * Copyright (C) 2001 Fritz Elfert * * 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 * along with this program; if not, see . */ #include #include #include #include #include #include #include #include #include #include #include static void usage(void) { fprintf(stderr, "usage: ttytap -q|\n"); exit(1); } int main(int argc, char **argv) { int fd; tapheader th; int i; int r; int major; int minor; int lastdir = -1; struct stat stbuf; char tbuf[256]; unsigned char buf[4096]; if (argc != 2) usage(); if (strcmp(argv[1], "-q")) { if (stat(argv[1], &stbuf)) { perror(argv[1]); exit(-1); } if (!S_ISCHR(stbuf.st_mode)) { fprintf(stderr, "%s is not a char device\n", argv[1]); exit(1); } major = MAJOR(stbuf.st_rdev); minor = MINOR(stbuf.st_rdev); } else { minor = 0; major = 0; } fd = open("/proc/ttytap", O_RDONLY); if (fd == -1) { perror("/proc/ttytap"); exit(-1); } if (ioctl(fd, TTYTAP_SETDEV, (major << 8) | minor)) { perror("ioctl /proc/ttytap"); exit(-1); } if ((minor == 0) && (major == 0)) { printf("Serial tapping switched off\n"); exit(0); } if (ioctl(fd, TTYTAP_GETQLEN, &i)) { perror("ioctl /proc/ttytap"); exit(-1); } fprintf(stderr, "Queue length is %d\n", i); while (1) { r = read(fd, &th, sizeof(th)); if (r != sizeof(th)) { perror("read /proc/ttytap"); exit(-1); } strftime(tbuf, 255, "%H:%M:%S", localtime(&th.stamp.tv_sec)); printf("%s.%06ld %c (%04d) ", tbuf, (unsigned long)th.stamp.tv_usec, (th.io) ? '<' : '>', th.len); r = read(fd, buf, th.len); if (r != th.len) { perror("read /proc/ttytap"); exit(-1); } for (i = 0; i < th.len; i++) printf("%02x ", buf[i] & 255); for (i = 0; i < th.len; i++) printf("%c", isprint(buf[i]) ? buf[i] : '.'); printf("\n"); fflush(stdout); } return 0; } plptools-1.0.26/etc/udev-usbserial-plptools.rules000066400000000000000000000002521504470754400221200ustar00rootroot00000000000000ACTION=="add", SUBSYSTEMS=="usb-serial", DRIVERS=="pl2303", RUN+="/etc/init.d/plptools start" ACTION=="remove", SUBSYSTEM=="usb-serial", RUN+="/etc/init.d/plptools stop" plptools-1.0.26/gl-mod/000077500000000000000000000000001504470754400146455ustar00rootroot00000000000000plptools-1.0.26/gl-mod/bootstrap/000077500000000000000000000000001504470754400166625ustar00rootroot00000000000000plptools-1.0.26/gnulib/000077500000000000000000000000001504470754400147465ustar00rootroot00000000000000plptools-1.0.26/lib/000077500000000000000000000000001504470754400142345ustar00rootroot00000000000000plptools-1.0.26/lib/.gitignore000066400000000000000000000000231504470754400162170ustar00rootroot00000000000000/*.la /*.lo /*.loT plptools-1.0.26/lib/Enum.cc000066400000000000000000000041011504470754400154430ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 2000 Henner Zeller * * 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 * along with this program; if not, see . * */ #include "config.h" #include #include "Enum.h" using namespace std; void EnumBase::i2sMapper::add(long i, const char* s) { stringMap.insert(pair(i, s)); } string EnumBase::i2sMapper::lookup (long i) const { i2s_map_t::const_iterator searchPtr = stringMap.find(i); if (searchPtr == stringMap.end()) return "[OUT-OF-RANGE]"; /* * now combine the probably the multiple strings belonging to this * integer */ string result; for (i = stringMap.count(i); i > 0 ; --i, ++searchPtr) { // this should be the case: assert(searchPtr != stringMap.end()); if (result.length() != 0) result += string(","); result += string(searchPtr->second); } return result; } long EnumBase::i2sMapper::lookup (const char *s) const { /* * look up a specific string. * Since speed does not matter, we just do an exhaustive * search. * Otherwise we would have to maintain another map * mapping strings to ints .. but it's not worth the memory */ i2s_map_t::const_iterator run = stringMap.begin(); while (run != stringMap.end() && strcmp(s, run->second)) { ++run; } if (run == stringMap.end()) return -1; return run->first; } bool EnumBase::i2sMapper::inRange (long i) const { return (stringMap.find(i) != stringMap.end()); } plptools-1.0.26/lib/Enum.h000066400000000000000000000224131504470754400153130ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 2000 Henner Zeller * * 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 * along with this program; if not, see . * */ #ifndef _ENUM_H_ #define _ENUM_H_ #include "config.h" #include #include #include #include #include /** * the Base for the Enum template. * currently, only purpose is to provide a class type for mapping * integer enum values to strings in the Enumeration class. * The mapping is done basically with a STL-multimap. * * @author Henner Zeller */ class EnumBase { protected: /** * maps integers (typically: enumeration values) to * Strings. Takes care of the fact, that an Integer may map * to multiple strings (sometimes multiple enumeration values * represent the same integer). * * Provides a means to get the string representation of an * integer and vice versa. * * @author Henner Zeller */ class i2sMapper { private: /** * there can be one value, mapping to multiple * strings. Therefore, we need a multimap. */ typedef std::multimap i2s_map_t; /** * just for the record. Mapping back a string to the * Integer value in question. Since Symbols must be unique, * there is only a 1:1 relation as opposed to i2s_map_t. So * we can use a normal map here. * * Since in the usual application, mapping a string back * to its value is not important performance wise (typically * in a frontend), so it is implemented as exhaustive search, * not as extra map. Saves some bits of memrory .. */ //typedef map s2i_map_t; i2s_map_t stringMap; public: /** * adds a new int -> string mapping * Does NOT take over responsibility for the * pointer (i.e. it is not freed), so it is save * to add constant strings provided in the program code. */ void add(long, const char*); /** * returns the string representation for this integer. * If there are multiple strings for this integer, * return a comma delimited list. */ std::string lookup(long) const; /** * returns the integer associated with the * given string or -1 if the value * is not found (XXX: this should throw * an exception). */ long lookup (const char *) const; /** * returns true, if we have an representation for * the given integer. */ bool inRange(long) const; }; }; /** * Wrapper class featuring range-checking and string * representation of enumerated values. * * The string representation capability is needed to provide a * generic input frontend for any Enumeration because text labels * are needed in GUIs, and, of course, aids debugging, because you * can provide a readable presentation of an entry if something * goes wrong. * * NOTE, that wrapping an enumeration with this class * does not mean any performance overhead at all since the * compiler optimizes the member calls away. Nor does an instance of * this class use more memory than the use of an usual Enumeration. * (i.e. sizeof(E) == sizeof(Enum)). * * Besides that, it provides a great opportunity to check the * code and make it bug free, esp. if you've to read the * Enumeration from some dubious integer source * (.. to make the dubious integer source bug free ;-) * * So there is no reason, not to use this class. * Alas, you've to provide a StringRepresentation for it, which may * be tedious for large enumerations. To make the Definition easier * and more readable, an ENUM_DEFINITION() macro is provided. * * FIXME: At the moment enumeration strings don't get translated by gettext * * @see #ENUM_DEFINITION * @author Henner Zeller */ template class Enum : private EnumBase { public: struct sdata { /** * The constructor of the static data part. * You've to provide a constructor for each Enumeration * you want to wrap with this class. Initializes * the string Representation map, the readable name * of this Enumeration and a default value. * * The constructor is called automatically on definition, * so this makes sure, that the static part is initialized * properly before the program starts. */ sdata(); i2sMapper stringRep; std::string name; E defaultValue; }; static sdata staticData; /** * The actual value hold by this instance */ E value; public: /** * default constructor. * Initialize with default value. */ Enum() : value(staticData.defaultValue) {} /** * initialize with Enumeration given. */ Enum(E init) : value(init){ // if this hits you and you're sure, that the // value is right .. is this Enum proper // initialized in the Enum::sdata::sdata() ? assert(inRange(init)); } /** * initialize with the string representation * XXX: throw Exception if not found ? */ Enum(const std::string& s) : value(getValueFor(s)) { assert(inRange(value)); } /** * assign an Enumeration of this type. In debug * version, assert, that it is really in the Range of * this Enumeration. */ inline Enum& operator = (E setval) { value = setval; assert(inRange(setval)); return *this; } inline Enum& operator = (const Enum& rhs) { if (&rhs != this) value = rhs.value; return *this; } /** * returns the enumeration value hold with this * enum. */ inline operator E () const { return value; } /** * returns the String representation for the value * represented by this instance. */ std::string toString() const { return getStringFor(value); } /** * returns the C string representation for the value * represented by this instance. */ operator const char *() const { return toString().c_str(); } /** * This static member returns true, if the integer value * given fits int the range of this Enumeration. Use this * to verify input/output. * Fitting in the range of Enumeration here means, that * there actually exists a String representation for it, * so this Enumeration is needed to be initialized properly * in its Enum::sdata::sdata() constructor, you've to * provide. For convenience, use the ENUM_DEFINITION() macro * for this. */ static bool inRange(long i) { return (staticData.stringRep.inRange(i)); } /** * returns the Name for this enumeration. Useful for * error reporting. */ static std::string getEnumName() { return staticData.name; } /** * gives the String represenatation of a specific * value of this Enumeration. */ static std::string getStringFor(E e) { return staticData.stringRep.lookup((long) e); } /** * returns the Value for a specific String. * XXX: throw OutOfRangeException ? */ static E getValueFor(const std::string &s) { return (E) staticData.stringRep.lookup(s.c_str()); } }; template typename Enum::sdata Enum::staticData; /** * Helper macro to construct an enumeration wrapper Enum for * a specific enum type. * * It defines the static variable holding the static * information and provides the head of its Constructor. * You only have to provide the string-mapping additions in the * constructor body. This macro behaves much like a function declaration, * i.e. you have to start the constructor with { .. * * usage example: *
 * // declaration of enumeration; somewhere
 * class rfsv {
 * [...]
 *	enum PSI_ERROR_CODES { E_PSI_GEN_NONE, E_PSI_GEN_FAIL, E_PSI_GEN_ARG };
 * [...]
 * };
 *
 * // definition of the Enum with the appropriate string representations
 * ENUM_DEFINITION(rfsv::PSI_ERROR_CODES,
 *		   rfsv::E_PSI_GEN_NONE) {
 *	stringRep.add(rfsv::E_PSI_GEN_NONE,	"no error");
 *	stringRep.add(rfsv::E_PSI_GEN_FAIL,	"general");
 *	stringRep.add(rfsv::E_PSI_GEN_ARG,	"bad argument");
 * }
 * 
* * @param EnumName The fully qualified Name of the enum type to be wrapped * @param init The fully qualified Name of the initialization * value. * * @author Henner Zeller */ /** * The definition of the static variable holding the static * data for this Enumeration wrapper. */ #define ENUM_DEFINITION_BEGIN(EnumName, initWith) \ /** \ * actual definition of the constructor for the static data. \ * This is called implicitly by the definition above. \ */ \ template <> Enum::sdata::sdata() : \ name(#EnumName),defaultValue(initWith) { #define ENUM_DEFINITION_END(EnumName) \ } template Enum< EnumName >::sdata Enum< EnumName >::staticData; /** * Writes enumeration's string representation. */ template inline std::ostream& operator << (std::ostream& out, const Enum &e) { return out << e.toString().c_str(); } #endif /* _ENUM_H_ */ plptools-1.0.26/lib/Makefile.am000066400000000000000000000032651504470754400162760ustar00rootroot00000000000000# lib/Makefile.am # # This file is part of plptools. # # Copyright (C) 1999-2002 Fritz Elfert # Copyright (C) 2007-2024 Reuben Thomas # # 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 # along with this program; if not, see . AM_CPPFLAGS = -I$(top_srcdir)/libgnu -I$(top_builddir)/libgnu AM_CXXFLAGS = $(THREADED_CXXFLAGS) $(WARN_CXXFLAGS) LDADD = $(HOSTENT_LIB) $(LIBSOCKET) pkglib_LTLIBRARIES = libplp.la libplp_la_SOURCES = bufferarray.cc bufferstore.cc iowatch.cc ppsocket.cc \ rfsv16.cc rfsv32.cc rfsvfactory.cc log.cc rfsv.cc rpcs32.cc rpcs16.cc \ rpcs.cc rpcsfactory.cc psitime.cc Enum.cc plpdirent.cc wprt.cc \ rclip.cc siscomponentrecord.cpp sisfile.cpp sisfileheader.cpp \ sisfilerecord.cpp sislangrecord.cpp sisreqrecord.cpp sistypes.cpp \ psibitmap.cpp psiprocess.cc noinst_HEADERS = bufferarray.h bufferstore.h iowatch.h ppsocket.h \ rfsv.h rfsv16.h rfsv32.h rfsvfactory.h log.h rpcs32.h rpcs16.h rpcs.h \ rpcsfactory.h psitime.h Enum.h plpdirent.h wprt.h plpintl.h rclip.h \ siscomponentrecord.h sisfile.h sisfileheader.h sisfilerecord.h \ sislangrecord.h sisreqrecord.h sistypes.h psibitmap.h psiprocess.h plptools-1.0.26/lib/bufferarray.cc000066400000000000000000000062131504470754400170550ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999 Philip Proudman * Copyright (C) 2000, 2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #include "config.h" #include "bufferstore.h" #include "bufferarray.h" bufferArray::bufferArray() { len = 0; lenAllocd = ALLOC_MIN; buff = new bufferStore[lenAllocd]; } bufferArray::bufferArray(const bufferArray & a) { len = a.len; lenAllocd = a.lenAllocd; buff = new bufferStore[lenAllocd]; for (int i = 0; i < len; i++) buff[i] = a.buff[i]; } bufferArray::~bufferArray() { delete []buff; } bufferStore bufferArray:: pop() { bufferStore ret; if (len > 0) { ret = buff[0]; len--; for (long i = 0; i < len; i++) { buff[i] = buff[i + 1]; } } return ret; } void bufferArray:: append(const bufferStore & b) { if (len == lenAllocd) { lenAllocd += ALLOC_MIN; bufferStore *nb = new bufferStore[lenAllocd]; for (long i = 0; i < len; i++) { nb[i] = buff[i]; } delete []buff; buff = nb; } buff[len++] = b; } void bufferArray:: push(const bufferStore & b) { if (len == lenAllocd) lenAllocd += ALLOC_MIN; bufferStore *nb = new bufferStore[lenAllocd]; for (long i = len; i > 0; i--) { nb[i] = buff[i - 1]; } nb[0] = b; delete[]buff; buff = nb; len++; } long bufferArray:: length(void) { return len; } void bufferArray:: clear(void) { len = 0; lenAllocd = ALLOC_MIN; delete []buff; buff = new bufferStore[lenAllocd]; } bufferArray &bufferArray:: operator =(const bufferArray & a) { delete []buff; len = a.len; lenAllocd = a.lenAllocd; buff = new bufferStore[lenAllocd]; for (int i = 0; i < len; i++) buff[i] = a.buff[i]; return *this; } bufferStore &bufferArray:: operator [](const unsigned long index) { return buff[index]; } bufferArray bufferArray:: operator +(const bufferStore &s) { bufferArray res = *this; res += s; return res; } bufferArray bufferArray:: operator +(const bufferArray &a) { bufferArray res = *this; res += a; return res; } bufferArray &bufferArray:: operator +=(const bufferArray &a) { lenAllocd += a.lenAllocd; bufferStore *nb = new bufferStore[lenAllocd]; for (int i = 0; i < len; i++) nb[len + i] = buff[i]; for (int i = 0; i < a.len; i++) nb[len + i] = a.buff[i]; len += a.len; delete []buff; buff = nb; return *this; } bufferArray &bufferArray:: operator +=(const bufferStore &s) { append(s); return *this; } plptools-1.0.26/lib/bufferarray.h000066400000000000000000000072201504470754400167160ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999 Philip Proudman * Copyright (C) 1999-2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #ifndef _BUFFERARRAY_H_ #define _BUFFERARRAY_H_ #include "config.h" class bufferStore; /** * An array of bufferStores */ class bufferArray { public: /** * constructs a new bufferArray. * A minimum of @ref ALLOC_MIN * elements is allocated. */ bufferArray(); /** * Constructs a new bufferArray. * * @param a The initial contents for this array. */ bufferArray(const bufferArray &a); /** * Destroys the bufferArray. */ ~bufferArray(); /** * Copys the bufferArray. */ bufferArray &operator =(const bufferArray &a); /** * Checks if this bufferArray is empty. * * @return true if the bufferArray is empty. */ bool empty() const; /** * Retrieves the bufferStore at given index. * * @return The bufferStore at index. */ bufferStore &operator [](const unsigned long index); /** * Appends a bufferStore to a bufferArray. * * @param s The bufferStore to be appended. * * @returns A new bufferArray with bufferStore appended to. */ bufferArray operator +(const bufferStore &s); /** * Concatenates two bufferArrays. * * @param a The bufferArray to be appended. * * @returns A new bufferArray consisting with a appended. */ bufferArray operator +(const bufferArray &a); /** * Appends a bufferStore to current instance. * * @param s The bufferStore to append. * * @returns A reference to the current instance with s appended. */ bufferArray &operator +=(const bufferStore &s); /** * Appends a bufferArray to current instance. * * @param a The bufferArray to append. * * @returns A reference to the current instance with a appended. */ bufferArray &operator +=(const bufferArray &a); /** * Removes the first bufferStore. * * @return The removed bufferStore. */ bufferStore pop(void); /** * Inserts a bufferStore at index 0. * * @param b The bufferStore to be inserted. */ void push(const bufferStore& b); /** * Appends a bufferStore. * * @param b The bufferStore to be appended. */ void append(const bufferStore& b); /** * Evaluates the current length. * * @return The current number of bufferStores */ long length(void); /** * Empties the bufferArray. */ void clear(void); private: /** * Minimum number of bufferStores to * allocate. */ static const long ALLOC_MIN = 5; /** * The current number of bufferStores in * this bufferArray. */ long len; /** * The current number of bufferStores * allocated. */ long lenAllocd; /** * The content. */ bufferStore* buff; }; inline bool bufferArray::empty() const { return len == 0; } #endif plptools-1.0.26/lib/bufferstore.cc000066400000000000000000000115561504470754400171010ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999 Philip Proudman * Copyright (C) 2000, 2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #include "config.h" #include "bufferstore.h" #include #include // Should be iostream.h, but won't build on Sun WorkShop C++ 5.0 #include #include #include #include #include using namespace std; bufferStore::bufferStore() : len(0) , lenAllocd(0) , start(0) , buff(0) { } bufferStore::bufferStore(const bufferStore &a) : start(0) { lenAllocd = (a.getLen() > MIN_LEN) ? a.getLen() : MIN_LEN; buff = (unsigned char *)malloc(lenAllocd); assert(buff); len = a.getLen(); memcpy(buff, a.getString(0), len); } bufferStore::bufferStore(const unsigned char *_buff, long _len) : start(0) { lenAllocd = (_len > MIN_LEN) ? _len : MIN_LEN; buff = (unsigned char *)malloc(lenAllocd); assert(buff); len = _len; memcpy(buff, _buff, len); } bufferStore &bufferStore::operator =(const bufferStore &a) { if (this != &a) { checkAllocd(a.getLen()); len = a.getLen(); memcpy(buff, a.getString(0), len); start = 0; } return *this; } void bufferStore::init() { start = 0; len = 0; } void bufferStore::init(const unsigned char *_buff, long _len) { checkAllocd(_len); start = 0; len = _len; memcpy(buff, _buff, len); } bufferStore::~bufferStore() { if (buff) ::free(buff); } unsigned long bufferStore::getLen() const { return (start > len) ? 0 : len - start; } unsigned char bufferStore::getByte(long pos) const { return buff[pos+start]; } uint16_t bufferStore::getWord(long pos) const { return buff[pos+start] + (buff[pos+start+1] << 8); } uint32_t bufferStore::getDWord(long pos) const { return buff[pos+start] + (buff[pos+start+1] << 8) + (buff[pos+start+2] << 16) + (buff[pos+start+3] << 24); } const char * bufferStore::getString(long pos) const { return (const char *)buff + pos + start; } ostream &operator<<(ostream &s, const bufferStore &m) { // save stream flags ostream::fmtflags old = s.flags(); for (int i = m.start; i < m.len; i++) s << hex << setw(2) << setfill('0') << (int)m.buff[i] << " "; // restore stream flags s.flags(old); s << "("; for (int i = m.start; i < m.len; i++) { unsigned char c = m.buff[i]; s << (unsigned char)(isprint(c) ? c : '.'); } return s << ")"; } void bufferStore::discardFirstBytes(int n) { start += n; if (start > len) start = len; } void bufferStore::checkAllocd(long newLen) { if (newLen >= lenAllocd) { do { lenAllocd = (lenAllocd < MIN_LEN) ? MIN_LEN : (lenAllocd * 2); } while (newLen >= lenAllocd); assert(lenAllocd); buff = (unsigned char *)realloc(buff, lenAllocd); assert(buff); } } void bufferStore::addByte(unsigned char cc) { checkAllocd(len + 1); buff[len++] = cc; } void bufferStore::addString(const char *s) { int l = strlen(s); checkAllocd(len + l); memcpy(&buff[len], s, l); len += l; } void bufferStore::addStringT(const char *s) { addString(s); addByte(0); } void bufferStore::addBytes(const unsigned char *s, int l) { checkAllocd(len + l); memcpy(&buff[len], s, l); len += l; } void bufferStore::addBuff(const bufferStore &s, long maxLen) { long l = s.getLen(); checkAllocd(len + l); if ((maxLen >= 0) && (maxLen < l)) l = maxLen; if (l > 0) { memcpy(&buff[len], s.getString(0), l); len += l; } } void bufferStore::addWord(int a) { checkAllocd(len + 2); buff[len++] = a & 0xff; buff[len++] = (a>>8) & 0xff; } void bufferStore::addDWord(long a) { checkAllocd(len + 4); buff[len++] = a & 0xff; buff[len++] = (a>>8) & 0xff; buff[len++] = (a>>16) & 0xff; buff[len++] = (a>>24) & 0xff; } void bufferStore::truncate(long newLen) { if (newLen < len) len = newLen; } void bufferStore::prependByte(unsigned char cc) { checkAllocd(len + 1); memmove(&buff[1], buff, len++); buff[0] = cc; } void bufferStore::prependWord(int a) { checkAllocd(len + 2); memmove(&buff[2], buff, len); len += 2; buff[0] = a & 0xff; buff[1] = (a>>8) & 0xff; } plptools-1.0.26/lib/bufferstore.h000066400000000000000000000137331504470754400167420ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999 Philip Proudman * Copyright (C) 1999-2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #ifndef _BUFFERSTORE_H_ #define _BUFFERSTORE_H_ #include #include #include /** * A generic container for an array of bytes. * * bufferStore provides an array of bytes which * can be accessed using various types. */ class bufferStore { public: /** * Constructs a new bufferStore. */ bufferStore(); /** * Constructs a new bufferStore and * initializes its content. * * @param buf Pointer to data for initialization. * @param len Length of data for initialization. */ bufferStore(const unsigned char *, long); /** * Destroys a bufferStore instance. */ ~bufferStore(); /** * Constructs a new bufferStore and * initializes its content. * * @param b A bufferStore, whose content is * used for initialization. */ bufferStore(const bufferStore &); /** * Copies a bufferStore. */ bufferStore &operator =(const bufferStore &); /** * Retrieves the length of a bufferStore. * * @returns The current length of the contents * in bytes. */ unsigned long getLen() const; /** * Retrieves the byte at index pos. * * @param pos The index of the byte to retrieve. * * @returns The value of the byte at index pos */ unsigned char getByte(long pos = 0) const; /** * Retrieves the word at index pos. * * @param pos The index of the word to retrieve. * * @returns The value of the word at index pos */ uint16_t getWord(long pos = 0) const; /** * Retrieves the dword at index pos. * * @param pos The index of the dword to retrieve. * * @returns The value of the dword at index pos */ uint32_t getDWord(long pos = 0) const; /** * Retrieves the characters at index pos. * * @param pos The index of the characters to retrieve. * * @returns A pointer to characters at index pos */ const char * getString(long pos = 0) const; /** * Removes bytes from the start of the buffer. * * @param len Number of bytes to remove. */ void discardFirstBytes(int len = 0); /** * Prints a dump of the content. * * Mainly used for debugging purposes. * * @param s The stream to write to. * @param b The bufferStore do be dumped. * * @returns The stream. */ friend std::ostream &operator<<(std::ostream &, const bufferStore &); /** * Tests if the bufferStore is empty. * * @returns true, if the bufferStore is empty. * false, if it contains data. */ bool empty() const; /** * Initializes the bufferStore. * * All data is removed, the length is * reset to 0. */ void init(); /** * Initializes the bufferStore with * a given data. * * @param buf Pointer to data to initialize from. * @param len Length of data. */ void init(const unsigned char * buf, long len); /** * Appends a byte to the content of this instance. * * @param c The byte to append. */ void addByte(unsigned char c); /** * Appends a word to the content of this instance. * * @param w The word to append. */ void addWord(int); /** * Appends a dword to the content of this instance. * * @param dw The dword to append. */ void addDWord(long dw); /** * Appends a string to the content of this instance. * * The trailing zero byte is not copied * to the content. * * @param s The string to append. */ void addString(const char *s); /** * Appends a string to the content of this instance. * * The trailing zero byte is copied * to the content. * * @param s The string to append. */ void addStringT(const char *s); /** * Appends data to the content of this instance. * * @param buf The data to append. * @param len Length of data. */ void addBytes(const unsigned char *buf, int len); /** * Appends data to the content of this instance. * * @param b The bufferStore whose content to append. * @param maxLen Length of content to append. If * @p maxLen is less than 0 or greater than * the current length of @p b , then the * whole content of @p b is appended. */ void addBuff(const bufferStore &b, long maxLen = -1); /** * Truncates the buffer. * If the buffer is smaller, does nothing. * * @param newLen The new length of the buffer. */ void truncate(long newLen); /** * Prepends a byte to the content of this instance. * * @param c The byte to append. */ void prependByte(unsigned char c); /** * Prepends a word to the content of this instance. * * @param w The word to append. */ void prependWord(int); private: void checkAllocd(long newLen); long len; long lenAllocd; long start; unsigned char * buff; enum c { MIN_LEN = 300 }; }; inline bool bufferStore::empty() const { return (len - start) == 0; } #endif plptools-1.0.26/lib/iowatch.cc000066400000000000000000000036251504470754400162070ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999 Philip Proudman * Copyright (C) 2000, 2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #include "config.h" #include "iowatch.h" #include #include #include #include #include IOWatch::IOWatch() { num = 0; io = new int [FD_SETSIZE]; memset(io, -1, FD_SETSIZE); } IOWatch::~IOWatch() { delete [] io; } void IOWatch::addIO(const int fd) { int pos; for (pos = 0; pos < num && fd < io[pos]; pos++); if (io[pos] == fd) return; for (int i = num; i > pos; i--) io[i] = io[i-1]; io[pos] = fd; num++; } void IOWatch::remIO(const int fd) { int pos; for (pos = 0; pos < num && fd != io[pos]; pos++); if (pos != num) { num--; for (int i = pos; i 0) { int maxfd = 0; fd_set iop; FD_ZERO(&iop); for (int i = 0; i < num; i++) { FD_SET(io[i], &iop); if (io[i] > maxfd) maxfd = io[i]; } struct timeval t; t.tv_usec = usecs; t.tv_sec = secs; return (select(maxfd+1, &iop, NULL, NULL, &t) > 0); } sleep(secs); usleep(usecs); return false; } plptools-1.0.26/lib/iowatch.h000066400000000000000000000036301504470754400160450ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999 Philip Proudman * Copyright (C) 1999-2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #ifndef _IOWATCH_H_ #define _IOWATCH_H_ /** * A simple wrapper for select() * * IOWatch is a simple wrapper for the select * system call. In particular, it takes care * of passing the maximum file descriptor * argument (arg 1) of select() correctly. * IOWatch handles select on read descriptors only. */ class IOWatch { public: /** * Creates a new instance. */ IOWatch(); /** * Destroys an instance. */ ~IOWatch(); /** * Adds a file descriptor to * the set of descriptors. * * @param fd The file descriptor to add. */ void addIO(const int fd); /** * Removes a file descriptor from the * set of descriptors. * * @param fd The file descriptor to remove. */ void remIO(const int fd); /** * Performs a select() call. * * @param secs Number of seconds to wait. * @param usecs Number of microseconds to wait. * * @return true, if any of the descriptors is * readable. */ bool watch(const long secs, const long usecs); private: int num; int *io; }; #endif plptools-1.0.26/lib/log.cc000066400000000000000000000025321504470754400153260ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999 Philip Proudman * Copyright (C) 1999-2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #include "config.h" #include "log.h" #include "ignore-value.h" #include logbuf::logbuf(int loglevel, int fd) { ptr = buf; len = 0; _on = true; _level = loglevel; _fd = fd; } int logbuf::overflow(int c) { if (c == '\n') { *ptr++ = '\n'; *ptr = '\0'; if (_on) syslog(_level, "%s", buf); else if (_fd != -1) ignore_value(write(_fd, buf, len + 1)); ptr = buf; len = 0; return 0; } if ((len + 2) >= sizeof(buf)) return EOF; *ptr++ = c; len++; return 0; } plptools-1.0.26/lib/log.h000066400000000000000000000062041504470754400151700ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999 Philip Proudman * Copyright (C) 1999-2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #ifndef _LOG_H_ #define _LOG_H_ #include #include #include /** * A streambuffer, logging via syslog * * logbuf can be used, if you want to use syslog for * logging but don't want to change all your nice * C++-style output statements in your code. * * Here is an example showing the usage of logbuf: * *
 *	openlog("myDaemon", LOG_CONS|LOG_PID, LOG_DAEMON);
 *	logbuf ebuf(LOG_ERR, 2);
 *	ostream lerr(&ebuf);
 *
 *	... some code ...
 *
 *	lerr << "Whoops, got an error" << endl;
 * 
* * The second optional argument of the constructor can be used * to switch the output destination between syslog and some * file. If it is omitted or set to -1, logging can be switched on * or off. The initial state is on. */ class logbuf : public std::streambuf { public: /** * Constructs a new instance. * * @param loglevel The log level for this instance. * see syslog(3) for symbolic names to use. * @param fd An optional file descriptor to use * if switched off. */ logbuf(int loglevel, int fd = -1); /** * Switches loggin on or off. * * @param newstate The desired state. */ void setOn(bool newstate) { _on = newstate; } /** * Modifies the loglevel of this instance. * * @param newlevel The new loglevel. */ void setLevel(int newlevel) { _level = newlevel; } /** * Retrieve the current state. * * @returns The current state. */ bool on() { return _on; } /** * Retrieves the current loglevel. * * @returns The current loglevel. */ int level() { return _level; } /** * Called by the associated * ostream to write a character. * Stores the character in a buffer * and calls syslog(level, buffer) * whenever a LF is seen. */ int overflow(int c = EOF); private: /** * Pointer to next char in buffer. */ char *ptr; /** * Current length of buffer. */ unsigned int len; /** * The log level to use with syslog. */ int _level; /** * File descriptor to use when switched off. * If this is -1, don't output anything. */ int _fd; /** * Log flag. */ bool _on; /** * The internal buffer for holding * messages. */ char buf[1024]; }; #endif plptools-1.0.26/lib/plpdirent.cc000066400000000000000000000115161504470754400165500ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999-2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #include "config.h" #include "plpdirent.h" #include using namespace std; PlpUID::PlpUID() { memset(uid, 0, sizeof(uid)); } PlpUID::PlpUID(const uint32_t u1, const uint32_t u2, const uint32_t u3) { uid[0] = u1; uid[1] = u2; uid[2] = u3; } uint32_t PlpUID:: operator[](int idx) { assert ((idx > -1) && (idx < 3)); return uid[idx]; } PlpDirent::PlpDirent() : size(0), attr(0), name(""), time(0L), attrstr("") { } PlpDirent::PlpDirent(const PlpDirent &e) { size = e.size; attr = e.attr; time = e.time; UID = e.UID; name = e.name; attrstr = e.attrstr; } PlpDirent::PlpDirent(const uint32_t _size, const uint32_t _attr, const uint32_t tHi, const uint32_t tLo, const char * const _name) { size = _size; attr = _attr; time = PsiTime(tHi, tLo); UID = PlpUID(); name = _name; attrstr = ""; } uint32_t PlpDirent:: getSize() { return size; } uint32_t PlpDirent:: getAttr() { return attr; } uint32_t PlpDirent:: getUID(int uididx) { if ((uididx >= 0) && (uididx < 4)) return UID[uididx]; return 0; } PlpUID &PlpDirent:: getUID() { return UID; } const char *PlpDirent:: getName() { return name.c_str(); } PsiTime PlpDirent:: getPsiTime() { return time; } void PlpDirent:: setName(const char *str) { name = str; } PlpDirent &PlpDirent:: operator=(const PlpDirent &e) { size = e.size; attr = e.attr; time = e.time; UID = e.UID; name = e.name; attrstr = e.attrstr; return *this; } ostream & operator<<(ostream &o, const PlpDirent &e) { ostream::fmtflags old = o.flags(); o << e.attrstr << " " << dec << setw(10) << setfill(' ') << e.size << " " << e.time << " " << e.name; o.flags(old); return o; } PlpDrive::PlpDrive() { } PlpDrive::PlpDrive(const PlpDrive &other) { } void PlpDrive:: setMediaType(uint32_t type) { mediatype = type; } void PlpDrive:: setDriveAttribute(uint32_t attr) { driveattr = attr; } void PlpDrive:: setMediaAttribute(uint32_t attr) { mediaattr = attr; } void PlpDrive:: setUID(uint32_t attr) { uid = attr; } void PlpDrive:: setSize(uint32_t sizeLo, uint32_t sizeHi) { size = ((unsigned long long)sizeHi << 32) + sizeLo; } void PlpDrive:: setSpace(uint32_t spaceLo, uint32_t spaceHi) { space = ((unsigned long long)spaceHi << 32) + spaceLo; } void PlpDrive:: setName(char drive, const char * const volname) { drivechar = drive; name = ""; name += volname; } uint32_t PlpDrive:: getMediaType() { return mediatype; } static const char * const media_types[] = { N_("Not present"), N_("Unknown"), N_("Floppy"), N_("Disk"), N_("CD-ROM"), N_("RAM"), N_("Flash Disk"), N_("ROM"), N_("Remote"), }; void PlpDrive:: getMediaType(string &ret) { ret = media_types[mediatype]; } uint32_t PlpDrive:: getDriveAttribute() { return driveattr; } static void appendWithDelim(string &s1, const char * const s2) { if (!s1.empty()) s1 += ','; s1 += s2; } void PlpDrive:: getDriveAttribute(string &ret) { ret = ""; if (driveattr & 1) appendWithDelim(ret, _("local")); if (driveattr & 2) appendWithDelim(ret, _("ROM")); if (driveattr & 4) appendWithDelim(ret, _("redirected")); if (driveattr & 8) appendWithDelim(ret, _("substituted")); if (driveattr & 16) appendWithDelim(ret, _("internal")); if (driveattr & 32) appendWithDelim(ret, _("removable")); } uint32_t PlpDrive:: getMediaAttribute() { return mediaattr; } void PlpDrive:: getMediaAttribute(string &ret) { ret = ""; if (mediaattr & 1) appendWithDelim(ret, _("variable size")); if (mediaattr & 2) appendWithDelim(ret, _("dual density")); if (mediaattr & 4) appendWithDelim(ret, _("formattable")); if (mediaattr & 8) appendWithDelim(ret, _("write protected")); } uint32_t PlpDrive:: getUID() { return uid; } uint64_t PlpDrive:: getSize() { return size; } uint64_t PlpDrive:: getSpace() { return space; } string PlpDrive:: getName() { return name; } char PlpDrive:: getDrivechar() { return drivechar; } plptools-1.0.26/lib/plpdirent.h000066400000000000000000000203201504470754400164030ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999-2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #ifndef _PLPDIRENT_H_ #define _PLPDIRENT_H_ #include #include #include #include #include /** * A class, representing the UIDs of a file on the Psion. * Every File on the Psion has a unique UID for determining * the application-mapping. This class stores these UIDs. * An object of this class is contained in every @ref PlpDirent * object. * * @author Fritz Elfert */ class PlpUID { friend inline bool operator<(const PlpUID &u1, const PlpUID &u2); public: /** * Default constructor. */ PlpUID(); /** * Constructor. * Create an instance, presetting all thre uid values. */ PlpUID(const uint32_t u1, const uint32_t u2, const uint32_t u3); /** * Retrieve a UID value. * * @param idx The index of the desired UID. Range must be (0..2), * otherwise an assertion is triggered. */ uint32_t operator[](int idx); private: long uid[3]; }; inline bool operator<(const PlpUID &u1, const PlpUID &u2) { return (memcmp(u1.uid, u2.uid, sizeof(u1.uid)) < 0); } /** * A class, representing a directory entry of the Psion. * Objects of this type are used by @ref rfsv::readdir , * @ref rfsv::dir and @ref rfsv::fgeteattr for returning * the entries of a directory. * * @author Fritz Elfert */ class PlpDirent { friend class rfsv32; friend class rfsv16; public: /** * Default constructor */ PlpDirent(); /** * A copy constructor. * Mainly used by STL container classes. * * @param d The object to be used as initializer. */ PlpDirent(const PlpDirent &d); /** * Initializing Constructor */ PlpDirent(const uint32_t size, const uint32_t attr, const uint32_t tHi, const uint32_t tLo, const char * const name); /** * Default destructor. */ ~PlpDirent() {}; /** * Retrieves the file size of a directory entry. * * @returns The file size in bytes. */ uint32_t getSize(); /** * Retrieves the file attributes of a directory entry. * * @returns The generic attributes ( @ref rfsv:file_attribs ). */ uint32_t getAttr(); /** * Retrieves the UIDs of a directory entry. * This method returns always 0 with a Series3. * * @param uididx The index of the UID to retrieve (0 .. 2). * * @returns The selected UID or 0 if the index is out of range. */ uint32_t getUID(int uididx); /** * Retrieves the @ref PlpUID object of a directory entry. * * @returns The PlpUID object. */ PlpUID &getUID(); /** * Retrieve the file name of a directory entry. * * @returns The name of the file. */ const char *getName(); /** * Retrieve the modification time of a directory entry. * * @returns A @ref PsiTime object, representing the time. */ PsiTime getPsiTime(); /** * Set the file name of a directory entry. * This is currently unused. It does NOT * change the name of the corresponding file on * the Psion. * * @param str The new name of the file. */ void setName(const char *str); /** * Assignment operator * Mainly used by STL container classes. * * @param e The new value to assign. * * @returns The modified object. */ PlpDirent &operator=(const PlpDirent &e); /** * Prints the object contents. * The output is in human readable similar to the * output of a "ls" command. */ friend std::ostream &operator<<(std::ostream &o, const PlpDirent &e); private: uint32_t size; uint32_t attr; PlpUID UID; PsiTime time; std::string attrstr; std::string name; }; /** * A class representing information about * a Disk drive on the psion. An Object of this type * is used by @ref rfsv::devinfo for returning the * information of the probed drive. * * @author Fritz Elfert */ class PlpDrive { friend class rfsv32; friend class rfsv16; public: /** * Default constructor. */ PlpDrive(); /** * Copy constructor */ PlpDrive(const PlpDrive &other); /** * Retrieve the media type of the drive. * * @returns The media type of the probed drive. *
    * Media types are encoded by a number
    * in the range 0 .. 8 with the following
    * meaning:
    *
    *   0 = Not present
    *   1 = Unknown
    *   2 = Floppy
    *   3 = Disk
    *   4 = CD-ROM
    *   5 = RAM
    *   6 = Flash Disk
    *   7 = ROM
    *   8 = Remote
    * 
*/ uint32_t getMediaType(); /** * Retrieve the media type of the drive. * Just like the above function, but returns * the media type as human readable string. * * @param ret The string is returned here. */ void getMediaType(std::string &ret); /** * Retrieve the attributes of the drive. * * @returns The attributes of the probed drive. *
    * Drive attributes are encoded by a number
    * in the range 0 .. 63. The bits have the
    * the following meaning:
    *
    *   bit 0 = local
    *   bit 1 = ROM
    *   bit 2 = redirected
    *   bit 3 = substituted
    *   bit 4 = internal
    *   bit 5 = removable
    * 
*/ uint32_t getDriveAttribute(); /** * Retrieve the attributes of the drive. * Just like the above function, but returns * the attributes as human readable string. * * @param ret The string is returned here. */ void getDriveAttribute(std::string &ret); /** * Retrieve the attributes of the media. * * @returns The attributes of the probed media. *
    * Media attributes are encoded by a number
    * in the range 0 .. 15. The bits have the
    * following meaning:
    *
    *   bit 0 = variable size
    *   bit 1 = dual density
    *   bit 2 = formattable
    *   bit 3 = write protected
    * 
*/ uint32_t getMediaAttribute(); /** * Retrieve the attributes of the media. * Just like the above function, but returns * the attributes as human readable string. * * @param ret The string is returned here. */ void getMediaAttribute(std::string &ret); /** * Retrieve the UID of the drive. * Each drive, except the ROM drive on a Psion has * a unique ID which can be retrieved here. * * @returns The UID of the probed drive. */ uint32_t getUID(); /** * Retrieve the total capacity of the drive. * * @returns The capacity of the probed drive in bytes. */ uint64_t getSize(); /** * Retrieve the free capacity on the drive. * * @returns The free space on the probed drive in bytes. */ uint64_t getSpace(); /** * Retrieve the volume name of the drive. * * returns The volume name of the drive. */ std::string getName(); /** * Retrieve the drive letter of the drive. * * returns The letter of the probed drive. */ char getDrivechar(); private: void setMediaType(uint32_t type); void setDriveAttribute(uint32_t attr); void setMediaAttribute(uint32_t attr); void setUID(uint32_t uid); void setSize(uint32_t sizeLo, uint32_t sizeHi); void setSpace(uint32_t spaceLo, uint32_t spaceHi); void setName(char drive, const char * const volname); uint32_t mediatype; uint32_t driveattr; uint32_t mediaattr; uint32_t uid; uint64_t size; uint64_t space; char drivechar; std::string name; }; #endif plptools-1.0.26/lib/plpintl.h000066400000000000000000000023061504470754400160700ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999-2002 Fritz Elfert * * 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 * along with this program; if not, see . * */ #ifndef _PLPINTL_H_ #define _PLPINTL_H_ #include "config.h" /* libintl.h includes locale.h only if optimized. * however, we need LC_ALL ... */ #include #if defined(ENABLE_NLS) # include # define _(String) gettext (String) # define N_(String) (String) #else # define _(String) (String) # define N_(String) String # define textdomain(Domain) # define bindtextdomain(Package, Directory) #endif #endif /* _PLPINTL_H_ */ plptools-1.0.26/lib/ppsocket.cc000066400000000000000000000326671504470754400164110ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999 Philip Proudman * Copyright (C) 1999-2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #include "config.h" #include "bufferstore.h" #include "ppsocket.h" #include "iowatch.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #define INVALID_SOCKET -1 #define SOCKET_ERROR -1 #ifndef MSG_NOSIGNAL #define MSG_NOSIGNAL 0 #endif using namespace std; ppsocket::ppsocket(const ppsocket & another) { m_Socket = another.m_Socket; m_HostAddr = another.m_HostAddr; m_PeerAddr = another.m_PeerAddr; m_Bound = another.m_Bound; m_LastError = another.m_LastError; myWatch = another.myWatch; } ppsocket::ppsocket() { m_Socket = INVALID_SOCKET; memset(&m_HostAddr, 0, sizeof(m_HostAddr)); memset(&m_PeerAddr, 0, sizeof(m_PeerAddr)); ((struct sockaddr_in *) &m_HostAddr)->sin_family = AF_INET; ((struct sockaddr_in *) &m_PeerAddr)->sin_family = AF_INET; m_Bound = false; m_LastError = 0; myWatch = 0L; } ppsocket::~ppsocket() { if (m_Socket != INVALID_SOCKET) { if (myWatch) myWatch->remIO(m_Socket); shutdown(m_Socket, SHUT_RDWR); ::close(m_Socket); } } void ppsocket:: setWatch(IOWatch *watch) { if (watch) { if (myWatch && (m_Socket != INVALID_SOCKET)) myWatch->remIO(m_Socket); myWatch = watch; } } bool ppsocket:: reconnect() { if (m_Socket != INVALID_SOCKET) { if (myWatch) myWatch->remIO(m_Socket); shutdown(m_Socket, SHUT_RDWR); ::close(m_Socket); } m_Socket = INVALID_SOCKET; if (!createSocket()) return (false); m_LastError = 0; m_Bound = false; if (::bind(m_Socket, &m_HostAddr, sizeof(m_HostAddr)) != 0) { m_LastError = errno; return (false); } if (::connect(m_Socket, &m_PeerAddr, sizeof(m_PeerAddr)) != 0) { m_LastError = errno; return (false); } if (myWatch) myWatch->addIO(m_Socket); return (true); } string ppsocket:: toString() { string ret = ""; char nbuf[10]; char *tmp = 0L; tmp = inet_ntoa(((struct sockaddr_in *) &m_HostAddr)->sin_addr); ret += tmp ? tmp : "none:none"; if (tmp) { ret += ':'; sprintf(nbuf, "%d", ntohs(((struct sockaddr_in *) &m_HostAddr)->sin_port)); ret += nbuf; } ret += " -> "; tmp = inet_ntoa(((struct sockaddr_in *) &m_PeerAddr)->sin_addr); ret += tmp ? tmp : "none:none"; if (tmp) { ret += ':'; sprintf(nbuf, "%d", ntohs(((struct sockaddr_in *) &m_PeerAddr)->sin_port)); ret += nbuf; } return ret; } bool ppsocket:: connect(const char * const Peer, int PeerPort, const char * const Host, int HostPort) { //**************************************************** //* If we aren't already bound set the host and bind * //**************************************************** if (!bindSocket(Host, HostPort)) { if (m_LastError != 0) { return (false); } } //**************** //* Set the peer * //**************** if (!setPeer(Peer, PeerPort)) { return (false); } //*********** //* Connect * //*********** if (::connect(m_Socket, &m_PeerAddr, sizeof(m_PeerAddr)) != 0) { m_LastError = errno; return (false); } if (myWatch) myWatch->addIO(m_Socket); return (true); } bool ppsocket:: listen(const char * const Host, int Port) { //**************************************************** //* If we aren't already bound set the host and bind * //**************************************************** if (!bindSocket(Host, Port)) { if (m_LastError != 0) { return (false); } } //********************** //* Listen on the port * //********************** if (myWatch) myWatch->addIO(m_Socket); if (::listen(m_Socket, 5) != 0) { m_LastError = errno; return (false); } return (true); } ppsocket *ppsocket:: accept(string *Peer, IOWatch *iow) { socklen_t len; ppsocket *accepted; char *peer; //***************************************************** //* Allocate a new object to hold the accepted socket * //***************************************************** accepted = new ppsocket; if (!iow) iow = myWatch; if (!accepted) { m_LastError = errno; return NULL; } //*********************** //* Accept a connection * //*********************** len = sizeof(struct sockaddr); accepted->m_Socket = ::accept(m_Socket, &accepted->m_PeerAddr, &len); if (accepted->m_Socket == INVALID_SOCKET) { m_LastError = errno; delete accepted; return NULL; } //**************************************************** //* Got a connection so fill in the other attributes * //**************************************************** // Make sure the new socket hasn't inherited O_NONBLOCK // from the accept socket int flags = fcntl(accepted->m_Socket, F_GETFL, 0); flags &= ~O_NONBLOCK; fcntl(accepted->m_Socket, F_SETFL, flags); accepted->m_HostAddr = m_HostAddr; accepted->m_Bound = true; //**************************************************** //* If required get the name of the connected client * //**************************************************** if (Peer) { peer = inet_ntoa(((struct sockaddr_in *) &accepted->m_PeerAddr)->sin_addr); if (peer) *Peer = peer; } if (accepted && iow) { accepted->setWatch(iow); iow->addIO(accepted->m_Socket); } return accepted; } bool ppsocket:: dataToGet(int sec, int usec) const { fd_set io; FD_ZERO(&io); FD_SET(m_Socket, &io); struct timeval t; t.tv_usec = usec; t.tv_sec = sec; return (select(m_Socket + 1, &io, NULL, NULL, &t) != 0) ? true : false; } int ppsocket:: getBufferStore(bufferStore & a, bool wait) { /* Returns a 0 for for no message, * 1 for message OK, and -1 for socket problem */ uint32_t l; long count = 0; unsigned char *buff; unsigned char *bp; if (!wait && !dataToGet(0, 0)) return 0; a.init(); if (recv(&l, sizeof(l), MSG_NOSIGNAL) != sizeof(l)) { return -1; } l = ntohl(l); if (l > 16384) return -1; bp = buff = new unsigned char[l]; while (l > 0) { int j = recv(bp, l, MSG_NOSIGNAL); if (j == SOCKET_ERROR || j == 0) { delete[]buff; return -1; } count += j; l -= j; bp += j; }; a.init(buff, count); delete[]buff; return (a.getLen() == 0) ? 0 : 1; } bool ppsocket:: sendBufferStore(const bufferStore & a) { long l = a.getLen(); uint32_t hl = htonl(l); long sent = 0; int retries = 0; int i; bufferStore b; b.addBytes(reinterpret_cast(&hl), sizeof(hl)); b.addBuff(a); l += 4; while (l > 0) { i = send((const char *)b.getString(sent), l, MSG_NOSIGNAL); if (i == SOCKET_ERROR || i == 0) return (false); sent += i; l -= i; if (++retries > 5) { m_LastError = 0; return (false); } } return true; } int ppsocket:: recv(void *buf, int len, int flags) { int i = ::recv(m_Socket, buf, len, flags); if (i < 0) m_LastError = errno; return (i); } int ppsocket:: send(const void * const buf, int len, int flags) { int i = ::send(m_Socket, buf, len, flags); if (i < 0) m_LastError = errno; return (i); } bool ppsocket:: closeSocket(void) { if (myWatch) myWatch->remIO(m_Socket); shutdown(m_Socket, SHUT_RDWR); if (::close(m_Socket) != 0) { m_LastError = errno; return false; } m_Socket = INVALID_SOCKET; return true; } bool ppsocket:: bindSocket(const char * const Host, int Port) { // If we are already bound return false but with no last error if (m_Bound) { m_LastError = 0; return false; } // If the socket hasn't been created create it now if (m_Socket == INVALID_SOCKET) { if (!createSocket()) return false; } // Set SO_REUSEADDR int one = 1; if (setsockopt(m_Socket, SOL_SOCKET, SO_REUSEADDR, (const char *)&one, sizeof(int)) < 0) cerr << "Warning: Unable to set SO_REUSEADDR option\n"; // If a host name was supplied then use it if (!setHost(Host, Port)) return false; // Now bind the socket if (::bind(m_Socket, &m_HostAddr, sizeof(m_HostAddr)) != 0) { m_LastError = errno; return false; } m_Bound = true; return true; } bool ppsocket:: bindInRange(const char * const Host, int Low, int High, int Retries) { int port; int i; // If we are already bound return false but with no last error if (m_Bound) { m_LastError = 0; return (false); } // If the socket hasn't been created create it now if (m_Socket == INVALID_SOCKET) { if (!createSocket()) return false; } // If the number of retries is greater than the range then work // through the range sequentially. if (Retries > High - Low) { for (port = Low; port <= High; port++) { if (!setHost(Host, port)) return false; if (::bind(m_Socket, &m_HostAddr, sizeof(m_HostAddr)) == 0) break; } if (port > High) { m_LastError = errno; return false; } } else { for (i = 0; i < Retries; i++) { port = Low + (rand() % (High - Low)); if (!setHost(Host, port)) return false; if (::bind(m_Socket, &m_HostAddr, sizeof(m_HostAddr)) == 0) break; } if (i >= Retries) { m_LastError = errno; return false; } } m_Bound = true; return true; } bool ppsocket:: linger(bool LingerOn, int LingerTime) { int i; struct linger l; // If the socket hasn't been created create it now if (m_Socket == INVALID_SOCKET) { if (!createSocket()) return false; } // Set the lingering if (LingerOn) { l.l_onoff = 1; l.l_linger = LingerTime; } else { l.l_onoff = 0; l.l_linger = 0; } i = setsockopt(m_Socket, SOL_SOCKET, SO_LINGER, (const char *) &l, sizeof(l)); // Check for errors if (i != 0) { m_LastError = errno; return false; } return true; } bool ppsocket:: createSocket(void) { // If the socket has already been created just return true if (m_Socket != INVALID_SOCKET) return true; // Create the socket m_Socket = ::socket(PF_INET, SOCK_STREAM, 0); if (m_Socket == INVALID_SOCKET) { m_LastError = errno; return false; } // By default set no lingering linger(false); // Return indicating success return true; } bool ppsocket:: setPeer(const char * const Peer, int Port) { struct hostent *he = NULL; // If a peer name was supplied then use it if (Peer) { if (!isdigit(Peer[0])) // RFC1035 specifies that hostnames must not start // with a digit. So we can speed up things here. he = gethostbyname(Peer); if (!he) { struct in_addr ipaddr; if (!inet_aton(Peer, &ipaddr)) { m_LastError = errno; return false; } he = gethostbyaddr((const char *)&ipaddr.s_addr, sizeof(ipaddr.s_addr), PF_INET); if (!he) { m_LastError = errno; return (false); } } memcpy(&((struct sockaddr_in *)&m_PeerAddr)->sin_addr, he->h_addr_list[0], sizeof(((struct sockaddr_in *)&m_PeerAddr)->sin_addr)); } // If a port name was supplied use it if (Port > 0) ((struct sockaddr_in *)&m_PeerAddr)->sin_port = htons(Port); return true; } bool ppsocket:: getPeer(string *Peer, int *Port) { char *peer; if (Peer) { peer = inet_ntoa(((struct sockaddr_in *) &m_PeerAddr)->sin_addr); if (!peer) { m_LastError = errno; return (false); } *Peer = peer; } if (Port) *Port = ntohs(((struct sockaddr_in *) &m_PeerAddr)->sin_port); return false; } bool ppsocket:: setHost(const char * const Host, int Port) { struct hostent *he; // If a host name was supplied then use it if (Host) { if (!isdigit(Host[0])) // RFC1035 specifies that hostnames must not start // with a digit. So we can speed up things here. he = gethostbyname(Host); he = gethostbyname(Host); if (!he) { struct in_addr ipaddr; if (!inet_aton(Host, &ipaddr)) { m_LastError = errno; return false; } he = gethostbyaddr((const char *)&ipaddr.s_addr, sizeof(ipaddr.s_addr), PF_INET); if (!he) { m_LastError = errno; return false; } } memcpy(&((struct sockaddr_in *)&m_HostAddr)->sin_addr, he->h_addr_list[0], sizeof(((struct sockaddr_in *)&m_HostAddr)->sin_addr)); } // If a port name was supplied use it if (Port > 0) ((struct sockaddr_in *)&m_HostAddr)->sin_port = htons(Port); return true; } bool ppsocket:: getHost(string *Host, int *Port) { char *host; if (Host) { host = inet_ntoa(((struct sockaddr_in *)&m_HostAddr)->sin_addr); if (!host) { m_LastError = errno; return false; } *Host = host; } if (Port) *Port = ntohs(((struct sockaddr_in *)&m_HostAddr)->sin_port); return true; } plptools-1.0.26/lib/ppsocket.h000066400000000000000000000140761504470754400162450ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999 Philip Proudman * Copyright (C) 1999-2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #ifndef _PPSOCKET_H_ #define _PPSOCKET_H_ #include #include #include #include #include class bufferStore; class IOWatch; /** * A class for dealing with sockets. */ class ppsocket { public: /** * Constructs a ppsocket */ ppsocket(); /** * Copy constructor */ ppsocket(const ppsocket&); /** * Destructor */ virtual ~ppsocket(); /** * Connects to a given host. * * @param Peer The Host to connect to (name or dotquad-string). * @param PeerPort The port to connect to. * @param Host The local address to bind to. * @param HostPort The local port to bind to. * * @returns true on success, false otherwise. */ virtual bool connect(const char * const Peer, int PeerPort, const char * const Host = NULL, int HostPort = 0); /** * Reopens the connection after closing it. * * @returns true on success, false otherwise. */ virtual bool reconnect(); /** * Retrieve a string representation of the ppsocket. * * @returns a string in the form ": -> :" * where elements not known, are replaced by "???" and none-existing * elements are represented by the word "none". */ virtual std::string toString(); /** * Starts listening. * * @param Host The local address to bind to. * @param Port The local port to listen on. * * @returns true on success, false otherwise. */ virtual bool listen(const char * const Host, int Port); /** * Accept a connection. * * @param Peer If non-Null, the peer's name is returned here. * * @returns A pointer to a new instance for the accepted connection or NULL * if an error happened. */ ppsocket *accept(std::string *Peer, IOWatch *); /** * Check and optionally wait for incoming data. * * @param sec Timeout in seconds * @param usec Timeout in microseconds * * @returns true if data is available, false otherwise. */ bool dataToGet(int sec, int usec) const; /** * Receive data into a @ref bufferStore . * * @param a The bufferStore to fill with received data. * @param wait If true, wait until something is received, else return * if no data is available. * @returns 1 if a bufferStore received, 0, if no bufferStore received, -1 * on error. */ int getBufferStore(bufferStore &a, bool wait = true); /** * Sends data from a @ref bufferStore . * * @param a The bufferStore to send. * @returns true on success, false otherwise. */ bool sendBufferStore(const bufferStore &a); /** * Closes the connection. * * @returns true on success, false otherwise. */ bool closeSocket(void); /** * Binds to a local address and port. * * @param Host The local address to bind to. * @param Port The local port to listen on. * * @returns true on success, false otherwise. */ bool bindSocket(const char * const Host, int Port); /** * Tries repeated binds to a local address and port. * If @p Retries is <= @p High - @p Low, then * the port to bind is randomly chosen in the given range. * Otherwise, all ports starting from @p High up to @p Low * are tried in sequence. * * @param Host The local address to bind to. * @param Low The lowest local port to listen on. * @param High The highest local port to listen on. * @param Retries The number of retries until giving up. * * @returns true on success, false otherwise. */ bool bindInRange(const char * const Host, int Low, int High, int Retries); /** * Sets the linger parameter of the socket. * * @param LingerOn true, if lingering should be on. * @param LingerTime If lingering is on, the linger-time. * * @returns true on success, false otherwise. */ bool linger(bool LingerOn, int LingerTime = 0); /** * Retrieves peer information. * * @param Peer The peers name is returned here. * @param Port The peers port is returned here. * * @returns true on success, false otherwise. */ bool getPeer(std::string *Peer, int *Port); /** * Retrieves local information. * * @param Host The local name is returned here. * @param Port The local port is returned here. * * @returns true on success, false otherwise. */ bool getHost(std::string *Host, int *Port); /** * Registers an @ref IOWatch for this socket. * This IOWatch gets the socket added/removed * automatically. * * @param watch The IOWatch to register. */ void setWatch(IOWatch *watch); private: /** * Creates the socket. */ virtual bool createSocket(void); int getLastError(void) { return(m_LastError); } bool setPeer(const char * const Peer, int Port); bool setHost(const char * const Host, int Port); int recv(void *buf, int len, int flags); int send(const void * const buf, int len, int flags); struct sockaddr m_HostAddr; struct sockaddr m_PeerAddr; int m_Socket; int m_Port; bool m_Bound; int m_LastError; IOWatch *myWatch; }; #endif plptools-1.0.26/lib/psibitmap.cpp000066400000000000000000000130331504470754400167300ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999-2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #include "config.h" #include #include "psibitmap.h" void encodeBitmap(int width, int height, getPixelFunction_t getPixel, bool /*rle*/, bufferStore &out) { bufferStore ib; ib.addDWord(0x00000028); // hdrlen ib.addDWord(width); // xPixels ib.addDWord(height); // yPixels ib.addDWord(0); // xTwips (unspecified) ib.addDWord(0); // yTwips (unspecified) ib.addDWord(2); // bitsPerPixel ib.addDWord(0); // unknown1 ib.addDWord(0); // unknown2 ib.addDWord(0); // RLEflag bufferStore rawBuf; for (int y = 0; y < height; y++) { int ov = 0; int shift = 0; int bc = 0; for (int x = 0; x < width; x++) { int v = getPixel(x, y) / 85; ov |= (v << shift); if (shift == 6) { rawBuf.addByte(ov); bc++; shift = 0; ov = 0; } else shift += 2; } if (shift != 0) { rawBuf.addByte(ov); shift = 0; ov = 0; bc++; } while (bc % 4) { rawBuf.addByte(0); bc++; } } #if 1 ib.addBuff(rawBuf); #else //TODO: RLE encoding int rawLen = rawBuf.getLen(); int eqCount = 1; int lastByte = rawBuf.getByte(0); bufferStore diBuf; for (int i = 1; i <= rawLen; i++) { int v; if (i < rawLen) v = rawBuf.getByte(i); else v = lastByte + 1; if (v == lastByte) { if (diBuf.getLen()) { ib.addByte(0x100 - diBuf.getLen()); ib.addBuff(diBuf); diBuf.init(); } eqCount++; if (eqCount > 0x7f) { ib.addByte(0x7f); ib.addByte(v); eqCount = 1; } } else { if (eqCount > 1) { ib.addByte(eqCount); ib.addByte(lastByte); eqCount = 1; } else { diBuf.addByte(lastByte); if ((diBuf.getLen() > 0x7f) || (i == rawLen)) { ib.addByte(0x100 - diBuf.getLen()); ib.addBuff(diBuf); diBuf.init(); } } } lastByte = v; } #endif out.addDWord(ib.getLen() + 4); out.addBuff(ib); } #define splitByte(v) \ do { \ int j; \ \ if (x < bytesPerLine) \ for (j = 0; j < pixelsPerByte; j++) { \ if (j && ((oidx % xPixels) == 0)) \ break; \ else \ if (oidx >= picsize) \ return false; \ else { \ out.addByte((v & mask) * grayVal); \ v >>= bitsPerPixel; \ oidx++; \ } \ } \ if (++x >= linelen) \ x = 0; \ } while (0) bool decodeBitmap(const unsigned char *p, int &width, int &height, bufferStore &out) { uint32_t totlen = *((const uint32_t*)p); p += 4; uint32_t hdrlen = *((const uint32_t*)p); p += 4; uint32_t datlen = totlen - hdrlen; uint32_t xPixels = *((const uint32_t*)p); p += 4; uint32_t yPixels = *((const uint32_t*)p); p += 4; uint32_t xTwips = *((const uint32_t*)p); p += 4; uint32_t yTwips = *((const uint32_t*)p); p += 4; uint32_t bitsPerPixel = *((const uint32_t*)p); p += 4; uint32_t unknown1 = *((const uint32_t*)p); p += 4; uint32_t unknown2 = *((const uint32_t*)p); p += 4; uint32_t RLEflag = *((const uint32_t*)p); p += 4; width = xPixels; height = yPixels; uint32_t picsize = xPixels * yPixels; uint32_t linelen; int pixelsPerByte = (8 / bitsPerPixel); int nColors = 1 << bitsPerPixel; int grayVal = 255 / (nColors - 1); int bytesPerLine = (xPixels + pixelsPerByte - 1) / pixelsPerByte; int mask = (bitsPerPixel << 1) - 1; int oidx = 0; int x = 0; int y = 0; int offset = 0; if (RLEflag) { int i = 0; while (offset < datlen) { unsigned char b = *(p + offset); if (b >= 0x80) { offset += 0x100 - b + 1; i += 0x100 - b; } else { offset += 2; i += b + 1; } } linelen = i / yPixels; offset = 0; while (offset < datlen) { unsigned char b = *(p + offset++); if (b >= 0x80) { for (i = 0; i < 0x100 - b; i++, offset++) { if (offset >= datlen) return false; // data corrupted unsigned char b2 = *(p + offset); splitByte(b2); } } else { if (offset >= datlen) return false; else { unsigned char b2 = *(p + offset); unsigned char bs = b2; for (i = 0; i <= b; i++) { splitByte(b2); b2 = bs; } } offset++; } } } else { linelen = datlen / yPixels; while (offset < datlen) { unsigned char b = *(p + offset++); splitByte(b); } } return true; } plptools-1.0.26/lib/psibitmap.h000066400000000000000000000047371504470754400164100ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999-2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #ifndef _PSIBITMAP_H_ #define _PSIBITMAP_H_ #include /** * This function is used by encodeBitmap for retrieving image data. * It must return a gray value between 0 and 255 where 0 is black and * 255 is white. * * @param x The x coordinate of the pixel to get (0 = left) * @param y The y coordinate of the pixel to get (0 = top) */ typedef int (*getPixelFunction_t)(int x, int y); /** * Convert an image into a bitmap in Psion format. * * @param width The width of the image to convert. * @param height The height of the image to convert. * @param getPixel Pointer to a function for retrieving pixel values. * @param rle Flag: Perform RLE compression (currently ignored). * @param out Output buffer; gets filled with the Psion representation * of the converted image. */ extern void encodeBitmap(int width, int height, getPixelFunction_t getPixel, bool rle, bufferStore &out); /** * Convert a Psion bitmap to a 8bit/pixel grayscale image. * * @param p Pointer to an input buffer which contains the Psion-formatted * bitmap to convert. Must start with a Psion bitmap header. * @param width On return, the image width in pixels is returned here. * @param height On return, the image height in pixels is returned here. * @param out Buffer which gets filled with the raw image data. Each pixel * is represented by a byte. The image data is organized in * height scanlines of width bytes, starting with the topmost * scanline. * * @returns true on success, false if input data is inconsistent. */ extern bool decodeBitmap(const unsigned char *p, int &width, int &height, bufferStore &out); #endif // !_PSIBITMAP_H_ plptools-1.0.26/lib/psiprocess.cc000066400000000000000000000042151504470754400167370ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999-2002 Fritz Elfert * * 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 * along with this program; if not, see . * */ #include "config.h" #include "psiprocess.h" #include #include using namespace std; PsiProcess::PsiProcess() : pid(0), name(""), args(""), s5mx(false) { } PsiProcess::PsiProcess(const PsiProcess &p) { pid = p.pid; name = p.name; args = p.args; s5mx = p.s5mx; } PsiProcess::PsiProcess(int _pid, const char * const _name, const char * const _args, bool _s5mx) { pid = _pid; name = _name; args = _args; s5mx = _s5mx; } int PsiProcess:: getPID() { return pid; } const char *PsiProcess:: getName() { return name.c_str(); } const char *PsiProcess:: getArgs() { return args.c_str(); } const char *PsiProcess:: getProcId() { ostringstream tmp; if (s5mx) tmp << name << ".$" << setw(2) << setfill('0') << pid << '\0'; else tmp << name << ".$" << pid << '\0'; return tmp.str().c_str(); } void PsiProcess:: setArgs(string _args) { args = _args; } PsiProcess &PsiProcess:: operator=(const PsiProcess &p) { pid = p.pid; name = p.name; args = p.args; s5mx = p.s5mx; return *this; } ostream & operator<<(ostream &o, const PsiProcess &p) { ostream::fmtflags old = o.flags(); o << dec << setw(5) << setfill(' ') << p.pid << " " << setw(12) << setfill(' ') << setiosflags(ios::left) << p.name.c_str() << resetiosflags(ios::left) << " " << p.args; o.flags(old); return o; } plptools-1.0.26/lib/psiprocess.h000066400000000000000000000052161504470754400166030ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999-2002 Fritz Elfert * * 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 * along with this program; if not, see . * */ #ifndef _PSIPROCESS_H_ #define _PSIPROCESS_H_ #include #include class rpcs; /** * A class, describing a Process on the Psion. * Objects of this type are used by @ref rpcs::queryPrograms * for returning the currently running processes. * * @author Fritz Elfert */ class PsiProcess { public: /** * Default constructor */ PsiProcess(); /** * A copy constructor. * Mainly used by STL container classes. * * @param p The object to be used as initializer. */ PsiProcess(const PsiProcess &p); /** * Initializing Constructor */ PsiProcess(const int, const char * const, const char * const, bool); /** * Default destructor. */ ~PsiProcess() {}; /** * Retrieves the PID of a process. * * @returns The PID of this instance. */ int getPID(); /** * Retrieve the file name of a process. * * @returns The name of this instance. */ const char *getName(); /** * Retrieve the file name of a process. * * @returns The arguments of this instance. */ const char *getArgs(); /** * Retrieve the file name and PID of a process. * * @returns The name and PID this instance in the format * name.$pid . */ const char *getProcId(); /** * Assignment operator * Mainly used by STL container classes. * * @param p The new value to assign. * * @returns The modified object. */ PsiProcess &operator=(const PsiProcess &p); /** * Prints the object contents. * The output is in human readable similar to the * output of a "ls" command. */ friend std::ostream &operator<<(std::ostream &o, const PsiProcess &p); private: friend class rpcs; void setArgs(std::string _args); int pid; std::string name; std::string args; bool s5mx; }; #endif plptools-1.0.26/lib/psitime.cc000066400000000000000000000145411504470754400162220ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 2000-2002 Fritz Elfert * * 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 * along with this program; if not, see . * */ #include "config.h" #include "psitime.h" #include #include #define OnePM 3600 // 13:00 offset for SIBO using namespace std; PsiTime::PsiTime(void) { ptzValid = false; tryPsiZone(); setUnixNow(); } PsiTime::PsiTime(time_t time) { ptzValid = false; gettimeofday(&utv, &utz); setUnixTime(time); } PsiTime::PsiTime(psi_timeval *_ptv, psi_timezone *_ptz) { if (_ptv != 0L) ptv = *_ptv; if (_ptz != 0L) { ptz = *_ptz; ptzValid = true; } else { ptzValid = false; tryPsiZone(); } /* get our own timezone */ gettimeofday(&utv, &utz); psi2unix(); } PsiTime::PsiTime(const uint32_t _ptvHi, const uint32_t _ptvLo) { ptv.tv_high = _ptvHi; ptv.tv_low = _ptvLo; ptzValid = false; tryPsiZone(); /* get our own timezone */ gettimeofday(&utv, &utz); psi2unix(); } PsiTime::PsiTime(struct timeval *_utv, struct timezone *_utz) { if (_utv != 0L) utv = *_utv; if (_utz != 0L) utz = *_utz; tryPsiZone(); unix2psi(); } PsiTime::PsiTime(const PsiTime &t) { utv = t.utv; utz = t.utz; ptv = t.ptv; ptz = t.ptz; ptzValid = t.ptzValid; tryPsiZone(); } PsiTime::~PsiTime() { tryPsiZone(); } void PsiTime::setUnixTime(struct timeval *_utv) { if (_utv != 0L) utv = *_utv; unix2psi(); } void PsiTime::setUnixTime(time_t time) { utv.tv_sec = time; utv.tv_usec = 0; unix2psi(); } void PsiTime::setUnixNow(void) { gettimeofday(&utv, &utz); unix2psi(); } void PsiTime::setPsiTime(psi_timeval *_ptv) { if (_ptv != 0L) ptv = *_ptv; psi2unix(); } void PsiTime::setPsiTime(const uint32_t _ptvHi, const uint32_t _ptvLo) { ptv.tv_high = _ptvHi; ptv.tv_low = _ptvLo; psi2unix(); } void PsiTime::setPsiZone(psi_timezone *_ptz) { if (_ptz != 0L) { ptz = *_ptz; ptzValid = true; } psi2unix(); } struct timeval &PsiTime::getTimeval(void) { return utv; } time_t PsiTime::getTime(void) { return utv.tv_sec; } psi_timeval &PsiTime::getPsiTimeval(void) { return ptv; } uint32_t PsiTime::getPsiTimeLo(void) { return ptv.tv_low; } uint32_t PsiTime::getPsiTimeHi(void) { return ptv.tv_high; } PsiTime &PsiTime::operator=(const PsiTime &t) { utv = t.utv; utz = t.utz; ptv = t.ptv; ptz = t.ptz; ptzValid = t.ptzValid; tryPsiZone(); return *this; } bool PsiTime::operator==(const PsiTime &t) { psi2unix(); return ((utv.tv_sec == t.utv.tv_sec) && (utv.tv_usec == t.utv.tv_usec)); } bool PsiTime::operator<(const PsiTime &t) { psi2unix(); if (utv.tv_sec == t.utv.tv_sec) return (utv.tv_usec < t.utv.tv_usec); else return (utv.tv_sec < t.utv.tv_sec); } bool PsiTime::operator>(const PsiTime &t) { psi2unix(); if (utv.tv_sec == t.utv.tv_sec) return (utv.tv_usec > t.utv.tv_usec); else return (utv.tv_sec > t.utv.tv_sec); } ostream &operator<<(ostream &s, const PsiTime &t) { const char *fmt = "%c"; char buf[100]; strftime(buf, sizeof(buf), fmt, localtime(&t.utv.tv_sec)); s << buf; return s; } /** * The difference between * EPOC epoch (01.01.0001 00:00:00) * and Unix epoch (01.01.1970 00:00:00) * in microseconds. */ #define EPOCH_DIFF 0x00dcddb30f2f8000ULL /* evalOffset() * Returns the difference between the Psion's timezone and the PC's timezone, in * microseconds */ static long long evalOffset(psi_timezone ptz, time_t time, bool valid) { int64_t offset = 0; bool flg = false; if (valid) { offset = ptz.utc_offset; flg = true; } else { /** * Fallback. If no Psion zone given, use * environment variable PSI_TZ */ const char *offstr = getenv("PSI_TZ"); if (offstr != 0) { char *err = 0; offset = strtol(offstr, &err, 0); if (err != 0 && *err != '\0') { offset = 0; } } else { flg = true; } } // If all else fails, we assume that PC Timezone == Psion Timezone; // offset should still be 0 at this point. if (flg) { struct tm *tm = localtime(&time); offset -= tm->tm_gmtoff; // Subtract out local timezone offset += 3600 * tm->tm_isdst * 2; offset *= 1000000; // Turn it into microseconds } return offset; } void PsiTime::setSiboTime(uint32_t stime) { long long micro = evalOffset(ptz, time(0), false); micro /= 1000000; utv.tv_sec = stime + OnePM + micro; utv.tv_usec = 0; // unix2psi(); } uint32_t PsiTime::getSiboTime(void) { long long micro = evalOffset(ptz, time(0), false); micro /= 1000000; return utv.tv_sec - OnePM - micro; } void PsiTime::psi2unix(void) { uint64_t micro = ptv.tv_high; micro = (micro << 32) | ptv.tv_low; /* Substract Psion's idea of UTC offset */ micro -= EPOCH_DIFF; micro -= evalOffset(ptz, micro / 1000000, ptzValid); utv.tv_sec = micro / 1000000; utv.tv_usec = micro % 1000000; } void PsiTime::unix2psi(void) { uint64_t micro = (uint64_t)utv.tv_sec * 1000000ULL + utv.tv_usec; /* Add Psion's idea of UTC offset */ micro += evalOffset(ptz, utv.tv_sec, ptzValid); micro += EPOCH_DIFF; ptv.tv_low = micro & 0x0ffffffff; ptv.tv_high = (micro >> 32) & 0x0ffffffff; } void PsiTime::tryPsiZone() { if (ptzValid) return; if (PsiZone::getInstance().getZone(ptz)) ptzValid = true; } PsiZone *PsiZone::_instance = 0L; PsiZone &PsiZone:: getInstance() { if (_instance == 0L) _instance = new PsiZone(); return *_instance; } PsiZone::PsiZone() { _ptzValid = false; } void PsiZone:: setZone(psi_timezone &ptz) { _ptz = ptz; _ptzValid = true; } bool PsiZone:: getZone(psi_timezone &ptz) { if (_ptzValid) ptz = _ptz; return _ptzValid; } plptools-1.0.26/lib/psitime.h000066400000000000000000000217141504470754400160640ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 2000-2002 Fritz Elfert * * 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 * along with this program; if not, see . * */ #ifndef _PSITIME_H_ #define _PSITIME_H_ #include "config.h" #include #include #include #include #include #include #include /** * Holds a Psion time value. * Psion time values are 64 bit * integers describing the time * since 01.01.0001 in microseconds. */ typedef struct psi_timeval_t { /** * Prints a psi_timeval in human readable format. */ friend std::ostream &operator<<(std::ostream &o, const psi_timeval_t &ptv) { std::ostream::fmtflags old = o.flags(); uint64_t micro = ptv.tv_high; micro = (micro << 32) | ptv.tv_low; micro /= 1000000; int s = micro % 60; micro /= 60; int m = micro % 60; micro /= 60; int h = micro % 24; micro /= 24; int d = micro % 365; micro /= 365; int y = micro; o << std::dec; if (y > 0) o << y << ((y > 1) ? _(" years ") : _(" year ")); if (d > 0) o << d << ((d > 1) ? _(" days ") : _(" day ")); if (h > 0) o << h << ((h != 1) ? _(" hours ") : _(" hour ")); if (m > 0) o << m << ((m != 1) ? _(" minutes ") : _(" minute ")); o << s << ((s != 1) ? _(" seconds") : _(" second")); o.flags(old); return o; } /** * The lower 32 bits */ uint32_t tv_low; /** * The upper 32 bits */ uint32_t tv_high; } psi_timeval; /** * holds a Psion time zone description. */ typedef struct psi_timezone_t { friend std::ostream &operator<<(std::ostream &s, const psi_timezone_t &ptz) { std::ostream::fmtflags old = s.flags(); int h = ptz.utc_offset / 3600; int m = ptz.utc_offset % 3600; s << "offs: " << std::dec << h << "h"; if (m != 0) s << ", " << m << "m"; s.flags(old); return s; } signed long utc_offset; unsigned long dst_zones; unsigned long home_zone; } psi_timezone; /** * Psion time related utility class. * * PsiTime provides easy access to the time format, used * when communicating with a Psion. Internally, the time * is always normalized to GMT. The time value can be set * and retrieved in both Unix and Psion formats. This * allows easy conversion between both formats. * NOTE: For proper conversion, the current timezone of * the Psion has to be set. For EPOC devices, the * timezone can be evaluated using * @ref rpcs::getMachineInfo . For SIBO devices, * unfortunately there is no known method of retrieving * this information. Therefore, if the timezone is * not set, a fallback using the environment * variable PSI_TZ is provided. Users should * set this variable to the offset of their time zone * in seconds. If PSI_TZ is net set, a second * fallback uses the local machine's setup, which assumes * that both Psion and local machine have the same * time zone and daylight settings. * * @author Fritz Elfert */ class PsiTime { public: /** * Contructs a new instance. * * @param _utv A Unix time value for initialization. * @param _utz A Unix timezone for initialization. */ PsiTime(struct timeval *_utv, struct timezone *_utz = nullptr); /** * Contructs a new instance. * * @param time A Unix time value for initialization. */ PsiTime(time_t time); /** * Contructs a new instance. * * @param _ptv A Psion time value for initialization. * @param _ptz A Psion timezone for initialization. */ PsiTime(psi_timeval *_ptv, psi_timezone *_ptz = nullptr); /** * Contructs a new instance. * * @param _ptvHi The high 32 bits of a Psion time value for initialization. * @param _ptvLo The low 32 bits of a Psion time value for initialization. */ PsiTime(const uint32_t _ptvHi, const uint32_t _ptvLo); /** * Constructs a new instance, initializing to now. */ PsiTime(void); /** * A copy-constructor */ PsiTime(const PsiTime &t); /** * Destroys the instance. */ ~PsiTime(); /** * Modifies the value of this instance. * * @param _ptv The new Psion time representation. */ void setPsiTime(psi_timeval *_ptv); /** * Modifies the value of this instance. * * @param stime The new SIBO time representation. */ void setSiboTime(uint32_t stime); /** * Modifies the value of this instance. * * @param _ptvHi The high 32 bits of a Psion time. * @param _ptvLo The low 32 bits of a Psion time. */ void setPsiTime(const uint32_t _ptvHi, const uint32_t _ptvLo); /** * Sets the Psion time zone of this instance. * * @param _ptz The new Psion time zone. */ void setPsiZone(psi_timezone *_ptz); /** * Sets the value of this instance. * * @param _utv The new Unix time representation. */ void setUnixTime(struct timeval *_utv); /** * Sets the value of this instance. * * @param _utv The new Unix time representation. */ void setUnixTime(time_t time); /** * Sets the value of this instance to the * current time of the Unix machine. */ void setUnixNow(void); /** * Retrieves the instance's current value * in Unix time format. * * @returns The instance's current time as Unix struct timeval. */ struct timeval &getTimeval(void); /** * Retrieves the instance's current value * in Unix time format. * * @returns The instance's current time as Unix time_t. */ time_t getTime(void); /** * Retrieves the instance's current value * in SIBO time format. * * @returns The instance's current time as SIBO time. */ uint32_t getSiboTime(); /** * Retrieves the instance's current value * in Psion time format. * * @returns The instance's current time a Psion struct psi_timeval_t. */ psi_timeval &getPsiTimeval(void); /** * Retrieves the instance's current value * in Psion time format, high 32 bits. * * @returns The instance's current time as lower 32 bits of * a Psion struct psi_timeval_t. */ uint32_t getPsiTimeLo(void); /** * Retrieves the instance's current value * in Psion time format, low 32 bits. * * @returns The instance's current time as upper 32 bits of * a Psion struct psi_timeval_t. */ uint32_t getPsiTimeHi(void); /** * Prints the instance's value in human readable format. * This function uses the current locale setting for * formatting the time. * * @param s The stream to be written. * @param t The instance whose value should be displayed. * * @returns The stream. */ friend std::ostream &operator<<(std::ostream &s, const PsiTime &t); /** * Assignment operator */ PsiTime &operator=(const PsiTime &t); /** * Comparison operators */ bool operator==(const PsiTime &t); bool operator<(const PsiTime &t); bool operator>(const PsiTime &t); enum zone { PSI_TZ_NONE = 0, PSI_TZ_EUROPEAN = 1, PSI_TZ_NORTHERN = 2, PSI_TZ_SOUTHERN = 4, PSI_TZ_HOME = 0x40000000 }; private: void psi2unix(void); void unix2psi(void); void tryPsiZone(); psi_timeval ptv; psi_timezone ptz; struct timeval utv; struct timezone utz; bool ptzValid; }; /** * A singleton wrapper for a @ref psi_timezone . This class is used * by @ref PsiTime to initialize its psi_timezone variable. * PsiZone itself is initialized from within @ref rpcs::getMachineInfo . * In an application, you typically call this at the very beginning, just * after connection setup. From then on, a single PsiZone instance is * held in memory and used by the various constructors of PsiTime. * * @author Fritz Elfert */ class PsiZone { friend class rpcs32; public: /** * Retrieve the singleton object. * If it does not exist, it is created. */ static PsiZone &getInstance(); /** * Retrieve the Psion time zone. * * @param ptz The time zone is returned here. * * @returns false, if the time zone is not * known (yet). */ bool getZone(psi_timezone &ptz); private: /** * This objects instance (singleton) */ static PsiZone *_instance; /** * Private constructor. */ PsiZone(); void setZone(psi_timezone &ptz); bool _ptzValid; psi_timezone _ptz; }; #endif plptools-1.0.26/lib/rclip.cc000066400000000000000000000076161504470754400156660ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999-2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #include "config.h" #include "rclip.h" #include "bufferstore.h" #include "ppsocket.h" #include "bufferarray.h" #include "Enum.h" #include #include rclip::rclip(ppsocket * _skt) { skt = _skt; reset(); } rclip::~rclip() { skt->closeSocket(); } // // public common API // void rclip:: reconnect(void) { //skt->closeSocket(); skt->reconnect(); reset(); } void rclip:: reset(void) { bufferStore a; status = rfsv::E_PSI_FILE_DISC; a.addStringT(getConnectName()); if (skt->sendBufferStore(a)) { if (skt->getBufferStore(a) == 1) { if (!strcmp(a.getString(0), "NAK")) status = rfsv::E_PSI_GEN_NSUP; if (!strcmp(a.getString(0), "Ok")) status = rfsv::E_PSI_GEN_NONE; } } } Enum rclip:: getStatus(void) { return status; } const char *rclip:: getConnectName(void) { return "CLIPSVR.RSY"; } // // protected internals // bool rclip:: sendCommand(enum commands cc) { if (status == rfsv::E_PSI_FILE_DISC) { reconnect(); if (status == rfsv::E_PSI_FILE_DISC) return false; } if (status != rfsv::E_PSI_GEN_NONE) return false; bool result; bufferStore a; a.addByte(cc); switch (cc) { case RCLIP_INIT: a.addWord(0x100); break; case RCLIP_NOTIFY: a.addByte(0); } result = skt->sendBufferStore(a); if (!result) { reconnect(); result = skt->sendBufferStore(a); if (!result) status = rfsv::E_PSI_FILE_DISC; } return result; } Enum rclip:: sendListen() { if (sendCommand(RCLIP_LISTEN)) return rfsv::E_PSI_GEN_NONE; else return status; } Enum rclip:: checkNotify() { Enum ret; bufferStore a; int r = skt->getBufferStore(a, false); if (r < 0) { ret = status = rfsv::E_PSI_FILE_DISC; } else { if (r == 0) ret = rfsv::E_PSI_FILE_EOF; else { if ((a.getLen() != 1) || (a.getByte(0) != 0)) ret = rfsv::E_PSI_GEN_FAIL; } } return ret; } Enum rclip:: waitNotify() { Enum ret; bufferStore a; sendCommand(RCLIP_LISTEN); if ((ret = getResponse(a)) == rfsv::E_PSI_GEN_NONE) { if ((a.getLen() != 1) || (a.getByte(0) != 0)) ret = rfsv::E_PSI_GEN_FAIL; } return ret; } Enum rclip:: notify() { Enum ret; bufferStore a; sendCommand(RCLIP_NOTIFY); if ((ret = getResponse(a)) == rfsv::E_PSI_GEN_NONE) { if ((a.getLen() != 1) || (a.getByte(0) != RCLIP_NOTIFY)) ret = rfsv::E_PSI_GEN_FAIL; } return ret; } Enum rclip:: initClipbd() { Enum ret; bufferStore a; if (status != rfsv::E_PSI_GEN_NONE) return status; sendCommand(RCLIP_INIT); if ((ret = getResponse(a)) == rfsv::E_PSI_GEN_NONE) { if ((a.getLen() != 3) || (a.getByte(0) != RCLIP_INIT) || (a.getWord(1) != 0x100)) ret = rfsv::E_PSI_GEN_FAIL; } return ret; } Enum rclip:: getResponse(bufferStore & data) { Enum ret = rfsv::E_PSI_GEN_NONE; if (status == rfsv::E_PSI_GEN_NSUP) return status; if (skt->getBufferStore(data) == 1) return ret; else status = rfsv::E_PSI_FILE_DISC; return status; } plptools-1.0.26/lib/rclip.h000066400000000000000000000113061504470754400155170ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999-2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #ifndef _RCLIP_H_ #define _RCLIP_H_ #include #include class ppsocket; class bufferStore; class bufferArray; /** * Remote ClipBoard services via PLP * * This class implements access to the remote clipboard notification * feature of the Psion. The Psion uses a file C:\System\Data\Clpboard.cbd * for storing the content of its clipboard. This file can be accessed like * any other regular file on the Psion using the @ref rfsv implementation. * This class handles notification about changes of this file. * There are two methods of notification implemented. Using @ref waitNotify , * a blocking method can be used and using @ref sendListen followed by * @ref checkNotify , a polling approach (usable for GUI programs) can * be implemented. */ class rclip { public: /** * Constructs a new rclip object. * * @param skt The socket to be used by this object. */ rclip(ppsocket *skt); /** * destructor. */ ~rclip(); /** * Initializes a connection to the remote * machine. */ void reset(); /** * Attempts to re-establish a remote * connection by first closing the socket, * then connecting again to the ncpd daemon * and finally calling @ref reset. */ void reconnect(); /** * Retrieves the current status of the * connection. * * @returns The connection status. */ Enum getStatus(); /** * Send initialization frame. * * Must be called once after a new rclip object has * be called. It sends an initialzation frame to the * Psion's server and returns its status. * * @returns The connection status. */ Enum initClipbd(); /** * Send listen request. * * Calling this method arms the Psion's clipboard server. * After that, every change of the Psion's clipboard file * will be signaled. To poll the signal, subsequent calls * to @ref checkNotify should be made. * * @returns The connection status. */ Enum sendListen(); /** * Check for clipboard notification. * * If the Psion has sent a notification, this method returns * @ref rfsv::E_PSI_GEN_NONE . If there is no notification * pending, this method returns @ref rfsv::E_PSI_FILE_EOF * All other return values are to be treated as errors * * @returns The connection status. */ Enum checkNotify(); /** * Send listen request and wait for notification. * * This method is the blocking version of the two above methods. * It first sends a listen request and then blocks until a * notification has sent by the Psion or an error occured. * * @returns The connection status, rfsv::E_PSI_GEN_NONE if a * notification has been received. */ Enum waitNotify(); /** * Send a notification to the Psion. * * If the application wishes to notify the Psion after changing the * clipboard file, this method can be used. * * @returns The connection status. */ Enum notify(); protected: /** * The possible commands. */ enum commands { RCLIP_INIT = 0x00, RCLIP_NOTIFY = 0x08, RCLIP_LISTEN = 0x04 }; /** * The socket, used for communication * with ncpd. */ ppsocket *skt; /** * The current status of the connection. */ Enum status; /** * Sends a command to the remote side. * * If communication fails, a reconnect is triggered * and a second attempt to transmit the request * is attempted. If that second attempt fails, * the function returns an error an sets rpcs::status * to E_PSI_FILE_DISC. * * @param cc The command to execute on the remote side. * * @returns true on success, false on failure. */ bool sendCommand(enum commands cc); Enum getResponse(bufferStore &data); const char *getConnectName(); }; #endif plptools-1.0.26/lib/rfsv.cc000066400000000000000000000177401504470754400155340ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999 Philip Proudman * Copyright (C) 1999 Matt J. Gumbley * Copyright (C) 1999-2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #include "config.h" #include "rfsv.h" #include "ppsocket.h" #include "bufferstore.h" #include "Enum.h" using namespace std; ENUM_DEFINITION_BEGIN(rfsv::errs, rfsv::E_PSI_GEN_NONE) stringRep.add(rfsv::E_PSI_GEN_NONE, N_("no error")); stringRep.add(rfsv::E_PSI_GEN_FAIL, N_("general")); stringRep.add(rfsv::E_PSI_GEN_ARG, N_("bad argument")); stringRep.add(rfsv::E_PSI_GEN_OS, N_("OS error")); stringRep.add(rfsv::E_PSI_GEN_NSUP, N_("not supported")); stringRep.add(rfsv::E_PSI_GEN_UNDER, N_("numeric underflow")); stringRep.add(rfsv::E_PSI_GEN_OVER, N_("numeric overflow")); stringRep.add(rfsv::E_PSI_GEN_RANGE, N_("numeric exception")); stringRep.add(rfsv::E_PSI_GEN_INUSE, N_("in use")); stringRep.add(rfsv::E_PSI_GEN_NOMEMORY, N_("out of memory")); stringRep.add(rfsv::E_PSI_GEN_NOSEGMENTS, N_("out of segments")); stringRep.add(rfsv::E_PSI_GEN_NOSEM, N_("out of semaphores")); stringRep.add(rfsv::E_PSI_GEN_NOPROC, N_("out of processes")); stringRep.add(rfsv::E_PSI_GEN_OPEN, N_("already open")); stringRep.add(rfsv::E_PSI_GEN_NOTOPEN, N_("not open")); stringRep.add(rfsv::E_PSI_GEN_IMAGE, N_("bad image")); stringRep.add(rfsv::E_PSI_GEN_RECEIVER, N_("receiver error")); stringRep.add(rfsv::E_PSI_GEN_DEVICE, N_("device error")); stringRep.add(rfsv::E_PSI_GEN_FSYS, N_("no filesystem")); stringRep.add(rfsv::E_PSI_GEN_START, N_("not ready")); stringRep.add(rfsv::E_PSI_GEN_NOFONT, N_("no font")); stringRep.add(rfsv::E_PSI_GEN_TOOWIDE, N_("too wide")); stringRep.add(rfsv::E_PSI_GEN_TOOMANY, N_("too many")); stringRep.add(rfsv::E_PSI_FILE_EXIST, N_("file already exists")); stringRep.add(rfsv::E_PSI_FILE_NXIST, N_("no such file")); stringRep.add(rfsv::E_PSI_FILE_WRITE, N_("write error")); stringRep.add(rfsv::E_PSI_FILE_READ, N_("read error")); stringRep.add(rfsv::E_PSI_FILE_EOF, N_("end of file")); stringRep.add(rfsv::E_PSI_FILE_FULL, N_("disk/serial read buffer full")); stringRep.add(rfsv::E_PSI_FILE_NAME, N_("invalid name")); stringRep.add(rfsv::E_PSI_FILE_ACCESS, N_("access denied")); stringRep.add(rfsv::E_PSI_FILE_LOCKED, N_("resource locked")); stringRep.add(rfsv::E_PSI_FILE_DEVICE, N_("no such device")); stringRep.add(rfsv::E_PSI_FILE_DIR, N_("no such directory")); stringRep.add(rfsv::E_PSI_FILE_RECORD, N_("no such record")); stringRep.add(rfsv::E_PSI_FILE_RDONLY, N_("file is read-only")); stringRep.add(rfsv::E_PSI_FILE_INV, N_("invalid I/O operation")); stringRep.add(rfsv::E_PSI_FILE_PENDING, N_("I/O pending (not yet completed)")); stringRep.add(rfsv::E_PSI_FILE_VOLUME, N_("invalid volume name")); stringRep.add(rfsv::E_PSI_FILE_CANCEL, N_("cancelled")); stringRep.add(rfsv::E_PSI_FILE_ALLOC, N_("no memory for control block")); stringRep.add(rfsv::E_PSI_FILE_DISC, N_("unit disconnected")); stringRep.add(rfsv::E_PSI_FILE_CONNECT, N_("already connected")); stringRep.add(rfsv::E_PSI_FILE_RETRAN, N_("retransmission threshold exceeded")); stringRep.add(rfsv::E_PSI_FILE_LINE, N_("physical link failure")); stringRep.add(rfsv::E_PSI_FILE_INACT, N_("inactivity timer expired")); stringRep.add(rfsv::E_PSI_FILE_PARITY, N_("serial parity error")); stringRep.add(rfsv::E_PSI_FILE_FRAME, N_("serial framing error")); stringRep.add(rfsv::E_PSI_FILE_OVERRUN, N_("serial overrun error")); stringRep.add(rfsv::E_PSI_MDM_CONFAIL, N_("modem cannot connect to remote modem")); stringRep.add(rfsv::E_PSI_MDM_BUSY, N_("remote modem busy")); stringRep.add(rfsv::E_PSI_MDM_NOANS, N_("remote modem did not answer")); stringRep.add(rfsv::E_PSI_MDM_BLACKLIST, N_("number blacklisted by the modem")); stringRep.add(rfsv::E_PSI_FILE_NOTREADY, N_("drive not ready")); stringRep.add(rfsv::E_PSI_FILE_UNKNOWN, N_("unknown media")); stringRep.add(rfsv::E_PSI_FILE_DIRFULL, N_("directory full")); stringRep.add(rfsv::E_PSI_FILE_PROTECT, N_("write-protected")); stringRep.add(rfsv::E_PSI_FILE_CORRUPT, N_("media corrupt")); stringRep.add(rfsv::E_PSI_FILE_ABORT, N_("aborted operation")); stringRep.add(rfsv::E_PSI_FILE_ERASE, N_("failed to erase flash media")); stringRep.add(rfsv::E_PSI_FILE_INVALID, N_("invalid file for DBF system")); stringRep.add(rfsv::E_PSI_GEN_POWER, N_("power failure")); stringRep.add(rfsv::E_PSI_FILE_TOOBIG, N_("too big")); stringRep.add(rfsv::E_PSI_GEN_DESCR, N_("bad descriptor")); stringRep.add(rfsv::E_PSI_GEN_LIB, N_("bad entry point")); stringRep.add(rfsv::E_PSI_FILE_NDISC, N_("could not diconnect")); stringRep.add(rfsv::E_PSI_FILE_DRIVER, N_("bad driver")); stringRep.add(rfsv::E_PSI_FILE_COMPLETION, N_("operation not completed")); stringRep.add(rfsv::E_PSI_GEN_BUSY, N_("server busy")); stringRep.add(rfsv::E_PSI_GEN_TERMINATED, N_("terminated")); stringRep.add(rfsv::E_PSI_GEN_DIED, N_("died")); stringRep.add(rfsv::E_PSI_FILE_HANDLE, N_("bad handle")); stringRep.add(rfsv::E_PSI_NOT_SIBO, N_("invalid operation for RFSV16")); stringRep.add(rfsv::E_PSI_INTERNAL, N_("libplp internal error")); ENUM_DEFINITION_END(rfsv::errs) const char *rfsv::getConnectName(void) { return "SYS$RFSV"; } rfsv::~rfsv() { skt->closeSocket(); } void rfsv::reconnect(void) { skt->reconnect(); serNum = 0; reset(); } void rfsv::reset(void) { bufferStore a; status = E_PSI_FILE_DISC; a.addStringT(getConnectName()); if (skt->sendBufferStore(a)) { if (skt->getBufferStore(a) == 1) { if (!strcmp(a.getString(0), "Ok")) status = E_PSI_GEN_NONE; } } } Enum rfsv::getStatus(void) { return status; } string rfsv:: convertSlash(const string &name) { string tmp = ""; for (const char *p = name.c_str(); *p; p++) tmp += (*p == '/') ? '\\' : *p; return tmp; } string rfsv:: attr2String(const uint32_t attr) { string tmp = ""; tmp += ((attr & PSI_A_DIR) ? 'd' : '-'); tmp += ((attr & PSI_A_READ) ? 'r' : '-'); tmp += ((attr & PSI_A_RDONLY) ? '-' : 'w'); tmp += ((attr & PSI_A_HIDDEN) ? 'h' : '-'); tmp += ((attr & PSI_A_SYSTEM) ? 's' : '-'); tmp += ((attr & PSI_A_ARCHIVE) ? 'a' : '-'); tmp += ((attr & PSI_A_VOLUME) ? 'v' : '-'); // EPOC tmp += ((attr & PSI_A_NORMAL) ? 'n' : '-'); tmp += ((attr & PSI_A_TEMP) ? 't' : '-'); tmp += ((attr & PSI_A_COMPRESSED) ? 'c' : '-'); // SIBO tmp[7] = ((attr & PSI_A_EXEC) ? 'x' : tmp[7]); tmp[8] = ((attr & PSI_A_STREAM) ? 'b' : tmp[8]); tmp[9] = ((attr & PSI_A_TEXT) ? 't' : tmp[9]); return tmp; } int rfsv:: getSpeed() { bufferStore a; a.addStringT("NCP$GSPD"); if (!skt->sendBufferStore(a)) return -1; if (skt->getBufferStore(a) != 1) return -1; if (a.getLen() != 5) return -1; if (a.getByte(0) != E_PSI_GEN_NONE) return -1; return a.getDWord(1); } plptools-1.0.26/lib/rfsv.h000066400000000000000000000513131504470754400153700ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999 Philip Proudman * Copyright (C) 1999-2002 Fritz Elfert * * 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 * along with this program; if not, see . * */ #ifndef _RFSV_H_ #define _RFSV_H_ #include #include #include #include #include typedef std::deque PlpDir; class ppsocket; class PlpDrive; inline const int RFSV_SENDLEN = 2000; /** * Defines the callback procedure for * progress indication of copy operations. */ typedef int (*cpCallback_t)(void *, uint32_t); class rfsv16; class rfsv32; /** * A helper class for storing * intermediate internal information in rfsv16 and * rfsv32 . * @internal */ class rfsvDirhandle { friend class rfsv16; friend class rfsv32; private: uint32_t h; bufferStore b; }; /** * Access remote file services of a Psion. * * rfsv provides an API for accessing file services * of a Psion connected via ncpd. This class defines the * interface and a small amount of common constants and * methods. The majority of implementation is provided * by @ref rfsv32 and @ref rfsv16 , which implement the * variations of the protocol for EPOC and SIBO respectively. * Usually, the class @ref rfsvfactory is used to instantiate * the correct variant depending on the remote machine, * currently connected. */ class rfsv { public: /** * The kown modes for seek. */ enum seek_mode { PSI_SEEK_SET = 1, PSI_SEEK_CUR = 2, PSI_SEEK_END = 3 }; /** * The known modes for file open. */ enum open_flags { PSI_O_RDONLY = 0000, PSI_O_WRONLY = 0001, PSI_O_RDWR = 0002 }; /** * The known modes for file creation. */ enum open_mode { PSI_O_CREAT = 00100, PSI_O_EXCL = 00200, PSI_O_TRUNC = 01000, PSI_O_APPEND = 02000, PSI_O_SHARE = 04000 }; /** * The known error codes. */ enum errs { E_PSI_GEN_NONE = 0, E_PSI_GEN_FAIL = -1, E_PSI_GEN_ARG = -2, E_PSI_GEN_OS = -3, E_PSI_GEN_NSUP = -4, E_PSI_GEN_UNDER = -5, E_PSI_GEN_OVER = -6, E_PSI_GEN_RANGE = -7, E_PSI_GEN_DIVIDE = -8, E_PSI_GEN_INUSE = -9, E_PSI_GEN_NOMEMORY = - 10, E_PSI_GEN_NOSEGMENTS = -11, E_PSI_GEN_NOSEM = -12, E_PSI_GEN_NOPROC = -13, E_PSI_GEN_OPEN = -14, E_PSI_GEN_NOTOPEN = -15, E_PSI_GEN_IMAGE = -16, E_PSI_GEN_RECEIVER = -17, E_PSI_GEN_DEVICE = -18, E_PSI_GEN_FSYS = -19, E_PSI_GEN_START = -20, E_PSI_GEN_NOFONT = -21, E_PSI_GEN_TOOWIDE = -22, E_PSI_GEN_TOOMANY = -23, E_PSI_FILE_EXIST = -32, E_PSI_FILE_NXIST = -33, E_PSI_FILE_WRITE = -34, E_PSI_FILE_READ = -35, E_PSI_FILE_EOF = -36, E_PSI_FILE_FULL = -37, E_PSI_FILE_NAME = -38, E_PSI_FILE_ACCESS = -39, E_PSI_FILE_LOCKED = -40, E_PSI_FILE_DEVICE = -41, E_PSI_FILE_DIR = -42, E_PSI_FILE_RECORD = -43, E_PSI_FILE_RDONLY = -44, E_PSI_FILE_INV = -45, E_PSI_FILE_PENDING = -46, E_PSI_FILE_VOLUME = -47, E_PSI_FILE_CANCEL = -48, E_PSI_FILE_ALLOC = -49, E_PSI_FILE_DISC = -50, E_PSI_FILE_CONNECT = -51, E_PSI_FILE_RETRAN = -52, E_PSI_FILE_LINE = -53, E_PSI_FILE_INACT = -54, E_PSI_FILE_PARITY = -55, E_PSI_FILE_FRAME = -56, E_PSI_FILE_OVERRUN = -57, E_PSI_MDM_CONFAIL = -58, E_PSI_MDM_BUSY = -59, E_PSI_MDM_NOANS = -60, E_PSI_MDM_BLACKLIST = -61, E_PSI_FILE_NOTREADY = -62, E_PSI_FILE_UNKNOWN = -63, E_PSI_FILE_DIRFULL = -64, E_PSI_FILE_PROTECT = -65, E_PSI_FILE_CORRUPT = -66, E_PSI_FILE_ABORT = -67, E_PSI_FILE_ERASE = -68, E_PSI_FILE_INVALID = -69, E_PSI_GEN_POWER = -100, E_PSI_FILE_TOOBIG = -101, E_PSI_GEN_DESCR = -102, E_PSI_GEN_LIB = -103, E_PSI_FILE_NDISC = -104, E_PSI_FILE_DRIVER = -105, E_PSI_FILE_COMPLETION = -106, E_PSI_GEN_BUSY = -107, E_PSI_GEN_TERMINATED = -108, E_PSI_GEN_DIED = -109, E_PSI_FILE_HANDLE = -110, // Special error code for "Operation not permitted in RFSV16" E_PSI_NOT_SIBO = -200, // Special error code for "internal library error" E_PSI_INTERNAL = -201 }; /** * The known file attributes */ enum file_attribs { /** Attributes, valid on both EPOC and SIBO. */ PSI_A_RDONLY = 0x0001, PSI_A_HIDDEN = 0x0002, PSI_A_SYSTEM = 0x0004, PSI_A_DIR = 0x0008, PSI_A_ARCHIVE = 0x0010, PSI_A_VOLUME = 0x0020, /** Attributes, valid on EPOC only. */ PSI_A_NORMAL = 0x0040, PSI_A_TEMP = 0x0080, PSI_A_COMPRESSED = 0x0100, /** Attributes, valid on SIBO only. */ PSI_A_READ = 0x0200, PSI_A_EXEC = 0x0400, PSI_A_STREAM = 0x0800, PSI_A_TEXT = 0x1000 }; virtual ~rfsv(); void reset(); void reconnect(); /** * Retrieves the current connection status. * * @returns The status of the connection. */ Enum getStatus(); /** * Opens a file. * * @param attr The open mode. Use @ref opMode to convert a combination of @ref open_flags * and @ref open_mode to the machine-specific representation. * @param name The name of the file to open. * @param handle The handle for usage with @ref fread , * @ref fwrite , @ref fseek or @ref fclose is returned here. * * @returns A Psion error code (One of enum @ref #errs ). */ virtual Enum fopen(const uint32_t attr, const char * const name, uint32_t &handle) = 0; /** * Creates a unique temporary file. * The file is opened for reading and writing. * * @param handle The handle for usage with @ref fread , * @ref fwrite , @ref fseek or @ref fclose is returned here. * @param name The name of the temporary file is returned here. * * @returns A Psion error code (One of enum @ref #errs ). */ virtual Enum mktemp(uint32_t &handle, std::string &name) = 0; /** * Creates a named file. * * @param attr The open mode. Use @ref opMode to convert a combination of @ref open_flags * and @ref open_mode to the machine-specific representation. * @param name The name of the file to create. * @param handle The handle for usage with @ref fread , * @ref fwrite , @ref fseek or @ref fclose is returned here. * * @returns A Psion error code (One of enum @ref #errs ). */ virtual Enum fcreatefile(const uint32_t attr, const char * const name, uint32_t &handle) = 0; /** * Creates an named file, overwriting an existing file. * * @param attr The open mode. Use @ref opMode to convert a combination of @ref open_flags * and @ref open_mode to the machine-specific representation. * @param name The name of the file to create. * @param handle The handle for usage with @ref fread , * @ref fwrite , @ref fseek or @ref fclose is returned here. * * @returns A Psion error code (One of enum @ref #errs ). */ virtual Enum freplacefile(const uint32_t attr, const char * const name, uint32_t &handle) = 0; /** * Close a file on the Psion whih was previously opened/created by using * @ref fopen , @ref fcreatefile , @ref freplacefile or @ref mktemp . * * @param handle A valid file handle. */ virtual Enum fclose(const uint32_t handle) = 0; /** * Reads a directory on the Psion. * The returned STL deque of @ref PlpDirent contains all * requested directory entries. * * @param name The name of the directory * @param ret An STL deque of @ref PlpDirent entries. * * @returns A Psion error code (One of enum @ref rfsv::errs ). */ virtual Enum dir(const char * const name, PlpDir &ret) = 0; /** * Retrieves the modification time of a file on the Psion. * * @param name Name of the file. * @param mtime Modification time is returned here. * * @returns A Psion error code (One of enum @ref #errs ). */ virtual Enum fgetmtime(const char * const name, PsiTime &mtime) = 0; /** * Sets the modification time of a file on the Psion. * * @param name Name of the file whose modification time should be set. * @param mtime The desired modification time. * * @returns A Psion error code (One of enum @ref #errs ). */ virtual Enum fsetmtime(const char * const name, const PsiTime mtime) = 0; /** * Retrieves attributes of a file on the Psion. * * @param name Name of the file whose attributes ar to be retrieved. * @param attr The file's attributes are returned here. * * @returns A Psion error code (One of enum @ref #errs ). */ virtual Enum fgetattr(const char * const name, uint32_t &attr) = 0; /** * Checks to see if the directory component of a path or file name exists * and is valid. Returns `E_PSI_GEN_NONE` if is valid. * * Only paths up to the last trailing backslash are tested; trailing * filenames are ignored. For example, * * - "C:\\" tests the validity of "C:\\" * - "C:\\System" tests the validity of "C:\\" * - "C:\\System\\" tests the validity of "C:\\System\\" * - "D:\\Documents\\hello.txt" tests the validity of "D:\\Documents\\" * * This can be safely used to test for the existence of directories and * drives on EPOC16 and EPOC32; check for `E_PSI_FILE_NXIST`, * `E_PSI_FILE_DIR`, `E_PSI_FILE_DEVICE`, and `E_PSI_FILE_NOTREADY` to * determine non-existence. * * @returns A Psion error code (One of enum @ref #errs ). */ virtual Enum pathtest(const char * const name) = 0; /** * Retrieves attributes, size and modification time of a file on the Psion. * * @param name The name of the file. * @param e @ref PlpDirent object, filled with the information on return. * * @returns A Psion error code (One of enum @ref #errs ). */ virtual Enum fgeteattr(const char * const name, PlpDirent &e) =0; /** * @param name * * @returns A Psion error code (One of enum @ref #errs ). */ virtual Enum fsetattr(const char * const name, const uint32_t seta, const uint32_t unseta) = 0; /** * Counts number of entries in a directory. * * @param name The directory whose entries are to be counted. * @param count The number of entries is returned here. * * @returns A Psion error code (One of enum @ref #errs ). */ virtual Enum dircount(const char * const name, uint32_t &count) = 0; /** * Retrieves available drives on the Psion. * @p devbits On return, for every exiting drive, a bit is set in this * variable. The lowest bit represents drive A:. * * @returns A Psion error code (One of enum @ref #errs ). */ virtual Enum devlist(uint32_t &devbits) = 0; /** * Retrieves details about a drive. * * @param drive The drive character of the drive to get details from * (e.g: 'C', 'D' etc.). * (0 represents A:, 1 is B: and so on ...) * @param dinfo A @ref PlpDrive object which is filled with the drive's * information upon return. * * @returns A Psion error code (One of enum @ref #errs ). */ virtual Enum devinfo(const char drive, PlpDrive &dinfo) = 0; /** * Reads from a file on the Psion. * * @param handle Handle of the file to read from. * @param buffer The area where to store the data read. * @param len The number of bytes to read. * @param count The number of bytes actually read is returned here. * * @returns A Psion error code (One of enum @ref #errs ). */ virtual Enum fread(const uint32_t handle, unsigned char * const buffer, const uint32_t len, uint32_t &count) = 0; /** * Write to a file on the Psion. * * @param handle Handle of the file to read from. * @param buffer The area to be written. * @param len The number of bytes to write. * @param count The number of bytes actually written is returned here. * * @returns A Psion error code (One of enum @ref #errs ). */ virtual Enum fwrite(const uint32_t handle, const unsigned char * const buffer, const uint32_t len, uint32_t &count) = 0; /** * Copies a file from the Psion to the local machine. * * @param from Name of the file on the Psion to be copied. * @param to Name of the destination file on the local machine. * @param func Pointer to a function which gets called on every read. * This function can be used to show some progress etc. May be set * to NULL, where no callback is performed. If the callback function * returns 0, the operation is aborted and E_PSI_FILE_CANCEL is returned. * * @returns A Psion error code (One of enum @ref #errs ). */ virtual Enum copyFromPsion(const char *from, const char *to, void *, cpCallback_t func) = 0; /** * Copies a file from the Psion to the local machine. */ virtual Enum copyFromPsion(const char *from, int fd, cpCallback_t cb) = 0; /** * Copies a file from local machine to the Psion. * * @param from Name of the file on the local machine to be copied. * @param to Name of the destination file on the Psion. * @param func Pointer to a function which gets called on every read. * This function can be used to show some progress etc. May be set * to NULL, where no callback is performed. If the callback function * returns 0, the operation is aborted and E_PSI_FILE_CANCEL is returned. * * @returns A Psion error code (One of enum @ref #errs ). */ virtual Enum copyToPsion(const char * const from, const char * const to, void *, cpCallback_t func) = 0; /** * Copies a file from the Psion to the Psion. * On the EPOC variants, this runs much faster than reading * data from the Psion and then writing it back to the Psion, since * data transfer is handled locally on the Psion. * * @param from Name of the file to be copied. * @param to Name of the destination file. * @param func Pointer to a function which gets called on every read. * This function can be used to show some progress etc. May be set * to NULL, where no callback is performed. If the callback function * returns 0, the operation is aborted and E_PSI_FILE_CANCEL is returned. * * @returns A Psion error code (One of enum @ref #errs ). */ virtual Enum copyOnPsion(const char * const from, const char * const to, void *, cpCallback_t func) = 0; /** * Resizes an open file on the Psion. * If the new size is greater than the file's * current size, the contents of the added * data is undefined. If The new size is smaller, * the file is truncated. * * @param handle Handle of the file to be resized. * @param size New size for that file. * * @returns A Psion error code (One of enum @ref #errs ). */ virtual Enum fsetsize(const uint32_t handle, const uint32_t size) = 0; /** * Sets the current file position of a file on the Psion. * * @param handle The file handle. * @param offset Position to be seeked to. * @param mode The mode for seeking. * @param resultpos The final file position after seeking is returned here. * * @returns A Psion error code (One of enum @ref #errs ). */ virtual Enum fseek(const uint32_t handle, const int32_t offset, const uint32_t mode, uint32_t &resultpos) = 0; /** * Creates a directory on the Psion. * * @param name Name of the directory to be created. * * @returns A Psion error code (One of enum @ref #errs ). */ virtual Enum mkdir(const char * const name) = 0; /** * Removes a directory on the Psion. * * @param name Name of the directory to be removed. * * @returns A Psion error code (One of enum @ref #errs ). */ virtual Enum rmdir(const char * const name) = 0; /** * Renames a file on the Psion. * * @param oldname Name of the file to be renamed. * @param newname New Name for that file. * * @returns A Psion error code (One of enum @ref #errs ). */ virtual Enum rename(const char * const oldname, const char * const newname) = 0; /** * Removes a file on the Psion. * * @param name Name of the file to be removed. * * @returns A Psion error code (One of enum @ref #errs ). */ virtual Enum remove(const char * const name) = 0; /** * Open a directory for reading with readdir. * * @param attr A combination of PSI_A_.. flags, representing the desired types * of entries to be returned when calling @ref readdir . * @param name The name of the directory * @param handle A handle to be used with @ref readdir and @ref closedir . * * @returns A Psion error code (One of enum @ref #errs ). */ virtual Enum opendir(const uint32_t attr, const char * const name, rfsvDirhandle &handle) = 0; /** * Read directory entries. * This method reads entries of a directory, previously * opened with @ref opendir . * * @param handle A handle, obtained by calling @ref opendir . * @param entry The entry information is returned here. * * @returns A Psion error code (One of enum @ref #errs ). */ virtual Enum readdir(rfsvDirhandle &handle, PlpDirent &entry) = 0; /** * Close a directory, previously opened with @ref opendir. * * @param handle A handle, obtained by calling @ref opendir . * * @returns A Psion error code (One of enum @ref #errs ). */ virtual Enum closedir(rfsvDirhandle &handle) = 0; /** * Set the name of a Psion Volume (Drive). * * @param drive The drive character of the Volume, whose name should be set. * @param name The new name for that drive. * * @returns A Psion error code (One of enum @ref #errs ). */ virtual Enum setVolumeName(const char drive, const char * const name) = 0; /** * Converts a file attribute @ref rfsv::file_attribs to * human readable format, usable for showing them in directory * listings. The first 7 characters are common to all * machine types: *
    * 	Char Nr. Value
    * 	0        'd' if a directory,                     '-' otherwise.
    * 	1        'r' if file is readable,                '-' otherwise.
    * 	2        'w' if file is writeable,               '-' otherwise.
    * 	3        'h' if file is hidden,                  '-' otherwise.
    * 	4        's' if file is a system file,           '-' otherwise.
    * 	5        'a' if file is modified (archive flag), '-' otherwise.
    * 	6        'v' if file is a volume name,           '-' otherwise.
    * 
* The rest (3 characters) are machine specific: *
    * 	Char Nr. EPOC Value          SIBO Value
    * 	7        'n' if normal,      'x' if executable, '-' otherwise.
    * 	8        't' if temporary,   'b' if a stream,   '-' otherwise.
    * 	8        'c' if compressed,  't' if a textfile, '-' otherwise.
    * 
* * @param attr the generic file attribute. * * @returns Pointer to static textual representation of file attributes. * */ std::string attr2String(const uint32_t attr); /** * Converts an open-mode (A combination of the PSI_O_ constants.) * from generic representation to the machine-specific representation. * * @param mode The generic open mode. * * @returns The machine specific representation for use with * @ref fopen , @ref fcreatefile and @freplacefile. */ virtual uint32_t opMode(const uint32_t mode) = 0; /** * Utility method, converts '/' to '\'. */ static std::string convertSlash(const std::string &name); /** * Retrieve speed of serial link. * * @returns The speed of the serial link in baud or -1 on error. */ int getSpeed(); /** * Retrieves the protocol version. * * @returns Either 3 or 5 representing Series 3 (SIBO) or Series 5 (EPOC) */ virtual int getProtocolVersion() = 0; protected: /** * Retrieves the PLP protocol name. Mainly internal use. * * @returns The connection name always "SYS$RFSV" */ const char *getConnectName(); ppsocket *skt; Enum status; int32_t serNum; }; #endif plptools-1.0.26/lib/rfsv16.cc000066400000000000000000000552051504470754400157010ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999 Philip Proudman * Copyright (C) 1999 Matt J. Gumbley * Copyright (C) 1999-2002 Fritz Elfert * * 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 * along with this program; if not, see . * */ #include "config.h" #include "rfsv16.h" #include "bufferstore.h" #include "ppsocket.h" #include "bufferarray.h" #include #include #include #include #include "ignore-value.h" #define RFSV16_MAXDATALEN 852 // 640 using namespace std; rfsv16::rfsv16(ppsocket *_skt) { serNum = 0; status = rfsv::E_PSI_FILE_DISC; skt = _skt; reset(); } Enum rfsv16:: fopen(uint32_t attr, const char *name, uint32_t &handle) { bufferStore a; string realName = convertSlash(name); // Allow random access, rather than forcing the caller to ask for it a.addWord((P_FRANDOM | attr) & 0xFFFF); a.addStringT(realName.c_str()); if (!sendCommand(SIBO_FOPEN, a)) return E_PSI_FILE_DISC; Enum res = getResponse(a); if (res == 0) { handle = (long)a.getWord(0); return E_PSI_GEN_NONE; } return res; } Enum rfsv16:: mktemp(uint32_t &handle, string &tmpname) { bufferStore a; a.addWord(P_FUNIQUE); a.addStringT("TMP"); if (!sendCommand(SIBO_OPENUNIQUE, a)) return E_PSI_FILE_DISC; Enum res = getResponse(a); if (res == E_PSI_GEN_NONE) { handle = a.getWord(0); tmpname = a.getString(2); return res; } return res; } Enum rfsv16:: fcreatefile(uint32_t attr, const char *name, uint32_t &handle) { return fopen(attr | P_FCREATE, name, handle); } Enum rfsv16:: freplacefile(uint32_t attr, const char *name, uint32_t &handle) { return fopen(attr | P_FREPLACE, name, handle); } Enum rfsv16:: fopendir(const char * const name, uint32_t &handle) { return fopen(P_FDIR, name, handle); } Enum rfsv16:: fclose(uint32_t fileHandle) { bufferStore a; a.addWord(fileHandle & 0xFFFF); if (!sendCommand(SIBO_FCLOSE, a)) return E_PSI_FILE_DISC; return getResponse(a); } Enum rfsv16:: opendir(const uint32_t attr, const char *name, rfsvDirhandle &dH) { uint32_t handle; Enum res = fopendir(name, handle); dH.h = handle; dH.b.init(); return res; } Enum rfsv16:: closedir(rfsvDirhandle &dH) { return fclose(dH.h); } Enum rfsv16:: readdir(rfsvDirhandle &dH, PlpDirent &e) { Enum res = E_PSI_GEN_NONE; if (dH.b.getLen() < 17) { dH.b.init(); dH.b.addWord(dH.h & 0xFFFF); if (!sendCommand(SIBO_FDIRREAD, dH.b)) return E_PSI_FILE_DISC; res = getResponse(dH.b); if (res == E_PSI_GEN_NONE) { uint16_t bufferLen = dH.b.getWord(0); dH.b.discardFirstBytes(2); if (dH.b.getLen() != bufferLen) return E_PSI_GEN_FAIL; } } if ((res == E_PSI_GEN_NONE) && (dH.b.getLen() > 16)) { uint16_t version = dH.b.getWord(0); if (version != 2) return E_PSI_GEN_FAIL; e.attr = attr2std((uint32_t)dH.b.getWord(2)); e.size = dH.b.getDWord(4); e.time.setSiboTime(dH.b.getDWord(8)); e.name = dH.b.getString(16); //e.UID = PlpUID(0,0,0); e.attrstr = attr2String(e.attr); dH.b.discardFirstBytes(17 + e.name.length()); } return res; } Enum rfsv16:: dir(const char *name, PlpDir &files) { rfsvDirhandle h; files.clear(); Enum res = opendir(PSI_A_HIDDEN|PSI_A_SYSTEM|PSI_A_DIR, name, h); while (res == E_PSI_GEN_NONE) { PlpDirent e; res = readdir(h, e); if (res == E_PSI_GEN_NONE) files.push_back(e); } closedir(h); if (res == E_PSI_FILE_EOF) res = E_PSI_GEN_NONE; return res; } uint32_t rfsv16:: opMode(uint32_t mode) { uint32_t ret = 0; ret |= ((mode & 03) == PSI_O_RDONLY) ? 0 : P_FUPDATE; ret |= (mode & PSI_O_TRUNC) ? P_FREPLACE : 0; ret |= (mode & PSI_O_CREAT) ? P_FCREATE : 0; ret |= (mode & PSI_O_APPEND) ? P_FAPPEND : 0; if ((mode & 03) == PSI_O_RDONLY) ret |= (mode & PSI_O_EXCL) ? 0 : P_FSHARE; return ret; } Enum rfsv16:: fgetmtime(const char * const name, PsiTime &mtime) { bufferStore a; string realName = convertSlash(name); a.addStringT(realName.c_str()); if (!sendCommand(SIBO_FINFO, a)) return E_PSI_FILE_DISC; Enum res = getResponse(a); if (res != E_PSI_GEN_NONE) return res; else if (a.getLen() == 16) { // According to Alex's docs, Psion's file times are in // seconds since 13:00!!, 1.1.1970 mtime.setSiboTime(a.getDWord(8)); return res; } return E_PSI_GEN_FAIL; } Enum rfsv16:: fsetmtime(const char *name, PsiTime mtime) { // According to Alexander's protocol doc, SIBO_SFDATE sets the modification // time - and as far as I can see SIBO only keeps a modification // time. So call SIBO_SFDATE here. bufferStore a; string realName = convertSlash(name); // According to Alex's docs, Psion's file times are in // seconds since 13:00!!, 1.1.1970 a.addDWord(mtime.getSiboTime()); a.addStringT(realName.c_str()); if (!sendCommand(SIBO_SFDATE, a)) return E_PSI_FILE_DISC; return getResponse(a); } Enum rfsv16:: fgetattr(const char * const name, uint32_t &attr) { bufferStore a; string realName = convertSlash(name); a.addStringT(realName.c_str()); if (!sendCommand(SIBO_FINFO, a)) return E_PSI_FILE_DISC; Enum res = getResponse(a); if (res != E_PSI_GEN_NONE) return res; else if (a.getLen() == 16) { attr = attr2std((long)a.getWord(2)); return res; } return E_PSI_GEN_FAIL; } Enum rfsv16:: fgeteattr(const char * const name, PlpDirent &e) { bufferStore a; string realName = convertSlash(name); a.addStringT(realName.c_str()); if (!sendCommand(SIBO_FINFO, a)) return E_PSI_FILE_DISC; Enum res = getResponse(a); if (res != E_PSI_GEN_NONE) return res; else if (a.getLen() == 16) { const char *p = strrchr(realName.c_str(), '\\'); if (p) p++; else p = realName.c_str(); e.name = p; e.attr = attr2std((long)a.getWord(2)); e.size = a.getDWord(4); e.time.setSiboTime(a.getDWord(8)); e.UID = PlpUID(0,0,0); e.attrstr = string(attr2String(e.attr)); return res; } return E_PSI_GEN_FAIL; } Enum rfsv16:: fsetattr(const char *name, uint32_t seta, uint32_t unseta) { uint32_t statusword = std2attr(seta) & (~ std2attr(unseta)); statusword ^= 0x0000001; // r bit is inverted uint32_t bitmask = std2attr(seta) | std2attr(unseta); bufferStore a; a.addWord(statusword & 0xFFFF); a.addWord(bitmask & 0xFFFF); a.addStringT(name); if (!sendCommand(SIBO_SFSTAT, a)) return E_PSI_FILE_DISC; return getResponse(a); } Enum rfsv16:: dircount(const char * const name, uint32_t &count) { rfsvDirhandle h; Enum res = opendir(PSI_A_HIDDEN|PSI_A_SYSTEM|PSI_A_DIR, name, h); while (res == E_PSI_GEN_NONE) { PlpDirent e; res = readdir(h, e); if (res == E_PSI_GEN_NONE) count++; } closedir(h); if (res == E_PSI_FILE_EOF) res = E_PSI_GEN_NONE; return res; } Enum rfsv16:: devlist(uint32_t &devbits) { Enum res; uint32_t fileHandle; devbits = 0; // The following is taken from a trace between a Series 3c and PsiWin. // Hope it works! We SIBO_PARSE to find the correct node, then FOPEN // (P_FDEVICE) this, SIBO_FDEVICEREAD each entry, setting the appropriate // drive-letter-bit in devbits, then FCLOSE. bufferStore a; a.init(); a.addByte(0x00); // no Name 1 a.addByte(0x00); // no Name 2 a.addByte(0x00); // no Name 3 if (!sendCommand(SIBO_PARSE, a)) return E_PSI_FILE_DISC; res = getResponse(a); if (res != E_PSI_GEN_NONE) return res; // Find the drive to FOPEN char name[4] = { 'x', ':', '\\', '\0' } ; a.discardFirstBytes(6); // Result, fsys, dev, path, file, file, ending, flag /* This leaves R E M : : M : \ */ name[0] = (char) a.getByte(5); // the M res = fopen(P_FDEVICE, name, fileHandle); if (res != E_PSI_GEN_NONE) return status; while (1) { a.init(); a.addWord(fileHandle & 0xFFFF); if (!sendCommand(SIBO_FDEVICEREAD, a)) return E_PSI_FILE_DISC; res = getResponse(a); if (res) break; uint16_t version = a.getWord(0); if ((version < 1) || (version > 2)) { cerr << "devlist: not version 1 or 2" << endl; fclose(fileHandle); return E_PSI_GEN_FAIL; } char drive = a.getByte(64); if (drive >= 'A' && drive <= 'Z') { int shift = (drive - 'A'); devbits |= (long) ( 1 << shift ); } else { cerr << "devlist: non-alphabetic drive letter (" << drive << ")" << endl; } } if (res == E_PSI_FILE_EOF) res = E_PSI_GEN_NONE; fclose(fileHandle); return res; } static int sibo_dattr[] = { 1, // Unknown 2, // Floppy 3, // Disk 6, // Flash 5, // RAM 7, // ROM 7, // write-protected == ROM ? }; Enum rfsv16:: devinfo(const char drive, PlpDrive &dinfo) { bufferStore a; Enum res; // Again, this is taken from an exchange between PsiWin and a 3c. // For each drive, we SIBO_PARSE with its drive letter to get a response // (which we ignore), then do a SIBO_STATUSDEVICE to get the info. a.init(); a.addByte(toupper(drive)); // Name 1 a.addByte(':'); a.addByte(0x00); a.addByte(0x00); // No name 2 a.addByte(0x00); // No name 3 if (!sendCommand(SIBO_PARSE, a)) return E_PSI_FILE_DISC; if ((res = getResponse(a)) != E_PSI_GEN_NONE) return res; a.init(); a.addByte(toupper(drive)); // Name 1 a.addByte(':'); a.addByte('\\'); a.addByte(0x00); if (!sendCommand(SIBO_STATUSDEVICE, a)) return E_PSI_FILE_DISC; if ((res = getResponse(a)) != E_PSI_GEN_NONE) return res; int attr = a.getWord(2); attr = sibo_dattr[a.getWord(2) & 0xff]; dinfo.setMediaType(attr); attr = a.getWord(2); int changeable = a.getWord(4) ? 32 : 0; int internal = (attr & 0x2000) ? 16 : 0; dinfo.setDriveAttribute(changeable | internal); int variable = (attr & 0x4000) ? 1 : 0; int dualdens = (attr & 0x1000) ? 2 : 0; int formattable = (attr & 0x0800) ? 4 : 0; int protect = ((attr & 0xff) == 6) ? 8 : 0; dinfo.setMediaAttribute(variable|dualdens|formattable|protect); dinfo.setUID(0); dinfo.setSize(a.getDWord(6), 0); dinfo.setSpace(a.getDWord(10), 0); dinfo.setName(toupper(drive), a.getString(14)); return res; } bool rfsv16:: sendCommand(enum commands cc, bufferStore & data) { if (status == E_PSI_FILE_DISC) { reconnect(); if (status == E_PSI_FILE_DISC) return false; } bool result; bufferStore a; a.addWord(cc); a.addWord(data.getLen()); a.addBuff(data); result = skt->sendBufferStore(a); if (!result) { reconnect(); result = skt->sendBufferStore(a); if (!result) status = E_PSI_FILE_DISC; } return result; } Enum rfsv16:: getResponse(bufferStore & data) { // getWord(2) is the size field // which is the body of the response not counting the command (002a) and // the size word. if (skt->getBufferStore(data) != 1) { cerr << "rfsv16::getResponse: duff response. " "getBufferStore failed." << endl; } else if (data.getWord(0) == 0x2a && data.getWord(2) == data.getLen()-4) { Enum ret = (enum errs)(int16_t)data.getWord(4); data.discardFirstBytes(6); return ret; } else { cerr << "rfsv16::getResponse: duff response. Size field:" << data.getWord(2) << " Frame size:" << data.getLen()-4 << " Result field:" << data.getWord(4) << endl; } status = E_PSI_FILE_DISC; return status; } Enum rfsv16:: fread(const uint32_t handle, unsigned char * const buf, const uint32_t len, uint32_t &count) { Enum res; unsigned char *p = buf; long l; count = 0; do { bufferStore a; // Read in blocks of 291 bytes; the maximum payload for // an RFSV frame. ( As seen in traces ) - this isn't optimal: // RFSV can handle fragmentation of frames, where only the // first SIBO_FREAD RESPONSE frame has a RESPONSE (00 2A), SIZE // and RESULT field. Every subsequent frame // just has data, 297 bytes (or less) of it. // a.addWord(handle); a.addWord((len - count) > RFSV16_MAXDATALEN ? RFSV16_MAXDATALEN : (len - count)); if (!sendCommand(SIBO_FREAD, a)) return E_PSI_FILE_DISC; if ((res = getResponse(a)) != E_PSI_GEN_NONE) { if (res == E_PSI_FILE_EOF) return E_PSI_GEN_NONE; return res; } if ((l = a.getLen()) > 0) { memcpy(p, a.getString(), l); count += l; p += l; } } while ((count < len) && (l > 0)); return res; } Enum rfsv16:: fwrite(const uint32_t handle, const unsigned char * const buf, const uint32_t len, uint32_t &count) { Enum res; const unsigned char *p = buf; count = 0; while (count < len) { bufferStore a; int nbytes; // Write in blocks of 291 bytes; the maximum payload for // an RFSV frame. ( As seen in traces ) - this isn't optimal: // RFSV can handle fragmentation of frames, where only the // first SIBO_FREAD RESPONSE frame has a RESPONSE (00 2A), SIZE // and RESULT field. Every subsequent frame // just has data, 297 bytes (or less) of it. nbytes = (len - count) > RFSV16_MAXDATALEN ? RFSV16_MAXDATALEN : (len - count); a.addWord(handle); a.addBytes(p, nbytes); if (!sendCommand(SIBO_FWRITE, a)) return E_PSI_FILE_DISC; if ((res = getResponse(a)) != E_PSI_GEN_NONE) return res; count += nbytes; p += nbytes; } return res; } Enum rfsv16:: copyFromPsion(const char *from, const char *to, void *ptr, cpCallback_t cb) { Enum res; uint32_t handle; uint32_t len; uint32_t total = 0; if ((res = fopen(P_FSHARE | P_FSTREAM, from, handle)) != E_PSI_GEN_NONE) return res; ofstream op(to); if (!op) { fclose(handle); return E_PSI_GEN_FAIL; } do { unsigned char buf[RFSV_SENDLEN]; if ((res = fread(handle, buf, sizeof(buf), len)) == E_PSI_GEN_NONE) { if (len > 0) op.write((char *)buf, len); total += len; if (cb && !cb(ptr, total)) res = E_PSI_FILE_CANCEL; } } while (len > 0 && (res == E_PSI_GEN_NONE)); fclose(handle); op.close(); if (res == E_PSI_FILE_EOF) res = E_PSI_GEN_NONE; return res; } Enum rfsv16:: copyFromPsion(const char *from, int fd, cpCallback_t cb) { Enum res; uint32_t handle; uint32_t len; uint32_t total = 0; if ((res = fopen(P_FSHARE | P_FSTREAM, from, handle)) != E_PSI_GEN_NONE) return res; do { unsigned char buf[RFSV_SENDLEN]; if ((res = fread(handle, buf, sizeof(buf), len)) == E_PSI_GEN_NONE) { if (len > 0) // FIXME: return UNIX errors from this method. ignore_value(write(fd, buf, len)); total += len; if (cb && !cb(NULL, total)) res = E_PSI_FILE_CANCEL; } } while (len > 0 && (res == E_PSI_GEN_NONE)); fclose(handle); if (res == E_PSI_FILE_EOF) res = E_PSI_GEN_NONE; return res; } Enum rfsv16:: copyToPsion(const char *from, const char *to, void *ptr, cpCallback_t cb) { uint32_t handle; uint32_t len = 0; uint32_t total = 0; Enum res; ifstream ip(from); if (!ip) return E_PSI_FILE_NXIST; res = fcreatefile(P_FSTREAM | P_FUPDATE, to, handle); if (res != E_PSI_GEN_NONE) { res = freplacefile(P_FSTREAM | P_FUPDATE, to, handle); if (res != E_PSI_GEN_NONE) return res; } unsigned char *buff = new unsigned char[RFSV_SENDLEN]; while (res == E_PSI_GEN_NONE && ip && !ip.eof()) { ip.read((char *)buff, RFSV_SENDLEN); if ((res = fwrite(handle, buff, ip.gcount(), len)) == E_PSI_GEN_NONE) { total += len; if (cb && !cb(ptr, total)) res = E_PSI_FILE_CANCEL; } } delete[]buff; fclose(handle); ip.close(); return res; } Enum rfsv16:: copyOnPsion(const char *from, const char *to, void *ptr, cpCallback_t cb) { uint32_t handle_from; uint32_t handle_to; uint32_t len; uint32_t wlen; uint32_t total = 0; Enum res; if ((res = fopen(P_FSHARE | P_FSTREAM, from, handle_from)) != E_PSI_GEN_NONE) return res; res = fcreatefile(P_FSTREAM | P_FUPDATE, to, handle_to); if (res != E_PSI_GEN_NONE) { res = freplacefile(P_FSTREAM | P_FUPDATE, to, handle_to); if (res != E_PSI_GEN_NONE) return res; } do { unsigned char buf[RFSV_SENDLEN]; if ((res = fread(handle_from, buf, sizeof(buf), len)) == E_PSI_GEN_NONE) { if (len > 0) { if ((res = fwrite(handle_to, buf, len, wlen)) == E_PSI_GEN_NONE) { total += wlen; if (cb && !cb(ptr, total)) res = E_PSI_FILE_CANCEL; } } } } while (len > 0 && wlen > 0 && (res == E_PSI_GEN_NONE)); fclose(handle_from); fclose(handle_to); if (res == E_PSI_FILE_EOF) res = E_PSI_GEN_NONE; return res; } Enum rfsv16:: pathtest(const char * const path) { string realName = convertSlash(path); bufferStore a; a.addStringT(realName.c_str()); sendCommand(SIBO_PATHTEST, a); return getResponse(a); } Enum rfsv16:: fsetsize(uint32_t handle, uint32_t size) { bufferStore a; a.addWord(handle & 0xffff); a.addDWord(size); if (!sendCommand(SIBO_FSETEOF, a)) return E_PSI_FILE_DISC; return getResponse(a); } /* * Unix-like implementation off fseek with one * exception: If seeking beyond eof, the gap * contains garbage instead of zeroes. */ Enum rfsv16:: fseek(const uint32_t handle, const int32_t pos, const uint32_t mode, uint32_t &resultpos) { bufferStore a; Enum res; uint32_t savpos = 0; uint32_t realpos; uint32_t calcpos = 0; /* seek-parameter for psion: dword position dword handle dword mode 1 = from start 2 = from current pos 3 = from end ??no more?? 4 = sense recpos ??no more?? 5 = set recpos ??no more?? 6 = text-rewind */ if ((mode < PSI_SEEK_SET) || (mode > PSI_SEEK_END)) return E_PSI_GEN_ARG; if ((mode == PSI_SEEK_CUR) && (pos >= 0)) { /* get and save current position */ a.init(); a.addWord(handle); a.addDWord(0); a.addWord(PSI_SEEK_CUR); if (!sendCommand(SIBO_FSEEK, a)) return E_PSI_FILE_DISC; if ((res = getResponse(a)) != E_PSI_GEN_NONE) return res; savpos = a.getDWord(0); if (pos == 0) { resultpos = savpos; return res; } } if ((mode == PSI_SEEK_END) && (pos >= 0)) { /* get and save end position */ a.init(); a.addWord(handle); a.addDWord(0); a.addWord(PSI_SEEK_END); if (!sendCommand(SIBO_FSEEK, a)) return E_PSI_FILE_DISC; if ((res = getResponse(a)) != E_PSI_GEN_NONE) return res; savpos = a.getDWord(0); if (pos == 0) { resultpos = savpos; return res; } } /* Now the real seek */ a.addWord(handle); a.addDWord(pos); a.addWord(mode); if (!sendCommand(SIBO_FSEEK, a)) return E_PSI_FILE_DISC; if ((res = getResponse(a)) != 0) return res; realpos = a.getDWord(0); switch (mode) { case PSI_SEEK_SET: calcpos = pos; break; case PSI_SEEK_CUR: calcpos = savpos + pos; break; case PSI_SEEK_END: resultpos = realpos; return res; break; } if (calcpos > realpos) { /* Beyond end of file */ res = fsetsize(handle, calcpos); if (res != E_PSI_GEN_NONE) return res; a.init(); a.addWord(handle); a.addDWord(calcpos); a.addWord(PSI_SEEK_SET); if (!sendCommand(SIBO_FSEEK, a)) return E_PSI_FILE_DISC; if ((res = getResponse(a)) != 0) return res; realpos = a.getDWord(0); } resultpos = realpos; return res; } Enum rfsv16:: mkdir(const char* dirName) { string realName = convertSlash(dirName); bufferStore a; a.addStringT(realName.c_str()); sendCommand(SIBO_MKDIR, a); return getResponse(a); } Enum rfsv16:: rmdir(const char *dirName) { // There doesn't seem to be an RMDIR command, but DELETE works. We // should probably check to see if the file is a directory first! return remove(dirName); } Enum rfsv16:: rename(const char *oldName, const char *newName) { string realOldName = convertSlash(oldName); string realNewName = convertSlash(newName); bufferStore a; a.addStringT(realOldName.c_str()); a.addStringT(realNewName.c_str()); sendCommand(SIBO_RENAME, a); return getResponse(a); } Enum rfsv16:: remove(const char* psionName) { string realName = convertSlash(psionName); bufferStore a; a.addStringT(realName.c_str()); // and this needs sending in the length word. sendCommand(SIBO_DELETE, a); return getResponse(a); } Enum rfsv16:: setVolumeName(const char drive , const char * const name) { // Not yet ... return E_PSI_GEN_FAIL; } /* * Translate SIBO attributes to standard attributes. */ uint32_t rfsv16:: attr2std(uint32_t attr) { uint32_t res = 0; // Common attributes if (!(attr & P_FAWRITE)) res |= PSI_A_RDONLY; if (attr & P_FAHIDDEN) res |= PSI_A_HIDDEN; if (attr & P_FASYSTEM) res |= PSI_A_SYSTEM; if (attr & P_FADIR) res |= PSI_A_DIR; if (attr & P_FAMOD) res |= PSI_A_ARCHIVE; if (attr & P_FAVOLUME) res |= PSI_A_VOLUME; // SIBO-specific if (attr & P_FAREAD) res |= PSI_A_READ; if (attr & P_FAEXEC) res |= PSI_A_EXEC; if (attr & P_FASTREAM) res |= PSI_A_STREAM; if (attr & P_FATEXT) res |= PSI_A_TEXT; // Do what we can for EPOC res |= PSI_A_NORMAL; return res; } /* * Translate standard attributes to SIBO attributes. */ uint32_t rfsv16:: std2attr(const uint32_t attr) { uint32_t res = 0; // Common attributes if (!(attr & PSI_A_RDONLY)) res |= P_FAWRITE; if (attr & PSI_A_HIDDEN) res |= P_FAHIDDEN; if (attr & PSI_A_SYSTEM) res |= P_FASYSTEM; if (attr & PSI_A_DIR) res |= P_FADIR; if (attr & PSI_A_ARCHIVE) res |= P_FAMOD; if (attr & PSI_A_VOLUME) res |= P_FAVOLUME; // SIBO-specific if (attr & PSI_A_READ) res |= P_FAREAD; if (attr & PSI_A_EXEC) res |= P_FAEXEC; if (attr & PSI_A_STREAM) res |= P_FASTREAM; if (attr & PSI_A_TEXT) res |= P_FATEXT; return res; } plptools-1.0.26/lib/rfsv16.h000066400000000000000000000135171504470754400155430ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999 Philip Proudman * Copyright (C) 1999-2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #ifndef _RFSV16_H_ #define _RFSV16_H_ #include class rfsvfactory; /** * This is the implementation of the @ref rfsv protocol for * Psion series 3 (SIBO) variant. You normally never create * objects of this class directly. Thus the constructor is * private. Use @ref rfsvfactory for creating an instance of * @ref rfsv . For a complete documentation, see @ref rfsv . */ class rfsv16 : public rfsv { /** * rfsvfactory may call our constructor. */ friend class rfsvfactory; public: Enum fopen(const uint32_t, const char * const, uint32_t &); Enum mktemp(uint32_t &, std::string &); Enum fcreatefile(const uint32_t, const char * const, uint32_t &); Enum freplacefile(const uint32_t, const char * const, uint32_t &); Enum fclose(const uint32_t); Enum dir(const char * const, PlpDir &); Enum pathtest(const char * const); Enum fgetmtime(const char * const, PsiTime &); Enum fsetmtime(const char * const, const PsiTime); Enum fgetattr(const char * const, uint32_t &); Enum fgeteattr(const char * const, PlpDirent &); Enum fsetattr(const char * const, const uint32_t seta, const uint32_t unseta); Enum dircount(const char * const, uint32_t &); Enum devlist(uint32_t &); Enum devinfo(const char, PlpDrive &); Enum fread(const uint32_t, unsigned char * const, const uint32_t, uint32_t &); Enum fwrite(const uint32_t, const unsigned char * const, const uint32_t, uint32_t &); Enum copyFromPsion(const char * const, const char * const, void *, cpCallback_t); Enum copyFromPsion(const char *from, int fd, cpCallback_t cb); Enum copyToPsion(const char * const, const char * const, void *, cpCallback_t); Enum copyOnPsion(const char *, const char *, void *, cpCallback_t); Enum fsetsize(const uint32_t, const uint32_t); Enum fseek(const uint32_t, const int32_t, const uint32_t, uint32_t &); Enum mkdir(const char * const); Enum rmdir(const char * const); Enum rename(const char * const, const char * const); Enum remove(const char * const); Enum opendir(const uint32_t, const char * const, rfsvDirhandle &); Enum readdir(rfsvDirhandle &, PlpDirent &); Enum closedir(rfsvDirhandle &); Enum setVolumeName(const char, const char * const); uint32_t opMode(const uint32_t); int getProtocolVersion() { return 3; } private: enum commands { SIBO_FOPEN = 0, // File Open SIBO_FCLOSE = 2, // File Close SIBO_FREAD = 4, // File Read SIBO_FDIRREAD = 6, // Read Directory entries SIBO_FDEVICEREAD = 8, // Device Information SIBO_FWRITE = 10, // File Write SIBO_FSEEK = 12, // File Seek SIBO_FFLUSH = 14, // Flush SIBO_FSETEOF = 16, SIBO_RENAME = 18, SIBO_DELETE = 20, SIBO_FINFO = 22, SIBO_SFSTAT = 24, SIBO_PARSE = 26, SIBO_MKDIR = 28, SIBO_OPENUNIQUE = 30, SIBO_STATUSDEVICE = 32, SIBO_PATHTEST = 34, SIBO_STATUSSYSTEM = 36, SIBO_CHANGEDIR = 38, SIBO_SFDATE = 40, SIBO_RESPONSE = 42 }; enum fopen_attrib { P_FOPEN = 0x0000, /* Open file */ P_FCREATE = 0x0001, /* Create file */ P_FREPLACE = 0x0002, /* Replace file */ P_FAPPEND = 0x0003, /* Append records */ P_FUNIQUE = 0x0004, /* Unique file open */ P_FSTREAM = 0x0000, /* Stream access to a binary file */ P_FSTREAM_TEXT = 0x0010, /* Stream access to a text file */ P_FTEXT = 0x0020, /* Record access to a text file */ P_FDIR = 0x0030, /* Record access to a directory file */ P_FFORMAT = 0x0040, /* Format a device */ P_FDEVICE = 0x0050, /* Record access to device name list */ P_FNODE = 0x0060, /* Record access to node name list */ P_FUPDATE = 0x0100, /* Read and write access */ P_FRANDOM = 0x0200, /* Random access */ P_FSHARE = 0x0400 /* File can be shared */ }; enum status_enum { P_FAWRITE = 0x0001, /* can the file be written to? */ P_FAHIDDEN = 0x0002, /* set if file is hidden */ P_FASYSTEM = 0x0004, /* set if file is a system file */ P_FAVOLUME = 0x0008, /* set if the name is a volume name */ P_FADIR = 0x0010, /* set if file is a directory file */ P_FAMOD = 0x0020, /* has the file been modified? */ P_FAREAD = 0x0100, /* can the file be read? */ P_FAEXEC = 0x0200, /* is the file executable? */ P_FASTREAM = 0x0400, /* is the file a byte stream file? */ P_FATEXT = 0x0800, /* is it a text file? */ P_FAMASK = 0x0f3f /* All of the above */ }; /** * Private constructor. Shall be called by * rfsvfactory only. */ rfsv16(ppsocket *); // Miscellaneous Enum fopendir(const char * const, uint32_t &); uint32_t attr2std(const uint32_t); uint32_t std2attr(const uint32_t); // Communication bool sendCommand(enum commands, bufferStore &); Enum getResponse(bufferStore &); }; #endif plptools-1.0.26/lib/rfsv32.cc000066400000000000000000000546301504470754400157000ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999 Philip Proudman * Copyright (C) 1999-2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #include "config.h" #include "rfsv32.h" #include "bufferstore.h" #include "ppsocket.h" #include "bufferarray.h" #include "plpdirent.h" #include #include #include #include #include "ignore-value.h" using namespace std; rfsv32::rfsv32(ppsocket * _skt) { skt = _skt; serNum = 0; status = rfsv::E_PSI_FILE_DISC; reset(); } Enum rfsv32:: fopen(uint32_t attr, const char *name, uint32_t &handle) { bufferStore a; string n = convertSlash(name); a.addDWord(attr); a.addWord(n.size()); a.addString(n.c_str()); if (!sendCommand(OPEN_FILE, a)) return E_PSI_FILE_DISC; Enum res = getResponse(a); if (res == E_PSI_GEN_NONE && a.getLen() == 4) { handle = a.getDWord(0); return E_PSI_GEN_NONE; } return res; } Enum rfsv32:: mktemp(uint32_t &handle, string &tmpname) { bufferStore a; if (!sendCommand(TEMP_FILE, a)) return E_PSI_FILE_DISC; Enum res = getResponse(a); if (res == E_PSI_GEN_NONE) { handle = a.getDWord(0); tmpname = a.getString(6); } return res; } Enum rfsv32:: fcreatefile(uint32_t attr, const char *name, uint32_t &handle) { bufferStore a; string n = convertSlash(name); a.addDWord(attr); a.addWord(n.size()); a.addString(n.c_str()); if (!sendCommand(CREATE_FILE, a)) return E_PSI_FILE_DISC; Enum res = getResponse(a); if (res == E_PSI_GEN_NONE && a.getLen() == 4) handle = a.getDWord(0); return res; } Enum rfsv32:: freplacefile(const uint32_t attr, const char * const name, uint32_t &handle) { bufferStore a; string n = convertSlash(name); a.addDWord(attr); a.addWord(n.size()); a.addString(n.c_str()); if (!sendCommand(REPLACE_FILE, a)) return E_PSI_FILE_DISC; Enum res = getResponse(a); if (res == E_PSI_GEN_NONE && a.getLen() == 4) handle = a.getDWord(0); return res; } Enum rfsv32:: fopendir(const uint32_t attr, const char * const name, uint32_t &handle) { bufferStore a; string n = convertSlash(name); a.addDWord(attr | EPOC_ATTR_GETUID); a.addWord(n.size()); a.addString(n.c_str()); if (!sendCommand(OPEN_DIR, a)) return E_PSI_FILE_DISC; Enum res = getResponse(a); if (!res && a.getLen() == 4) handle = a.getDWord(0); return res; } Enum rfsv32:: fclose(uint32_t handle) { bufferStore a; a.addDWord(handle); if (!sendCommand(CLOSE_HANDLE, a)) return E_PSI_FILE_DISC; return getResponse(a); } Enum rfsv32:: opendir(const uint32_t attr, const char *name, rfsvDirhandle &dH) { uint32_t handle; Enum res = fopendir(std2attr(attr), name, handle); dH.h = handle; dH.b.init(); return res; } Enum rfsv32:: closedir(rfsvDirhandle &dH) { return fclose(dH.h); } Enum rfsv32:: readdir(rfsvDirhandle &dH, PlpDirent &e) { Enum res = E_PSI_GEN_NONE; if (dH.b.getLen() < 17) { dH.b.init(); dH.b.addDWord(dH.h); if (!sendCommand(READ_DIR, dH.b)) return E_PSI_FILE_DISC; res = getResponse(dH.b); } if ((res == E_PSI_GEN_NONE) && (dH.b.getLen() > 16)) { long shortLen = dH.b.getDWord(0); long longLen = dH.b.getDWord(32); e.attr = attr2std(dH.b.getDWord(4)); e.size = dH.b.getDWord(8); e.UID = PlpUID(dH.b.getDWord(20), dH.b.getDWord(24), dH.b.getDWord(28)); e.time = PsiTime(dH.b.getDWord(16), dH.b.getDWord(12)); e.name = ""; e.attrstr = string(attr2String(e.attr)); int d = 36; for (int i = 0; i < longLen; i++, d++) e.name += dH.b.getByte(d); while (d % 4) d++; d += shortLen; while (d % 4) d++; dH.b.discardFirstBytes(d); } return res; } Enum rfsv32:: dir(const char *name, PlpDir &files) { rfsvDirhandle h; files.clear(); Enum res = opendir(PSI_A_HIDDEN | PSI_A_SYSTEM | PSI_A_DIR, name, h); while (res == E_PSI_GEN_NONE) { PlpDirent e; res = readdir(h, e); if (res == E_PSI_GEN_NONE) files.push_back(e); } closedir(h); if (res == E_PSI_FILE_EOF) res = E_PSI_GEN_NONE; return res; } uint32_t rfsv32:: opMode(const uint32_t mode) { uint32_t ret = 0; ret |= (((mode & 03) == PSI_O_RDONLY) ? 0 : EPOC_OMODE_READ_WRITE); if (!ret) ret |= (mode & PSI_O_EXCL) ? 0 : EPOC_OMODE_SHARE_READERS; if ((!ret) && (mode & PSI_O_SHARE)) ret |= EPOC_OMODE_SHARE_ANY; return ret; } Enum rfsv32:: fgetmtime(const char * const name, PsiTime &mtime) { bufferStore a; string n = convertSlash(name); a.addWord(n.size()); a.addString(n.c_str()); if (!sendCommand(MODIFIED, a)) return E_PSI_FILE_DISC; Enum res = getResponse(a); if (res != E_PSI_GEN_NONE) return res; mtime.setPsiTime(a.getDWord(4), a.getDWord(0)); return res; } Enum rfsv32:: fsetmtime(const char * const name, PsiTime mtime) { bufferStore a; string n = convertSlash(name); a.addDWord(mtime.getPsiTimeLo()); a.addDWord(mtime.getPsiTimeHi()); a.addWord(n.size()); a.addString(n.c_str()); if (!sendCommand(SET_MODIFIED, a)) return E_PSI_FILE_DISC; return getResponse(a); } Enum rfsv32:: fgetattr(const char * const name, uint32_t &attr) { bufferStore a; string n = convertSlash(name); a.addWord(n.size()); a.addString(n.c_str()); if (!sendCommand(ATT, a)) return E_PSI_FILE_DISC; Enum res = getResponse(a); if (res != E_PSI_GEN_NONE) return res; attr = attr2std(a.getDWord(0)); return res; } Enum rfsv32:: fgeteattr(const char * const name, PlpDirent &e) { bufferStore a; string n = convertSlash(name); a.addWord(n.size()); a.addString(n.c_str()); const char *p = strrchr(n.c_str(), '\\'); if (p) p++; else p = n.c_str(); e.name = p; if (!sendCommand(REMOTE_ENTRY, a)) return E_PSI_FILE_DISC; Enum res = getResponse(a); if (res != E_PSI_GEN_NONE) return res; // long shortLen = a.getDWord(0); // long longLen = a.getDWord(32); e.attr = attr2std(a.getDWord(4)); e.size = a.getDWord(8); e.UID = PlpUID(a.getDWord(20), a.getDWord(24), a.getDWord(28)); e.time = PsiTime(a.getDWord(16), a.getDWord(12)); e.attrstr = string(attr2String(e.attr)); return res; } Enum rfsv32:: fsetattr(const char * const name, const uint32_t seta, const uint32_t unseta) { bufferStore a; string n = convertSlash(name); a.addDWord(std2attr(seta)); a.addDWord(std2attr(unseta)); a.addWord(n.size()); a.addString(n.c_str()); if (!sendCommand(SET_ATT, a)) return E_PSI_FILE_DISC; return getResponse(a); } Enum rfsv32:: dircount(const char * const name, uint32_t &count) { uint32_t handle; Enum res = fopendir(EPOC_ATTR_HIDDEN | EPOC_ATTR_SYSTEM | EPOC_ATTR_DIRECTORY, name, handle); count = 0; if (res != E_PSI_GEN_NONE) return res; while (1) { bufferStore a; a.addDWord(handle); if (!sendCommand(READ_DIR, a)) return E_PSI_FILE_DISC; res = getResponse(a); if (res != E_PSI_GEN_NONE) break; while (a.getLen() > 16) { int d = 36 + a.getDWord(32); while (d % 4) d++; d += a.getDWord(0); while (d % 4) d++; a.discardFirstBytes(d); count++; } } fclose(handle); if (res == E_PSI_FILE_EOF) res = E_PSI_GEN_NONE; return res; } Enum rfsv32:: devlist(uint32_t &devbits) { bufferStore a; Enum res; if (!sendCommand(GET_DRIVE_LIST, a)) return E_PSI_FILE_DISC; res = getResponse(a); devbits = 0; if ((res == E_PSI_GEN_NONE) && (a.getLen() == 26)) { for (int i = 25; i >= 0; i--) { devbits <<= 1; if (a.getByte(i) != 0) devbits |= 1; } } return res; } Enum rfsv32:: devinfo(const char drive, PlpDrive &dinfo) { bufferStore a; Enum res; a.addDWord(toupper(drive) - 'A'); if (!sendCommand(DRIVE_INFO, a)) return E_PSI_FILE_DISC; res = getResponse(a); if (res == E_PSI_GEN_NONE) { dinfo.setMediaType(a.getDWord(0)); dinfo.setDriveAttribute(a.getDWord(8)); dinfo.setMediaAttribute(a.getDWord(12)); dinfo.setUID(a.getDWord(16)); dinfo.setSize(a.getDWord(20), a.getDWord(24)); dinfo.setSpace(a.getDWord(28), a.getDWord(32)); a.addByte(0); dinfo.setName(toupper(drive), a.getString(40)); } return res; } bool rfsv32:: sendCommand(enum commands cc, bufferStore & data) { if (status == E_PSI_FILE_DISC) { reconnect(); if (status == E_PSI_FILE_DISC) return false; } bool result; bufferStore a; a.addWord(cc); a.addWord(serNum); if (serNum < 0xffff) serNum++; else serNum = 0; a.addBuff(data); result = skt->sendBufferStore(a); if (!result) { reconnect(); result = skt->sendBufferStore(a); if (!result) status = E_PSI_FILE_DISC; } return result; } Enum rfsv32:: getResponse(bufferStore & data) { if (skt->getBufferStore(data) == 1 && data.getWord(0) == 0x11) { int32_t ret = data.getDWord(4); data.discardFirstBytes(8); return err2psierr(ret); } else status = E_PSI_FILE_DISC; return status; } Enum rfsv32:: fread(const uint32_t handle, unsigned char * const buf, const uint32_t len, uint32_t &count) { Enum res; bufferStore a; count = 0; long l; unsigned char *p = buf; do { a.addDWord(handle); a.addDWord(((len - count) > RFSV_SENDLEN)?RFSV_SENDLEN:(len - count)); if (!sendCommand(READ_FILE, a)) return E_PSI_FILE_DISC; if ((res = getResponse(a)) != E_PSI_GEN_NONE) return res; if ((l = a.getLen()) > 0) { memcpy(p, a.getString(), l); count += l; p += l; } a.init(); } while ((count < len) && (l > 0)); return res; } Enum rfsv32:: fwrite(const uint32_t handle, const unsigned char * const buf, const uint32_t len, uint32_t &count) { Enum res; const unsigned char *p = buf; long l; count = 0; do { l = ((len - count) > RFSV_SENDLEN)?RFSV_SENDLEN:(len - count); if (l > 0) { bufferStore a; bufferStore tmp(p, l); a.addDWord(handle); a.addBuff(tmp); if (!sendCommand(WRITE_FILE, a)) return E_PSI_FILE_DISC; if ((res = getResponse(a)) != E_PSI_GEN_NONE) return res; count += l; p += l; } } while ((count < len) && (l > 0)); return res; } Enum rfsv32:: copyFromPsion(const char *from, const char *to, void *ptr, cpCallback_t cb) { Enum res; uint32_t handle; uint32_t len; uint32_t total = 0; if ((res = fopen(EPOC_OMODE_SHARE_READERS | EPOC_OMODE_BINARY, from, handle)) != E_PSI_GEN_NONE) return res; ofstream op(to); if (!op) { fclose(handle); return E_PSI_GEN_FAIL; } unsigned char *buff = new unsigned char[RFSV_SENDLEN]; do { if ((res = fread(handle, buff, RFSV_SENDLEN, len)) == E_PSI_GEN_NONE) { op.write((char *)buff, len); total += len; if (cb && !cb(ptr, total)) res = E_PSI_FILE_CANCEL; } } while ((len > 0) && (res == E_PSI_GEN_NONE)); delete [] buff; fclose(handle); op.close(); return res; } Enum rfsv32:: copyFromPsion(const char *from, int fd, cpCallback_t cb) { Enum res; uint32_t handle; uint32_t len; uint32_t total = 0; if ((res = fopen(EPOC_OMODE_SHARE_READERS | EPOC_OMODE_BINARY, from, handle)) != E_PSI_GEN_NONE) return res; unsigned char *buff = new unsigned char[RFSV_SENDLEN]; do { if ((res = fread(handle, buff, RFSV_SENDLEN, len)) == E_PSI_GEN_NONE) { // FIXME: return UNIX errors from this method. ignore_value(write(fd, buff, len)); total += len; if (cb && !cb(NULL, total)) res = E_PSI_FILE_CANCEL; } } while ((len > 0) && (res == E_PSI_GEN_NONE)); delete [] buff; fclose(handle); return res; } Enum rfsv32:: copyToPsion(const char *from, const char *to, void *ptr, cpCallback_t cb) { uint32_t handle; Enum res; ifstream ip(from); if (!ip) return E_PSI_FILE_NXIST; res = fcreatefile(EPOC_OMODE_BINARY | EPOC_OMODE_SHARE_EXCLUSIVE | EPOC_OMODE_READ_WRITE, to, handle); if (res != E_PSI_GEN_NONE) { res = freplacefile(EPOC_OMODE_BINARY | EPOC_OMODE_SHARE_EXCLUSIVE | EPOC_OMODE_READ_WRITE, to, handle); if (res != E_PSI_GEN_NONE) return res; } unsigned char *buff = new unsigned char[RFSV_SENDLEN]; uint32_t total = 0; while (ip && !ip.eof() && (res == E_PSI_GEN_NONE)) { uint32_t len; ip.read((char *)buff, RFSV_SENDLEN); if ((res = fwrite(handle, buff, ip.gcount(), len)) == E_PSI_GEN_NONE) { total += len; if (cb && !cb(ptr, total)) res = E_PSI_FILE_CANCEL; } } fclose(handle); ip.close(); delete[]buff; return res; } Enum rfsv32:: copyOnPsion(const char *from, const char *to, void *ptr, cpCallback_t cb) { uint32_t handle_from; uint32_t handle_to; PlpDirent from_e; Enum res; if ((res = fgeteattr(from, from_e)) != E_PSI_GEN_NONE) return res; if ((res = fopen(EPOC_OMODE_SHARE_READERS | EPOC_OMODE_BINARY, from, handle_from)) != E_PSI_GEN_NONE) return res; res = fcreatefile(EPOC_OMODE_BINARY | EPOC_OMODE_SHARE_EXCLUSIVE | EPOC_OMODE_READ_WRITE, to, handle_to); if (res != E_PSI_GEN_NONE) { res = freplacefile(EPOC_OMODE_BINARY | EPOC_OMODE_SHARE_EXCLUSIVE | EPOC_OMODE_READ_WRITE, to, handle_to); if (res != E_PSI_GEN_NONE) { fclose(handle_from); return res; } } uint32_t total = 0; while (res == E_PSI_GEN_NONE) { bufferStore b; b.addDWord(RFSV_SENDLEN * 10); b.addDWord(handle_to); b.addDWord(handle_from); if (!sendCommand(READ_WRITE_FILE, b)) return E_PSI_FILE_DISC; res = getResponse(b); if (res != E_PSI_GEN_NONE) break; if (b.getLen() != 4) { res = E_PSI_GEN_FAIL; break; } uint32_t len = b.getDWord(0); total += len; if (cb && !cb(ptr, total)) res = E_PSI_FILE_CANCEL; if (len != (RFSV_SENDLEN * 10)) break; } fclose(handle_from); fclose(handle_to); if (res != E_PSI_GEN_NONE) remove(to); return res; } Enum rfsv32:: pathtest(const char * const name) { bufferStore a; uint32_t r; string n = convertSlash(name); a.addWord(n.size()); a.addString(n.c_str()); if (!sendCommand(PATH_TEST, a)) return E_PSI_FILE_DISC; return getResponse(a); } Enum rfsv32:: fsetsize(uint32_t handle, uint32_t size) { bufferStore a; a.addDWord(handle); a.addDWord(size); if (!sendCommand(SET_SIZE, a)) return E_PSI_FILE_DISC; return getResponse(a); } /* * Unix-like implementation off fseek with one * exception: If seeking beyond eof, the gap * contains garbage instead of zeroes. */ Enum rfsv32:: fseek(const uint32_t handle, const int32_t pos, const uint32_t mode, uint32_t &resultpos) { bufferStore a; Enum res; uint32_t savpos = 0; uint32_t calcpos = 0; int32_t mypos = pos; uint32_t realpos; /* seek-parameter for psion: dword position dword handle dword mode 1 = from start 2 = from current pos 3 = from end ??no more?? 4 = sense recpos ??no more?? 5 = set recpos ??no more?? 6 = text-rewind */ if ((mode < PSI_SEEK_SET) || (mode > PSI_SEEK_END)) return E_PSI_GEN_ARG; if ((mode == PSI_SEEK_CUR) && (mypos >= 0)) { /* get and save current position */ a.addDWord(0); a.addDWord(handle); a.addDWord(PSI_SEEK_CUR); if (!sendCommand(SEEK_FILE, a)) return E_PSI_FILE_DISC; if ((res = getResponse(a)) != E_PSI_GEN_NONE) return res; savpos = a.getDWord(0); if (mypos == 0) { resultpos = savpos; return res; } a.init(); } if ((mode == PSI_SEEK_END) && (mypos >= 0)) { /* get and save end position */ a.addDWord(0); a.addDWord(handle); a.addDWord(PSI_SEEK_END); if (!sendCommand(SEEK_FILE, a)) return E_PSI_FILE_DISC; if ((res = getResponse(a)) != E_PSI_GEN_NONE) return res; savpos = a.getDWord(0); if (mypos == 0) { resultpos = savpos; return res; } /* Expand file */ a.init(); a.addDWord(handle); a.addDWord(savpos + mypos); if (!sendCommand(SET_SIZE, a)) return E_PSI_FILE_DISC; if ((res = getResponse(a)) != E_PSI_GEN_NONE) return res; mypos = 0; a.init(); } /* Now the real seek */ a.addDWord(mypos); a.addDWord(handle); a.addDWord(mode); if (!sendCommand(SEEK_FILE, a)) return E_PSI_FILE_DISC; if ((res = getResponse(a)) != E_PSI_GEN_NONE) return res; realpos = a.getDWord(0); switch (mode) { case PSI_SEEK_SET: calcpos = mypos; break; case PSI_SEEK_CUR: calcpos = savpos + mypos; break; case PSI_SEEK_END: resultpos = realpos; return res; break; } if (calcpos > realpos) { /* Beyond end of file */ a.init(); a.addDWord(handle); a.addDWord(calcpos); if (!sendCommand(SET_SIZE, a)) return E_PSI_FILE_DISC; if ((res = getResponse(a)) != E_PSI_GEN_NONE) return res; a.addDWord(calcpos); a.addDWord(handle); a.addDWord(PSI_SEEK_SET); if (!sendCommand(SEEK_FILE, a)) return E_PSI_FILE_DISC; if ((res = getResponse(a)) != E_PSI_GEN_NONE) return res; realpos = a.getDWord(0); } resultpos = realpos; return res; } Enum rfsv32:: mkdir(const char *name) { bufferStore a; string n = convertSlash(name); if (n.find_last_of('\\') != (n.size() - 1)) n += '\\'; a.addWord(n.size()); a.addString(n.c_str()); if (!sendCommand(MK_DIR_ALL, a)) return E_PSI_FILE_DISC; return getResponse(a); } Enum rfsv32:: rmdir(const char *name) { bufferStore a; string n = convertSlash(name); if (n.find_last_of('\\') != (n.size() - 1)) n += '\\'; a.addWord(n.size()); a.addString(n.c_str()); if (!sendCommand(RM_DIR, a)) return E_PSI_FILE_DISC; return getResponse(a); } Enum rfsv32:: rename(const char *oldname, const char *newname) { bufferStore a; string on = convertSlash(oldname); string nn = convertSlash(newname); a.addWord(on.size()); a.addString(on.c_str()); a.addWord(nn.size()); a.addString(nn.c_str()); if (!sendCommand(RENAME, a)) return E_PSI_FILE_DISC; return getResponse(a); } Enum rfsv32:: remove(const char *name) { bufferStore a; string n = convertSlash(name); a.addWord(n.size()); a.addString(n.c_str()); if (!sendCommand(DELETE, a)) return E_PSI_FILE_DISC; return getResponse(a); } Enum rfsv32:: setVolumeName(const char drive , const char * const name) { bufferStore a; a.addDWord(toupper(drive) - 'A'); a.addWord(strlen(name)); a.addStringT(name); if (!sendCommand(SET_VOLUME_LABEL, a)) return E_PSI_FILE_DISC; return getResponse(a); } static enum rfsv::errs e2psi[] = { rfsv::E_PSI_FILE_DIRFULL, rfsv::E_PSI_GEN_POWER, rfsv::E_PSI_GEN_DIVIDE, rfsv::E_PSI_FILE_TOOBIG, rfsv::E_PSI_FILE_ABORT, rfsv::E_PSI_GEN_DESCR, rfsv::E_PSI_GEN_LIB, rfsv::E_PSI_FILE_NDISC, rfsv::E_PSI_FILE_DISC, rfsv::E_PSI_FILE_CONNECT, rfsv::E_PSI_FILE_RETRAN, rfsv::E_PSI_FILE_PARITY, rfsv::E_PSI_FILE_OVERRUN, rfsv::E_PSI_FILE_FRAME, rfsv::E_PSI_FILE_LINE, rfsv::E_PSI_FILE_NAME, rfsv::E_PSI_FILE_DRIVER, rfsv::E_PSI_FILE_FULL, rfsv::E_PSI_FILE_EOF, rfsv::E_PSI_GEN_FSYS, rfsv::E_PSI_FILE_WRITE, rfsv::E_PSI_FILE_LOCKED, rfsv::E_PSI_FILE_ACCESS, rfsv::E_PSI_FILE_CORRUPT, rfsv::E_PSI_FILE_UNKNOWN, rfsv::E_PSI_FILE_NOTREADY, rfsv::E_PSI_FILE_COMPLETION, rfsv::E_PSI_GEN_BUSY, rfsv::E_PSI_GEN_TERMINATED, rfsv::E_PSI_GEN_INUSE, rfsv::E_PSI_GEN_DIED, rfsv::E_PSI_FILE_DIR, rfsv::E_PSI_FILE_EXIST, rfsv::E_PSI_GEN_UNDER, rfsv::E_PSI_GEN_OVER, rfsv::E_PSI_FILE_HANDLE, rfsv::E_PSI_GEN_RANGE, rfsv::E_PSI_GEN_ARG, rfsv::E_PSI_GEN_NSUP, rfsv::E_PSI_GEN_NOMEMORY, rfsv::E_PSI_FILE_CANCEL, rfsv::E_PSI_GEN_FAIL, rfsv::E_PSI_FILE_NXIST, rfsv::E_PSI_GEN_NONE }; Enum rfsv32:: err2psierr(int32_t istatus) { if ((istatus > E_EPOC_NONE) || (istatus < E_EPOC_DIR_FULL)) { cerr << "FATAL: invalid error-code" << endl; cerr << "status: " << istatus << " " << hex << istatus << endl; return E_PSI_INTERNAL; } return e2psi[istatus - E_EPOC_DIR_FULL]; } /* * Translate EPOC attributes to standard attributes. */ uint32_t rfsv32:: attr2std(const uint32_t attr) { long res = 0; // Common attributes if (attr & EPOC_ATTR_RONLY) res |= PSI_A_RDONLY; if (attr & EPOC_ATTR_HIDDEN) res |= PSI_A_HIDDEN; if (attr & EPOC_ATTR_SYSTEM) res |= PSI_A_SYSTEM; if (attr & EPOC_ATTR_DIRECTORY) res |= PSI_A_DIR; if (attr & EPOC_ATTR_ARCHIVE) res |= PSI_A_ARCHIVE; if (attr & EPOC_ATTR_VOLUME) res |= PSI_A_VOLUME; // EPOC-specific if (attr & EPOC_ATTR_NORMAL) res |= PSI_A_NORMAL; if (attr & EPOC_ATTR_TEMPORARY) res |= PSI_A_TEMP; if (attr & EPOC_ATTR_COMPRESSED) res |= PSI_A_COMPRESSED; // Do what we can for SIBO res |= PSI_A_READ; return res; } /* * Translate standard attributes to EPOC attributes. */ uint32_t rfsv32:: std2attr(const uint32_t attr) { long res = 0; // Common attributes if (attr & PSI_A_RDONLY) res |= EPOC_ATTR_RONLY; if (attr & PSI_A_HIDDEN) res |= EPOC_ATTR_HIDDEN; if (attr & PSI_A_SYSTEM) res |= EPOC_ATTR_SYSTEM; if (attr & PSI_A_DIR) res |= EPOC_ATTR_DIRECTORY; if (attr & PSI_A_ARCHIVE) res |= EPOC_ATTR_ARCHIVE; if (attr & PSI_A_VOLUME) res |= EPOC_ATTR_VOLUME; // EPOC-specific if (attr & PSI_A_NORMAL) res |= EPOC_ATTR_NORMAL; if (attr & PSI_A_TEMP) res |= EPOC_ATTR_TEMPORARY; if (attr & PSI_A_COMPRESSED) res |= EPOC_ATTR_COMPRESSED; return res; } plptools-1.0.26/lib/rfsv32.h000066400000000000000000000152111504470754400155320ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999 Philip Proudman * Copyright (C) 1999-2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #ifndef _RFSV32_H_ #define _RFSV32_H_ #include #include class rfsvfactory; /** * This is the implementation of the @ref rfsv protocol for * Psion series 5 (EPOC) variant. You normally never create * objects of this class directly. Thus the constructor is * private. Use @ref rfsvfactory for creating an instance of * @ref rfsv . For a complete documentation, see @ref rfsv . */ class rfsv32 : public rfsv { /** * rfsvfactory may call our constructor. */ friend class rfsvfactory; public: Enum dir(const char * const, PlpDir &); Enum dircount(const char * const, uint32_t &); Enum copyFromPsion(const char * const, const char * const, void *, cpCallback_t); Enum copyFromPsion(const char *from, int fd, cpCallback_t cb); Enum copyToPsion(const char * const, const char * const, void *, cpCallback_t); Enum copyOnPsion(const char * const, const char * const, void *, cpCallback_t); Enum pathtest(const char * const); Enum mkdir(const char * const); Enum rmdir(const char * const); Enum remove(const char * const); Enum rename(const char * const, const char * const); Enum mktemp(uint32_t &, std::string &); Enum fgeteattr(const char * const, PlpDirent &); Enum fgetattr(const char * const, uint32_t &); Enum fsetattr(const char * const, const uint32_t, const uint32_t); Enum fgetmtime(const char * const, PsiTime &); Enum fsetmtime(const char * const, PsiTime const); Enum fopen(const uint32_t, const char * const, uint32_t &); Enum fcreatefile(const uint32_t, const char * const, uint32_t &); Enum freplacefile(const uint32_t, const char * const, uint32_t &); Enum fseek(const uint32_t, const int32_t, const uint32_t, uint32_t &); Enum fread(const uint32_t, unsigned char * const, const uint32_t, uint32_t &); Enum fwrite(const uint32_t, const unsigned char * const, const uint32_t, uint32_t &); Enum fsetsize(uint32_t, uint32_t); Enum fclose(const uint32_t); Enum devlist(uint32_t &); Enum devinfo(const char, PlpDrive&); Enum opendir(const uint32_t, const char * const, rfsvDirhandle &); Enum readdir(rfsvDirhandle &, PlpDirent &); Enum closedir(rfsvDirhandle &); Enum setVolumeName(const char, const char * const); uint32_t opMode(const uint32_t); int getProtocolVersion() { return 5; } private: enum file_attrib { EPOC_ATTR_RONLY = 0x0001, EPOC_ATTR_HIDDEN = 0x0002, EPOC_ATTR_SYSTEM = 0x0004, EPOC_ATTR_DIRECTORY = 0x0010, EPOC_ATTR_ARCHIVE = 0x0020, EPOC_ATTR_VOLUME = 0x0040, EPOC_ATTR_NORMAL = 0x0080, EPOC_ATTR_TEMPORARY = 0x0100, EPOC_ATTR_COMPRESSED = 0x0800, EPOC_ATTR_MASK = 0x09f7, /* All of the above */ EPOC_ATTR_GETUID = 0x10000000 /* Deliver UIDs on dir listing */ }; enum open_mode { EPOC_OMODE_SHARE_EXCLUSIVE = 0x0000, EPOC_OMODE_SHARE_READERS = 0x0001, EPOC_OMODE_SHARE_ANY = 0x0002, EPOC_OMODE_BINARY = 0x0000, EPOC_OMODE_TEXT = 0x0020, EPOC_OMODE_READ_WRITE = 0x0200 }; enum epoc_errs { E_EPOC_NONE = 0, E_EPOC_NOT_FOUND = -1, E_EPOC_GENERAL = -2, E_EPOC_CANCEL = -3, E_EPOC_NO_MEMORY = -4, E_EPOC_NOT_SUPPORTED = -5, E_EPOC_ARGUMENT = -6, E_EPOC_TOTAL_LOSS_OF_PRECISION = -7, E_EPOC_BAD_HANDLE = -8, E_EPOC_OVERFLOW = -9, E_EPOC_UNDERFLOW = -10, E_EPOC_ALREADY_EXISTS = -11, E_EPOC_PATH_NOT_FOUND = -12, E_EPOC_DIED = -13, E_EPOC_IN_USE = -14, E_EPOC_SERVER_TERMINATED = -15, E_EPOC_SERVER_BUSY = -16, E_EPOC_COMPLETION = -17, E_EPOC_NOT_READY = -18, E_EPOC_UNKNOWN = -19, E_EPOC_CORRUPT = -20, E_EPOC_ACCESS_DENIED = -21, E_EPOC_LOCKED = -22, E_EPOC_WRITE = -23, E_EPOC_DISMOUNTED = -24, E_EPOC_EoF = -25, E_EPOC_DISK_FULL = -26, E_EPOC_BAD_DRIVER = -27, E_EPOC_BAD_NAME = -28, E_EPOC_COMMS_LINE_FAIL = -29, E_EPOC_COMMS_FRAME = -30, E_EPOC_COMMS_OVERRUN = -31, E_EPOC_COMMS_PARITY = -32, E_EPOC_TIMEOUT = -33, E_EPOC_COULD_NOT_CONNECT = -34, E_EPOC_COULD_NOT_DISCONNECT = -35, E_EPOC_DISCONNECTED = -36, E_EPOC_BAD_LIBRARY_ENTRY_POINT = -37, E_EPOC_BAD_DESCRIPTOR = -38, E_EPOC_ABORT = -39, E_EPOC_TOO_BIG = -40, E_EPOC_DIVIDE_BY_ZERO = -41, E_EPOC_BAD_POWER = -42, E_EPOC_DIR_FULL = -43 }; enum commands { CLOSE_HANDLE = 0x01, OPEN_DIR = 0x10, READ_DIR = 0x12, GET_DRIVE_LIST = 0x13, DRIVE_INFO = 0x14, SET_VOLUME_LABEL = 0x15, OPEN_FILE = 0x16, TEMP_FILE = 0x17, READ_FILE = 0x18, WRITE_FILE = 0x19, SEEK_FILE = 0x1a, DELETE = 0x1b, REMOTE_ENTRY = 0x1c, FLUSH = 0x1d, SET_SIZE = 0x1e, RENAME = 0x1f, MK_DIR_ALL = 0x20, RM_DIR = 0x21, SET_ATT = 0x22, ATT = 0x23, SET_MODIFIED = 0x24, MODIFIED = 0x25, SET_SESSION_PATH = 0x26, SESSION_PATH = 0x27, READ_WRITE_FILE = 0x28, CREATE_FILE = 0x29, REPLACE_FILE = 0x2a, PATH_TEST = 0x2b, LOCK = 0x2d, UNLOCK = 0x2e, OPEN_DIR_UID = 0x2f, DRIVE_NAME = 0x30, SET_DRIVE_NAME = 0x31, REPLACE = 0x32 }; /** * Private constructor. Shall be called by * rfsvfactory only. */ rfsv32(ppsocket *); Enum err2psierr(int32_t); Enum fopendir(const uint32_t, const char *, uint32_t &); uint32_t attr2std(const uint32_t); uint32_t std2attr(const uint32_t); // Communication bool sendCommand(enum commands, bufferStore &); Enum getResponse(bufferStore &); }; #endif plptools-1.0.26/lib/rfsvfactory.cc000066400000000000000000000056461504470754400171260ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999 Matt J. Gumbley * * 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 * along with this program; if not, see . * */ #include "config.h" #include "rfsv.h" #include "rfsv16.h" #include "rfsv32.h" #include "rfsvfactory.h" #include "bufferstore.h" #include "ppsocket.h" #include "Enum.h" #include #include using namespace std; ENUM_DEFINITION_BEGIN(rfsvfactory::errs, rfsvfactory::FACERR_NONE) stringRep.add(rfsvfactory::FACERR_NONE, N_("no error")); stringRep.add(rfsvfactory::FACERR_COULD_NOT_SEND, N_("could not send version request")); stringRep.add(rfsvfactory::FACERR_AGAIN, N_("try again")); stringRep.add(rfsvfactory::FACERR_NOPSION, N_("no EPOC device connected")); stringRep.add(rfsvfactory::FACERR_PROTVERSION, N_("wrong protocol version")); stringRep.add(rfsvfactory::FACERR_NORESPONSE, N_("no response from ncpd")); ENUM_DEFINITION_END(rfsvfactory::errs) rfsvfactory::rfsvfactory(ppsocket *_skt) : serNum(0) { err = FACERR_NONE; skt = _skt; } rfsv * rfsvfactory::create(bool reconnect) { // skt is connected to the ncp daemon, which will have (hopefully) seen // an INFO exchange, where the protocol version of the remote Psion was // sent, and noted. We have to ask the ncp daemon which protocol it saw, // so we can instantiate the correct RFSV protocol handler for the // caller. We announce ourselves to the NCP daemon, and the relevant // RFSV module will also announce itself. bufferStore a; err = FACERR_NONE; a.addStringT("NCP$INFO"); if (!skt->sendBufferStore(a)) { if (!reconnect) err = FACERR_COULD_NOT_SEND; else { skt->closeSocket(); serNum = 0; skt->reconnect(); err = FACERR_AGAIN; } return NULL; } if (skt->getBufferStore(a) == 1) { if (a.getLen() > 8 && !strncmp(a.getString(), "Series 3", 8)) { return new rfsv16(skt); } else if (a.getLen() > 8 && !strncmp(a.getString(), "Series 5", 8)) { return new rfsv32(skt); } if ((a.getLen() > 8) && !strncmp(a.getString(), "No Psion", 8)) { skt->closeSocket(); serNum = 0; skt->reconnect(); err = FACERR_NOPSION; return NULL; } // Invalid protocol version err = FACERR_PROTVERSION; } else err = FACERR_NORESPONSE; return NULL; } plptools-1.0.26/lib/rfsvfactory.h000066400000000000000000000042011504470754400167520ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999 Philip Proudman * Copyright (C) 1999 Matt J. Gumbley * Copyright (C) 1999-2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #ifndef _RFSVFACTORY_H_ #define _RFSVFACTORY_H_ #include class ppsocket; /** * A factory for automatically instantiating the correct * @ref rfsv protocol variant depending on the connected Psion. */ class rfsvfactory { public: /** * The known errors which can happen during @ref create . */ enum errs { FACERR_NONE = 0, FACERR_COULD_NOT_SEND = 1, FACERR_AGAIN = 2, FACERR_NOPSION = 3, FACERR_PROTVERSION = 4, FACERR_NORESPONSE = 5 }; /** * Constructs a rfsvfactory. * * @param skt The socket to be used for connecting * to the ncpd daemon. */ rfsvfactory(ppsocket * skt); /** * Creates a new @ref rfsv instance. * * @param reconnect Set to true, if automatic reconnect * should be performed on failure. * * @returns A pointer to a newly created rfsv instance or * NULL on failure. */ virtual rfsv * create(bool); /** * Retrieve an error code. * * @returns The error code, in case @ref create has * failed, 0 otherwise. */ virtual Enum getError() { return err; } private: /** * The socket to be used for connecting to the * ncpd daemon. */ ppsocket *skt; int serNum; Enum err; }; #endif plptools-1.0.26/lib/rpcs.cc000066400000000000000000000302751504470754400155210ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999-2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #include "config.h" #include "rpcs.h" #include "bufferstore.h" #include "ppsocket.h" #include "bufferarray.h" #include "psiprocess.h" #include "Enum.h" #include #include #include using namespace std; ENUM_DEFINITION_BEGIN(rpcs::machs, rpcs::PSI_MACH_UNKNOWN) stringRep.add(rpcs::PSI_MACH_UNKNOWN, N_("Unknown device")); stringRep.add(rpcs::PSI_MACH_PC, N_("PC")); stringRep.add(rpcs::PSI_MACH_MC, N_("MC")); stringRep.add(rpcs::PSI_MACH_HC, N_("HC")); stringRep.add(rpcs::PSI_MACH_S3, N_("Series 3")); stringRep.add(rpcs::PSI_MACH_S3A, N_("Series 3a, 3c or 3mx")); stringRep.add(rpcs::PSI_MACH_WORKABOUT, N_("Workabout")); stringRep.add(rpcs::PSI_MACH_SIENNA, N_("Sienna")); stringRep.add(rpcs::PSI_MACH_S3C, N_("Series 3c")); stringRep.add(rpcs::PSI_MACH_S5, N_("Series 5")); stringRep.add(rpcs::PSI_MACH_WINC, N_("WinC")); ENUM_DEFINITION_END(rpcs::machs) ENUM_DEFINITION_BEGIN(rpcs::batterystates, rpcs::PSI_BATT_DEAD) stringRep.add(rpcs::PSI_BATT_DEAD, N_("Empty")); stringRep.add(rpcs::PSI_BATT_VERYLOW, N_("Very Low")); stringRep.add(rpcs::PSI_BATT_LOW, N_("Low")); stringRep.add(rpcs::PSI_BATT_GOOD, N_("Good")); ENUM_DEFINITION_END(rpcs::batterystates) ENUM_DEFINITION_BEGIN(rpcs::languages, rpcs::PSI_LANG_TEST) stringRep.add(rpcs::PSI_LANG_TEST, N_("Test")); stringRep.add(rpcs::PSI_LANG_en_GB, N_("English")); stringRep.add(rpcs::PSI_LANG_de_DE, N_("German")); stringRep.add(rpcs::PSI_LANG_fr_FR, N_("French")); stringRep.add(rpcs::PSI_LANG_es_ES, N_("Spanish")); stringRep.add(rpcs::PSI_LANG_it_IT, N_("Italian")); stringRep.add(rpcs::PSI_LANG_sv_SE, N_("Swedish")); stringRep.add(rpcs::PSI_LANG_da_DK, N_("Danish")); stringRep.add(rpcs::PSI_LANG_no_NO, N_("Norwegian")); stringRep.add(rpcs::PSI_LANG_fi_FI, N_("Finnish")); stringRep.add(rpcs::PSI_LANG_en_US, N_("American")); stringRep.add(rpcs::PSI_LANG_fr_CH, N_("Swiss French")); stringRep.add(rpcs::PSI_LANG_de_CH, N_("Swiss German")); stringRep.add(rpcs::PSI_LANG_pt_PT, N_("Portugese")); stringRep.add(rpcs::PSI_LANG_tr_TR, N_("Turkish")); stringRep.add(rpcs::PSI_LANG_is_IS, N_("Icelandic")); stringRep.add(rpcs::PSI_LANG_ru_RU, N_("Russian")); stringRep.add(rpcs::PSI_LANG_hu_HU, N_("Hungarian")); stringRep.add(rpcs::PSI_LANG_nl_NL, N_("Dutch")); stringRep.add(rpcs::PSI_LANG_nl_BE, N_("Belgian Flemish")); stringRep.add(rpcs::PSI_LANG_en_AU, N_("Australian")); stringRep.add(rpcs::PSI_LANG_fr_BE, N_("Belgish French")); stringRep.add(rpcs::PSI_LANG_de_AT, N_("Austrian")); stringRep.add(rpcs::PSI_LANG_en_NZ, N_("New Zealand English")); stringRep.add(rpcs::PSI_LANG_fr_CA, N_("Canadian French")); stringRep.add(rpcs::PSI_LANG_cs_CZ, N_("Czech")); stringRep.add(rpcs::PSI_LANG_sk_SK, N_("Slovak")); stringRep.add(rpcs::PSI_LANG_pl_PL, N_("Polish")); stringRep.add(rpcs::PSI_LANG_sl_SI, N_("Slovenian")); ENUM_DEFINITION_END(rpcs::languages) rpcs::~rpcs() { skt->closeSocket(); } // // public common API // void rpcs:: reconnect(void) { skt->reconnect(); reset(); } void rpcs:: reset(void) { bufferStore a; mtCacheS5mx = 0; status = rfsv::E_PSI_FILE_DISC; a.addStringT(getConnectName()); if (skt->sendBufferStore(a)) { if (skt->getBufferStore(a) == 1) { if (!strcmp(a.getString(0), "Ok")) status = rfsv::E_PSI_GEN_NONE; } } } Enum rpcs:: getStatus(void) { return status; } const char *rpcs:: getConnectName(void) { return "SYS$RPCS"; } // // protected internals // bool rpcs:: sendCommand(enum commands cc, bufferStore & data) { if (status == rfsv::E_PSI_FILE_DISC) { reconnect(); if (status == rfsv::E_PSI_FILE_DISC) return false; } bool result; bufferStore a; a.addByte(cc); a.addBuff(data); result = skt->sendBufferStore(a); if (!result) { reconnect(); result = skt->sendBufferStore(a); if (!result) status = rfsv::E_PSI_FILE_DISC; } return result; } Enum rpcs:: getResponse(bufferStore & data, bool statusIsFirstByte) { Enum ret; if (skt->getBufferStore(data) == 1) { if (statusIsFirstByte) { ret = (enum rfsv::errs)((char)data.getByte(0)); data.discardFirstBytes(1); } else { int l = data.getLen(); if (l > 0) { ret = (enum rfsv::errs)((char)data.getByte(data.getLen() - 1)); data.init((const unsigned char *)data.getString(), l - 1); } else ret = rfsv::E_PSI_GEN_FAIL; } return ret; } else status = rfsv::E_PSI_FILE_DISC; return status; } // // APIs, identical on SIBO and EPOC // Enum rpcs:: getNCPversion(int &major, int &minor) { Enum res; bufferStore a; if (!sendCommand(QUERY_NCP, a)) return rfsv::E_PSI_FILE_DISC; if ((res = getResponse(a, true)) != rfsv::E_PSI_GEN_NONE) return res; if (a.getLen() != 2) return rfsv::E_PSI_GEN_FAIL; major = a.getByte(0); minor = a.getByte(1); return res; } Enum rpcs:: execProgram(const char *program, const char *args) { bufferStore a; a.addStringT(program); int l = strlen(program); for (int i = 127; i > l; i--) a.addByte(0); /** * This is a hack for the jotter app on mx5 pro. (and probably others) * Jotter seems to read its arguments one char past normal apps. * Without this hack, The Drive-Character gets lost. Other apps don't * seem to be hurt by the additional blank. */ a.addByte(strlen(args)+1); a.addByte(' '); a.addStringT(args); if (!sendCommand(EXEC_PROG, a)) return rfsv::E_PSI_FILE_DISC; return getResponse(a, true); } Enum rpcs:: stopProgram(const char *program) { bufferStore a; a.addStringT(program); if (!sendCommand(STOP_PROG, a)) return rfsv::E_PSI_FILE_DISC; return getResponse(a, true); } Enum rpcs:: queryProgram(const char *program) { bufferStore a; a.addStringT(program); if (!sendCommand(QUERY_PROG, a)) return rfsv::E_PSI_FILE_DISC; return getResponse(a, true); } Enum rpcs:: queryPrograms(processList &ret) { bufferStore a; const char *drives; const char *dptr; bool anySuccess = false; Enum res; // First, check how many drives we need to query a.addStringT("M:"); // Drive M only exists on a SIBO if (!sendCommand(rpcs::GET_UNIQUEID, a)) return rfsv::E_PSI_FILE_DISC; if (getResponse(a, false) == rfsv::E_PSI_GEN_NONE) // A SIBO; Must query all possible drives drives = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; else // A Series 5; Query of C is sufficient drives = "C"; dptr = drives; ret.clear(); if ((mtCacheS5mx & 4) == 0) { Enum tmp; if (getMachineType(tmp) != rfsv::E_PSI_GEN_NONE) return rfsv::E_PSI_GEN_FAIL; } if ((mtCacheS5mx & 9) == 1) { machineInfo tmp; if (getMachineInfo(tmp) == rfsv::E_PSI_FILE_DISC) return rfsv::E_PSI_FILE_DISC; } bool s5mx = (mtCacheS5mx == 15); while (*dptr) { a.init(); a.addByte(*dptr); if (!sendCommand(rpcs::QUERY_DRIVE, a)) return rfsv::E_PSI_FILE_DISC; if (getResponse(a, false) == rfsv::E_PSI_GEN_NONE) { anySuccess = true; int l = a.getLen(); while (l > 0) { const char *s; char *p; int pid; int sl; s = a.getString(0); sl = strlen(s) + 1; l -= sl; a.discardFirstBytes(sl); if ((p = strstr((char *)s, ".$"))) { *p = '\0'; p += 2; sscanf(p, "%d", &pid); } else pid = 0; PsiProcess proc(pid, s, a.getString(0), s5mx); ret.push_back(proc); sl = strlen(a.getString(0)) + 1; l -= sl; a.discardFirstBytes(sl); } } dptr++; } if (anySuccess && !ret.empty()) for (processList::iterator i = ret.begin(); i != ret.end(); i++) { string cmdline; if (getCmdLine(i->getProcId(), cmdline) == rfsv::E_PSI_GEN_NONE) i->setArgs(cmdline + " " + i->getArgs()); } return anySuccess ? rfsv::E_PSI_GEN_NONE : rfsv::E_PSI_GEN_FAIL; } Enum rpcs:: formatOpen(const char drive, int &handle, int &count) { Enum res; bufferStore a; a.addByte(toupper(drive)); a.addByte(':'); a.addByte(0); if (!sendCommand(FORMAT_OPEN, a)) return rfsv::E_PSI_FILE_DISC; if ((res = getResponse(a, true)) != rfsv::E_PSI_GEN_NONE) return res; if (a.getLen() != 4) return rfsv::E_PSI_GEN_FAIL; handle = a.getWord(0); count = a.getWord(2); return res; } Enum rpcs:: formatRead(int handle) { bufferStore a; a.addWord(handle); if (!sendCommand(FORMAT_READ, a)) return rfsv::E_PSI_FILE_DISC; return getResponse(a, true); } Enum rpcs:: getUniqueID(const char *device, long &id) { Enum res; bufferStore a; a.addStringT(device); if (!sendCommand(GET_UNIQUEID, a)) return rfsv::E_PSI_FILE_DISC; if ((res = getResponse(a, true)) != rfsv::E_PSI_GEN_NONE) return res; if (a.getLen() != 4) return rfsv::E_PSI_GEN_FAIL; id = a.getDWord(0); return res; } Enum rpcs:: getOwnerInfo(bufferArray &owner) { Enum res; bufferStore a; if (!sendCommand(GET_OWNERINFO, a)) return rfsv::E_PSI_FILE_DISC; if ((res = (enum rfsv::errs)getResponse(a, true)) != rfsv::E_PSI_GEN_NONE) return res; a.addByte(0); string s = a.getString(0); owner.clear(); int p = 0; int l; while ((l = s.find('\006', p)) != s.npos) { bufferStore b; b.addStringT(s.substr(p, l - p).c_str()); owner += b; p = l + 1; } if (s.substr(p).length()) { bufferStore b; b.addStringT(s.substr(p).c_str()); owner += b; } return res; } Enum rpcs:: getMachineType(Enum &type) { Enum res; bufferStore a; if (!sendCommand(GET_MACHINETYPE, a)) return rfsv::E_PSI_FILE_DISC; if ((res = getResponse(a, true)) != rfsv::E_PSI_GEN_NONE) return res; if (a.getLen() != 2) return rfsv::E_PSI_GEN_FAIL; type = (enum machs)a.getWord(0); mtCacheS5mx |= 4; if (res == rfsv::E_PSI_GEN_NONE) { if (type == rpcs::PSI_MACH_S5) mtCacheS5mx |= 1; } return res; } Enum rpcs:: fuser(const char *name, char *buf, int maxlen) { Enum res; bufferStore a; char *p; a.addStringT(name); if (!sendCommand(FUSER, a)) return rfsv::E_PSI_FILE_DISC; if ((res = getResponse(a, true)) != rfsv::E_PSI_GEN_NONE) return res; strncpy(buf, a.getString(0), maxlen - 1); while ((p = strchr(buf, 6))) *p = '\0'; return res; } Enum rpcs:: quitServer(void) { bufferStore a; if (!sendCommand(QUIT_SERVER, a)) return rfsv::E_PSI_FILE_DISC; return getResponse(a, true); } plptools-1.0.26/lib/rpcs.h000066400000000000000000000335571504470754400153710ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999-2002 Fritz Elfert * * 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 * along with this program; if not, see . * */ #ifndef _RPCS_H_ #define _RPCS_H_ #include #include #include #include #include class ppsocket; class bufferStore; class bufferArray; typedef std::vector processList; /** * Remote procedure call services via PLP * * rpcs provides an interface for communicating * with the Psion's remote procedure call * service. The generic facilities for both, * EPOC and SIBO are implemented here, while * the facilities, unique to each of those * variants are implemented in * @ref rpcs32 or @ref rpcs16 respectively. * These normally are instantiated by using * @ref rpcsfactory . * * @author Fritz Elfert */ class rpcs { public: /** * The known machine types. */ enum machs { PSI_MACH_UNKNOWN = 0, PSI_MACH_PC = 1, PSI_MACH_MC = 2, PSI_MACH_HC = 3, PSI_MACH_S3 = 4, PSI_MACH_S3A = 5, PSI_MACH_WORKABOUT = 6, PSI_MACH_SIENNA = 7, PSI_MACH_S3C = 8, PSI_MACH_S5 = 32, PSI_MACH_WINC = 33 // TODO: Code for 5mx }; /** * The known interface languages. */ enum languages { PSI_LANG_TEST = 0, PSI_LANG_en_GB = 1, PSI_LANG_fr_FR = 2, PSI_LANG_de_DE = 3, PSI_LANG_es_ES = 4, PSI_LANG_it_IT = 5, PSI_LANG_sv_SE = 6, PSI_LANG_da_DK = 7, PSI_LANG_no_NO = 8, PSI_LANG_fi_FI = 9, PSI_LANG_en_US = 10, PSI_LANG_fr_CH = 11, PSI_LANG_de_CH = 12, PSI_LANG_pt_PT = 13, PSI_LANG_tr_TR = 14, PSI_LANG_is_IS = 15, PSI_LANG_ru_RU = 16, PSI_LANG_hu_HU = 17, PSI_LANG_nl_NL = 18, PSI_LANG_nl_BE = 19, PSI_LANG_en_AU = 20, PSI_LANG_fr_BE = 21, PSI_LANG_de_AT = 22, PSI_LANG_en_NZ = 23, PSI_LANG_fr_CA = 24, PSI_LANG_cs_CZ = 25, PSI_LANG_sk_SK = 26, PSI_LANG_pl_PL = 27, PSI_LANG_sl_SI = 28 }; /** * The known battery states. */ enum batterystates { PSI_BATT_DEAD = 0, PSI_BATT_VERYLOW = 1, PSI_BATT_LOW = 2, PSI_BATT_GOOD = 3 }; /** * This struct holds the data returned * by @ref rpcs::getMachineInfo. */ typedef struct machineInfo_t { Enum machineType; char machineName[17]; unsigned long long machineUID; unsigned long countryCode; Enum uiLanguage; unsigned short romMajor; unsigned short romMinor; unsigned short romBuild; unsigned long romSize; bool romProgrammable; unsigned long ramSize; unsigned long ramFree; unsigned long ramMaxFree; unsigned long ramDiskSize; unsigned long registrySize; unsigned long displayWidth; unsigned long displayHeight; psi_timeval time; psi_timezone tz; psi_timeval mainBatteryInsertionTime; Enum mainBatteryStatus; psi_timeval mainBatteryUsedTime; unsigned long mainBatteryCurrent; unsigned long mainBatteryUsedPower; unsigned long mainBatteryVoltage; unsigned long mainBatteryMaxVoltage; Enum backupBatteryStatus; unsigned long backupBatteryVoltage; unsigned long backupBatteryMaxVoltage; psi_timeval externalPowerUsedTime; bool externalPower; } machineInfo; /** * Virtual destructor. */ virtual ~rpcs(); /** * Initializes a connection to the remote * machine. */ void reset(); /** * Attempts to re-establish a remote * connection by first closing the socket, * then connecting again to the ncpd daemon * and finally calling @ref reset. */ void reconnect(); /** * Retrieves the current status of the * connection. * * @returns The connection status. */ Enum getStatus(); /** * Retrieves the version of the NCP protocol * on the remote side. * * This function is working with both SIBO and EPOC * devices. * * @param major The major part of the NCP version. * Valid only if returned with no error. * @param minor The minor part of the NCP version. * Valid only if returned with no error. * * @returns A psion error code. 0 = Ok. */ Enum getNCPversion(int &major, int &minor); /** * Starts execution of a program on the remote machine. * * This function is working with both SIBO and EPOC * devices. * * @param program The full path of the executable * on the remote machine * @param args The arguments for this program, separated * by space. * * @returns A psion error code. 0 = Ok. */ Enum execProgram(const char *program, const char *args); /** * Requests termination of a program running on the * remote machine. * * This function is working with both SIBO and EPOC * devices. * * @param program * * @returns A psion error code. 0 = Ok. */ Enum stopProgram(const char *); Enum queryProgram(const char *); /** * Starts formatting a drive. * * This function is working with both SIBO and EPOC * devices. After calling formatOpen, formatRead should * be called n times with the returned handle where n is * the value of the returned parameter count. * * @param drive The drive character to format (e.g: 'C', 'D' etc). * @param handle The handle for calling formatRead is returned here. * @param count The number of required calls to formatRead is returned * here. * * @returns A psion error code. 0 = Ok. */ Enum formatOpen(const char drive, int &handle, int &count); /** * Continues a running format. * * This function is working with both SIBO and EPOC * devices. Call this function with the handle, returned by formatOpen. * * @returns A psion error code. 0 = Ok. */ Enum formatRead(int handle); Enum getUniqueID(const char *, long &); /** * Retrieve owner information of the remote machine. * * This function is working with both SIBO and EPOC * devices. * * @param owner A bufferArray, containing the lines * of the owner info upon return. * * @returns A psion error code. 0 = Ok. */ Enum getOwnerInfo(bufferArray &); /** * Retrieves the type of machine on the remote side * as defined in @ref #machs. * * This function is working with both SIBO and EPOC * devices * * @param type The code describing the type of machine * on the remote side is stored here on return. * * @returns A psion error code. 0 = Ok. */ Enum getMachineType(Enum &type); /** * Retrieves the name of a process, having a * given file opened on the remote side. * * This function is working with both SIBO and EPOC * devices * * @param name The full path of a file to be checked * for being used by a program. * @param buf A buffer which gets filled with the * program's name. * @param maxlen The maximum capacity of the buffer. */ Enum fuser(const char *name, char *buf, int maxlen); /** * Requests the remote server to terminate. * * This function is working with both SIBO and EPOC * devices. There is usually no need to call this * function, because the remote server is automatically * stopped on disconnect. * * @returns A psion error code. 0 = Ok. */ Enum quitServer(void); /** * Retrieves a list of all running Programs. * * This function works with both SIBO and EPOC. * * @param ret The list of currently running processes is returned here. * * @returns A psion error code. 0 = Ok. */ Enum queryPrograms(processList &ret); /** * Retrieves the command line of a running process. * * This function works with both SIBO and EPOC. * Note: @ref rfsv::getPrograms calls this method internally and sets * the args member of @ref PsiProcess , so you usually don't have to call * this method yourself. * * @param process Name of process. Format: processname.$pid * @param ret The program name and arguments are returned here. * * @return Psion error code. 0 = Ok. */ virtual Enum getCmdLine(const char *process, std::string &ret) = 0; /** * Retrieve general Information about the connected * machine. * * This function works with EPOC only. Using it with SIBO * machines, returns always an error code E_PSI_NOT_SIBO. * * @param machineInfo The struct holding all information on return. * * @returns Psion error code. 0 = Ok. */ virtual Enum getMachineInfo(machineInfo &) { return rfsv::E_PSI_NOT_SIBO;} /** * Release an rpcs handle. * * This function works with EPOC only. Using it with SIBO * machines, returns always an error code E_PSI_NOT_SIBO. * It releases a handle, obtained by a previous call to * @ref rpcs::configOpen . * * @param handle The handle to close. * * @returns A psion error code. 0 = Ok. */ virtual Enum closeHandle(uint32_t handle) { return rfsv::E_PSI_NOT_SIBO; } virtual Enum regOpenIter(uint32_t, char *, uint16_t &) { return rfsv::E_PSI_NOT_SIBO;} virtual Enum regReadIter(uint16_t) { return rfsv::E_PSI_NOT_SIBO;} virtual Enum regWrite(void) { return rfsv::E_PSI_NOT_SIBO;} virtual Enum regRead(void) { return rfsv::E_PSI_NOT_SIBO;} virtual Enum regDelete(void) { return rfsv::E_PSI_NOT_SIBO;} virtual Enum setTime(time_t time) { return rfsv::E_PSI_NOT_SIBO;} /** * Read from Series 5 scratch RAM * * This function works with EPOC only. Using it with SIBO * machines, returns always an error code E_PSI_NOT_SIBO. * It reads raw data from the scratch RAM of the Series 5. * * @param size The amount of data to be read. * @param data The content of the scratch RAM is returned here. * * @returns A psion error code. 0 = Ok. */ virtual Enum configRead(uint32_t size, bufferStore &data) { return rfsv::E_PSI_NOT_SIBO; } /** * Write to Series 5 scratch RAM * * This function works with EPOC only. Using it with SIBO * machines, returns always an error code E_PSI_NOT_SIBO. * It writes raw data to the scatch RAM of the Series 5. * * @param data The data to be written to the scratch RAM. * * @returns A psion error code. 0 = Ok. */ virtual Enum configWrite(bufferStore data) { return rfsv::E_PSI_NOT_SIBO;} virtual Enum queryOpen(void) { return rfsv::E_PSI_NOT_SIBO;} virtual Enum queryRead(void) { return rfsv::E_PSI_NOT_SIBO;} protected: /** * The socket, used for communication * with ncpd. */ ppsocket *skt; /** * The current status of the connection. */ Enum status; /** * The possible commands. */ enum commands { QUERY_NCP = 0x00, EXEC_PROG = 0x01, QUERY_DRIVE = 0x02, STOP_PROG = 0x03, QUERY_PROG = 0x04, FORMAT_OPEN = 0x05, FORMAT_READ = 0x06, GET_UNIQUEID = 0x07, GET_OWNERINFO = 0x08, GET_MACHINETYPE = 0x09, GET_CMDLINE = 0x0a, FUSER = 0x0b, GET_MACHINE_INFO = 0x64, CLOSE_HANDLE = 0x65, REG_OPEN_ITER = 0x66, REG_READ_ITER = 0x67, REG_WRITE = 0x68, REG_READ = 0x69, REG_DELETE = 0x6a, SET_TIME = 0x6b, CONFIG_OPEN = 0x6c, CONFIG_READ = 0x6d, CONFIG_WRITE = 0x6e, QUERY_OPEN = 0x6f, QUERY_READ = 0x70, QUIT_SERVER = 0xff }; /** * Flag: getMachineType and getMachineInfo have been called and the * machine is an S5mx. */ int mtCacheS5mx; /** * Prepare scratch RAM in Series 5 for read/write * * This function works with EPOC only. Using it with SIBO * machines, returns always an error code E_PSI_NOT_SIBO. * It prepares a scratch area on the EPOC machine for a following * use from within @ref rpcs::configRead or @ref rpcs::configWrite . * These functions call @ref rpcs::closeHandle on exit. The contents * of the scratch area is stored in RAM of the Series 5, therefore it * gets lost when the Series 5 is switched off. * * @param handle The handle to be used in @ref rpcs::configRead , * @ref rpcs::configWrite and @ref rpcs::closeHandle is * returned here. * @param size The number of bytes you want to use. * * @returns A psion error code. 0 = Ok. */ virtual Enum configOpen(uint16_t &handle, uint32_t size) { return rfsv::E_PSI_NOT_SIBO; } /** * Sends a command to the remote side. * * If communication fails, a reconnect is triggered * and a second attempt to transmit the request * is attempted. If that second attempt fails, * the function returns an error and sets rpcs::status * to E_PSI_FILE_DISC. * * @param cc The command to execute on the remote side. * @param data Additional data for this command. * * @returns true on success, false on failure. */ bool sendCommand(enum commands cc, bufferStore &data); Enum getResponse(bufferStore &data, bool statusIsFirstByte); const char *getConnectName(); }; #endif plptools-1.0.26/lib/rpcs16.cc000066400000000000000000000026411504470754400156640ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999 Matt J. Gumbley * Copyright (C) 1999-2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #include "config.h" #include "rpcs16.h" #include "bufferstore.h" #include "bufferarray.h" #include "ppsocket.h" #include #include #include using namespace std; rpcs16::rpcs16(ppsocket * _skt) { skt = _skt; mtCacheS5mx = 0; reset(); } Enum rpcs16:: getCmdLine(const char *process, string &ret) { bufferStore a; Enum res; a.addStringT(process); if (!sendCommand(rpcs::GET_CMDLINE, a)) return rfsv::E_PSI_FILE_DISC; if ((res = getResponse(a, true)) == rfsv::E_PSI_GEN_NONE) ret = a.getString(0); return res; } plptools-1.0.26/lib/rpcs16.h000066400000000000000000000026421504470754400155270ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999 Philip Proudman * Copyright (C) 1999-2002 Fritz Elfert * * 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 * along with this program; if not, see . * */ #ifndef _RPCS16_H_ #define _RPCS16_H_ #include class ppsocket; class bufferStore; class rpcsfactory; /** * This is the implementation of the @ref rpcs protocol for * Psion series 3 (SIBO) variant. You normally never create * objects of this class directly. Thus the constructor is * private. Use @ref rpcsfactory for creating an instance of * @ref rpcs . For a complete documentation, see @ref rpcs . */ class rpcs16 : public rpcs { friend class rpcsfactory; public: Enum getCmdLine(const char *, std::string &); private: rpcs16(ppsocket *); }; #endif plptools-1.0.26/lib/rpcs32.cc000066400000000000000000000161211504470754400156600ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999-2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #include "config.h" #include "rpcs32.h" #include "bufferstore.h" #include "bufferarray.h" #include "ppsocket.h" #include #include #include #include using namespace std; rpcs32::rpcs32(ppsocket * _skt) { skt = _skt; mtCacheS5mx = 0; reset(); } Enum rpcs32:: getCmdLine(const char *process, string &ret) { bufferStore a; Enum res; a.addStringT(process); if (!sendCommand(rpcs::GET_CMDLINE, a)) return rfsv::E_PSI_FILE_DISC; if ((res = getResponse(a, true)) == rfsv::E_PSI_GEN_NONE) ret = a.getString(0); return res; } Enum rpcs32:: getMachineInfo(machineInfo &mi) { bufferStore a; Enum res; if (!sendCommand(rpcs::GET_MACHINE_INFO, a)) return rfsv::E_PSI_FILE_DISC; if ((res = getResponse(a, true)) != rfsv::E_PSI_GEN_NONE) return res; if (a.getLen() != 256) return rfsv::E_PSI_GEN_FAIL; mi.machineType = (enum rpcs::machs)a.getDWord(0); strncpy(mi.machineName, a.getString(16), 16); mi.machineName[16] = '\0'; mi.machineUID = a.getDWord(44); mi.machineUID <<= 32; mi.machineUID |= a.getDWord(40); mi.countryCode = a.getDWord(56); mi.uiLanguage = (enum rpcs::languages)a.getDWord(164); mi.romMajor = a.getByte(4); mi.romMinor = a.getByte(5); mi.romBuild = a.getWord(6); mi.romSize = a.getDWord(140); mi.ramSize = a.getDWord(136); mi.ramMaxFree = a.getDWord(144); mi.ramFree = a.getDWord(148); mi.ramDiskSize = a.getDWord(152); mi.registrySize = a.getDWord(156); mi.romProgrammable = (a.getDWord(160) != 0); mi.displayWidth = a.getDWord(32); mi.displayHeight = a.getDWord(36); mi.time.tv_low = a.getDWord(48); mi.time.tv_high = a.getDWord(52); mi.tz.utc_offset = a.getDWord(60); mi.tz.dst_zones = a.getDWord(64); mi.tz.home_zone = a.getDWord(68); PsiZone::getInstance().setZone(mi.tz); mi.mainBatteryInsertionTime.tv_low = a.getDWord(72); mi.mainBatteryInsertionTime.tv_high = a.getDWord(76); mi.mainBatteryStatus = (enum rpcs::batterystates)a.getDWord(80); mi.mainBatteryUsedTime.tv_low = a.getDWord(84); mi.mainBatteryUsedTime.tv_high = a.getDWord(88); mi.mainBatteryCurrent = a.getDWord(92); mi.mainBatteryUsedPower = a.getDWord(96); mi.mainBatteryVoltage = a.getDWord(100); mi.mainBatteryMaxVoltage = a.getDWord(104); mi.backupBatteryStatus = (enum rpcs::batterystates)a.getDWord(108); mi.backupBatteryVoltage = a.getDWord(112); mi.backupBatteryMaxVoltage = a.getDWord(116); mi.externalPowerUsedTime.tv_low = a.getDWord(124); mi.externalPowerUsedTime.tv_high = a.getDWord(128); mi.externalPower = (a.getDWord(120) != 0); mtCacheS5mx |= 8; if (res == rfsv::E_PSI_GEN_NONE) { if (!strcmp(mi.machineName, "SERIES5mx")) mtCacheS5mx |= 2; } return res; } Enum rpcs32:: regOpenIter(uint32_t uid, char *match, uint16_t &handle) { bufferStore a; Enum res; cout << "Oiter" << endl; a.addDWord(uid); a.addDWord(strlen(match)); a.addStringT(match); if (!sendCommand(rpcs::REG_OPEN_ITER, a)) return rfsv::E_PSI_FILE_DISC; res = getResponse(a, true); cout << "ro: r=" << res << " a=" << a << endl; if (a.getLen() == 2) handle = a.getWord(0); return rfsv::E_PSI_GEN_NONE; } Enum rpcs32:: regReadIter(uint16_t handle) { bufferStore a; Enum res; cout << "Riter" << endl; a.addWord(handle); if (!sendCommand(rpcs::REG_READ_ITER, a)) return rfsv::E_PSI_FILE_DISC; res = getResponse(a, true); cout << "ro: r=" << res << " a=" << a << endl; if ((a.getLen() == 3) && (a.getByte(2) == 0xff)) return rfsv::E_PSI_FILE_EOF; return rfsv::E_PSI_GEN_NONE; } Enum rpcs32:: setTime(time_t time) { bufferStore a; Enum res; PsiTime pt; psi_timezone ptz; rpcs::machineInfo mi; // cout << "settime" << endl; if ((res = getMachineInfo(mi)) == rfsv::E_PSI_GEN_NONE) { if (PsiZone::getInstance().getZone(ptz)) { pt = PsiTime(time + ptz.utc_offset); a.addDWord(pt.getPsiTimeLo()); a.addDWord(pt.getPsiTimeHi()); a.addDWord(mi.countryCode); a.addDWord(ptz.utc_offset); a.addDWord(ptz.dst_zones); a.addDWord(ptz.home_zone); // cout << "a=" << a << endl; if (!sendCommand(rpcs::SET_TIME, a)) return rfsv::E_PSI_FILE_DISC; return rfsv::E_PSI_GEN_NONE; } else return rfsv::E_PSI_GEN_FAIL; } else return res; } Enum rpcs32:: configOpen(uint16_t &handle, uint32_t size) { bufferStore a; Enum res; a.addDWord(size); if (!sendCommand(rpcs::CONFIG_OPEN, a)) return rfsv::E_PSI_FILE_DISC; res = getResponse(a, true); if (res == rfsv::E_PSI_GEN_NONE && (a.getLen() >= 2)) handle = a.getWord(0); return res; } Enum rpcs32:: configRead(uint32_t size, bufferStore &ret) { bufferStore a; uint16_t handle; Enum res; ret.init(); if ((res = configOpen(handle, size)) != rfsv::E_PSI_GEN_NONE) return res; do { a.init(); a.addWord(handle); a.addDWord(2047); if (!sendCommand(rpcs::CONFIG_READ, a)) return rfsv::E_PSI_FILE_DISC; if ((res = getResponse(a, true)) != rfsv::E_PSI_GEN_NONE) { closeHandle(handle); return res; } if (a.getLen() > 0) ret.addBuff(a); } while (a.getLen() > 0); return rfsv::E_PSI_GEN_NONE; } Enum rpcs32:: configWrite(bufferStore data) { bufferStore a; uint16_t handle; Enum res; return rfsv::E_PSI_GEN_NONE; if ((res = configOpen(handle, data.getLen())) != rfsv::E_PSI_GEN_NONE) return res; do { a.init(); long l = (data.getLen() > 2047) ? 2047 : data.getLen(); a.addWord(handle); a.addBuff(data, l); data.discardFirstBytes(l); if (!sendCommand(rpcs::CONFIG_WRITE, a)) return rfsv::E_PSI_FILE_DISC; if ((res = getResponse(a, true)) != rfsv::E_PSI_GEN_NONE) { closeHandle(handle); return res; } } while (data.getLen() > 0); return rfsv::E_PSI_GEN_NONE; } Enum rpcs32:: closeHandle(uint16_t handle) { bufferStore a; a.addWord(handle); if (!sendCommand(rpcs::CLOSE_HANDLE, a)) return rfsv::E_PSI_FILE_DISC; return getResponse(a, true); } plptools-1.0.26/lib/rpcs32.h000066400000000000000000000040661504470754400155270ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999 Philip Proudman * Copyright (C) 1999-2002 Fritz Elfert * * 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 * along with this program; if not, see . * */ #ifndef _RPCS32_H_ #define _RPCS32_H_ #include class ppsocket; class rpcsfactory; /** * This is the implementation of the @ref rpcs protocol for * Psion series 5 (EPOC) variant. You normally never create * objects of this class directly. Thus the constructor is * private. Use @ref rpcsfactory for creating an instance of * @ref rpcs . For a complete documentation, see @ref rpcs . */ class rpcs32 : public rpcs { friend class rpcsfactory; public: Enum getCmdLine(const char *, std::string &); Enum getMachineInfo(machineInfo &); Enum configRead(uint32_t, bufferStore &); Enum configWrite(bufferStore); Enum closeHandle(uint16_t); Enum regOpenIter(uint32_t uid, char *match, uint16_t &handle); Enum regReadIter(uint16_t handle); Enum setTime(time_t time); #if 0 Enum regWrite(void); Enum regRead(void); Enum regDelete(void); Enum queryOpen(void); Enum queryRead(void); Enum quitServer(void); #endif protected: Enum configOpen(uint16_t &, uint32_t); private: rpcs32(ppsocket *); }; #endif plptools-1.0.26/lib/rpcsfactory.cc000066400000000000000000000055441504470754400171120ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 2000-2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #include "config.h" #include "rpcs16.h" #include "rpcs32.h" #include "rpcsfactory.h" #include "bufferstore.h" #include "ppsocket.h" #include "Enum.h" #include #include ENUM_DEFINITION_BEGIN(rpcsfactory::errs, rpcsfactory::FACERR_NONE) stringRep.add(rpcsfactory::FACERR_NONE, N_("no error")); stringRep.add(rpcsfactory::FACERR_COULD_NOT_SEND, N_("could not send version request")); stringRep.add(rpcsfactory::FACERR_AGAIN, N_("try again")); stringRep.add(rpcsfactory::FACERR_NOPSION, N_("no EPOC device connected")); stringRep.add(rpcsfactory::FACERR_PROTVERSION, N_("wrong protocol version")); stringRep.add(rpcsfactory::FACERR_NORESPONSE, N_("no response from ncpd")); ENUM_DEFINITION_END(rpcsfactory::errs) rpcsfactory::rpcsfactory(ppsocket *_skt) { err = FACERR_NONE; skt = _skt; } rpcs * rpcsfactory::create(bool reconnect) { // skt is connected to the ncp daemon, which will have (hopefully) seen // an INFO exchange, where the protocol version of the remote Psion was // sent, and noted. We have to ask the ncp daemon which protocol it saw, // so we can instantiate the correct rpcs protocol handler for the // caller. We announce ourselves to the NCP daemon, and the relevant // rpcs module will also announce itself. bufferStore a; err = FACERR_NONE; a.addStringT("NCP$INFO"); if (!skt->sendBufferStore(a)) { if (!reconnect) err = FACERR_COULD_NOT_SEND; else { skt->closeSocket(); skt->reconnect(); err = FACERR_AGAIN; } return NULL; } if (skt->getBufferStore(a) == 1) { if (a.getLen() > 8 && !strncmp(a.getString(), "Series 3", 8)) { return new rpcs16(skt); } else if (a.getLen() > 8 && !strncmp(a.getString(), "Series 5", 8)) { return new rpcs32(skt); } if ((a.getLen() > 8) && !strncmp(a.getString(), "No Psion", 8)) { skt->closeSocket(); skt->reconnect(); err = FACERR_NOPSION; return NULL; } // Invalid protocol version err = FACERR_PROTVERSION; } else err = FACERR_NORESPONSE; // No message returned. return NULL; } plptools-1.0.26/lib/rpcsfactory.h000066400000000000000000000040541504470754400167470ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999 Philip Proudman * Copyright (C) 1999-2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #ifndef _RPCSFACTORY_H_ #define _RPCSFACTORY_H_ #include class ppsocket; /** * A factory for automatically instantiating the correct protocol * variant depending on the connected Psion. */ class rpcsfactory { public: /** * The known errors which can happen during @ref create . */ enum errs { FACERR_NONE = 0, FACERR_COULD_NOT_SEND = 1, FACERR_AGAIN = 2, FACERR_NOPSION = 3, FACERR_PROTVERSION = 4, FACERR_NORESPONSE = 5 }; /** * Constructs a rpcsfactory. * * @param skt The socket to be used for connecting * to the ncpd daemon. */ rpcsfactory(ppsocket * skt); /** * Creates a new rpcs instance. * * @param reconnect Set to true, if automatic reconnect * should be performed on failure. * * @returns A pointer to a newly created rpcs instance or * NULL on failure. */ virtual rpcs * create(bool reconnect); /** * Retrieve an error code. * * @returns The error code, in case @ref create has * failed, 0 otherwise. */ virtual Enum getError() { return err; } private: /** * The socket to be used for connecting to the * ncpd daemon. */ ppsocket *skt; Enum err; }; #endif plptools-1.0.26/lib/siscomponentrecord.cpp000066400000000000000000000053461504470754400206700ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 2002 Daniel Brahneborg * * 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 * along with this program; if not, see . */ #include "config.h" #include "siscomponentrecord.h" #include "sisfile.h" #include "plpintl.h" #include #include SISComponentNameRecord::SISComponentNameRecord() { m_names = NULL; } SISComponentNameRecord::~SISComponentNameRecord() { if (m_names) { for (int i = 0; i < m_nameCount; ++i) delete[] m_names[i]; delete[] m_names; } } SisRC SISComponentNameRecord::fillFrom(uint8_t* buf, int* basePos, off_t len, SISFile* sisFile) { int n = sisFile->m_header.m_nlangs; int base = *basePos; int entrySize = 8 + n * 4 * 2; if (base + entrySize > len) return SIS_TRUNCATED; *basePos += entrySize; uint8_t* p = buf + base; int size = 0; m_nameLengths = new uint32_t[n]; m_namePtrs = new uint32_t[n]; // First read lengths. // for (int i = 0; i < n; ++i) { m_nameLengths[i] = read32(p + size); if (m_nameLengths[i] > len) { printf(_("Length too large for name record %d.\n"), i); return SIS_TRUNCATED; } size += 4; } // Then read ptrs. // m_names = new uint8_t*[n]; m_nameCount = n; for (int i = 0; i < n; ++i) { m_namePtrs[i] = read32(p + size); if (m_namePtrs[i] + m_nameLengths[i] > len) { printf(_("Position/length too large for name record %d.\n"), i); return SIS_TRUNCATED; } size += 4; if (logLevel >= 2) printf(_("Name %d (for %s) is %.*s\n"), i, sisFile->getLanguage(i)->m_name, m_nameLengths[i], buf + m_namePtrs[i]); int nlen = m_nameLengths[i]; m_names[i] = new uint8_t[nlen + 1]; memcpy(m_names[i], buf + m_namePtrs[i], nlen); m_names[i][nlen] = 0; } if (logLevel >= 1) printf(_("%d .. %d (%d bytes): Name records\n"), base, base + size, size); return SIS_OK; } uint32_t SISComponentNameRecord::getLastEnd() { uint32_t last = 0; for (int i = 0; i < m_nameCount; ++i) { uint32_t pos = m_namePtrs[i] + m_nameLengths[i]; if (last < pos) last = pos; } return last; } uint8_t* SISComponentNameRecord::getName(int no) { return m_names[no]; } plptools-1.0.26/lib/siscomponentrecord.h000066400000000000000000000040661504470754400203330ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 2002 Daniel Brahneborg * * 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 * along with this program; if not, see . */ #ifndef _SISCOMPONENTRECORD_H #define _SISCOMPONENTRECORD_H #include class SISFile; /** * The name of the component in this SIS file. * A single instance holds the names for all languages. * * There is one name record for each language. * First comes the lengths of all the names, as 32 bit integers. * Second comes pointers to the names, as an index in the complete * SISFile. */ class SISComponentNameRecord { public: SISComponentNameRecord(); virtual ~SISComponentNameRecord(); /** * Populate the fields. * * @param buf The buffer to read data from. * @param base The index where we start reading data. * @param len The length of the buffer, for range checking. * @param sisFile The container sis file. */ SisRC fillFrom(uint8_t* buf, int* base, off_t len, SISFile* sisFile); /** * Find out the end position for the last name in the file. */ uint32_t getLastEnd(); /** * Return the name for the given language. * * @param no The sequence number in the list of language records in * the sis file. */ uint8_t* getName(int no); private: uint32_t* m_nameLengths; uint32_t* m_namePtrs; /** * The extracted names, as zero terminated strings. */ uint8_t** m_names; /** * The number of names, so we know how much to delete. */ int m_nameCount; }; #endif plptools-1.0.26/lib/sisfile.cpp000066400000000000000000000067051504470754400164060ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 2002 Daniel Brahneborg * * 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 * along with this program; if not, see . */ #include "config.h" #include "sisfile.h" #include "sislangrecord.h" #include "sisfilerecord.h" #include "sisreqrecord.h" #include "plpintl.h" #include SISFile::SISFile() { m_buf = 0; m_ownBuffer = false; } SISFile::~SISFile() { if (m_ownBuffer) delete[] m_buf; } SisRC SISFile::compareApp(SISFile* other) { return m_header.compareApp(&other->m_header); } SisRC SISFile::fillFrom(uint8_t* buf, off_t len) { m_end = 0; int ix = 0; m_buf = buf; SisRC rc = m_header.fillFrom(buf, &ix, len); if (rc != SIS_OK) { printf(_("Could not read header, rc = %d\n"), rc); return rc; } if (logLevel >= 2) printf(_("Ate header, got ix = %d\n"), ix); int n; // Read languages. // n = m_header.m_nlangs; m_langRecords = new SISLangRecord[n]; ix = m_header.m_languagePtr; for (int i = 0; i < n; ++i) { if (ix >= len) return SIS_TRUNCATED; rc = m_langRecords[i].fillFrom(buf, &ix, len); if (rc != SIS_OK) { printf(_("Problem reading language record %d, rc = %d.\n"), i, rc); return rc; } } updateEnd(ix); // Read requisites. // n = m_header.m_nreqs; m_reqRecords = new SISReqRecord[n]; ix = m_header.m_reqPtr; for (int i = 0; i < n; ++i) { if (ix >= len) return SIS_TRUNCATED; rc = m_reqRecords[i].fillFrom(buf, &ix, len, this); if (rc != SIS_OK) { printf(_("Problem reading requisite record %d, rc = %d.\n"), i, rc); return rc; } } updateEnd(ix); // Read component names, by language. // ix = m_header.m_componentPtr; rc = m_componentRecord.fillFrom(buf, &ix, len, this); updateEnd(ix); updateEnd(m_componentRecord.getLastEnd()); if (rc != SIS_OK) { printf(_("Problem reading the name record, rc = %d.\n"), rc); return rc; } // Read files. // n = m_header.m_nfiles; m_fileRecords = new SISFileRecord[n]; ix = m_header.m_filesPtr; SisRC myrc = SIS_OK; for (int i = 0; i < n; ++i) { if (ix >= len) return SIS_TRUNCATED; rc = m_fileRecords[i].fillFrom(buf, &ix, len, this); if (rc != SIS_OK) { printf(_("Problem reading file record %d, rc = %d.\n"), i, rc); if (rc == SIS_TRUNCATEDDATA) myrc = rc; else return rc; } } updateEnd(ix); return SIS_OK; } int SISFile::getLanguage() { return m_header.m_installationLanguage; } LangTableEntry* SISFile::getLanguage(int i) { return &langTable[m_langRecords[i].m_lang]; } uint8_t* SISFile::getName() { return m_componentRecord.getName(m_header.m_installationLanguage); } void SISFile::setDrive(char drive) { m_header.setDrive(drive); } void SISFile::setFiles(int nFiles) { m_header.setFiles(nFiles); } void SISFile::setLanguage(int lang) { m_header.m_installationLanguage = lang; } void SISFile::updateEnd(uint32_t pos) { if (m_end < pos) m_end = pos; } plptools-1.0.26/lib/sisfile.h000066400000000000000000000047751504470754400160600ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 2002 Daniel Brahneborg * * 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 * along with this program; if not, see . */ #ifndef _SISFILE_H #define _SISFILE_H #include #include #include class SISLangRecord; class SISFileRecord; class SISReqRecord; /** * The top level container of a SIS file. * Based on documentation by Alexander Thoukydides . */ class SISFile { public: SISFile(); virtual ~SISFile(); /** * Compare uid and version number of this file, with another. * * @see SISFileHeader::compareApp() */ SisRC compareApp(SISFile* other); /** * Populate the fields. * * @param buf The buffer to read from. * @param len The length of the buffer. */ SisRC fillFrom(uint8_t* buf, off_t len); /** * Return the currently selected installation language. */ int getLanguage(); /** * Find a language entry, based on the sequence number in the SISLangRecord * part of the file. */ LangTableEntry* getLanguage(int i); /** * Get the name of this component, in the selected language. */ uint8_t* getName(); /** * Get the number of bytes that should be copied to the residual sis * file on the psion. */ uint32_t getResidualEnd() { return m_end; } void ownBuffer() { m_ownBuffer = true; } /** * Is this the same application? */ bool sameApp(SISFile* other); /** * Set the installed drive. */ void setDrive(char drive); /** * Set the number of installed files. */ void setFiles(int nFiles); /** * Set the selected installation language. */ void setLanguage(int lang); SISFileHeader m_header; SISLangRecord* m_langRecords; SISFileRecord* m_fileRecords; SISReqRecord* m_reqRecords; private: SISComponentNameRecord m_componentRecord; bool m_ownBuffer; uint8_t* m_buf; uint32_t m_end; void updateEnd(uint32_t pos); }; #endif plptools-1.0.26/lib/sisfileheader.cpp000066400000000000000000000135351504470754400175560ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 2002 Daniel Brahneborg * * 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 * along with this program; if not, see . */ #include "config.h" #include "sisfileheader.h" #include "plpintl.h" #include #include const int OFF_NUMBER_OF_FILES = 26; const int OFF_INSTALLATION_DRIVE = 28; SisRC SISFileHeader::compareApp(SISFileHeader* other) { if (m_uid1 != other->m_uid1) return SIS_DIFFERENT_APP; if ((m_major < other->m_major) || ((m_major == other->m_major) && (m_minor < other->m_minor))) return SIS_VER_EARLIER; if ((m_major == other->m_major) && (m_minor == other->m_minor) && (m_variant != other->m_variant)) return SIS_OTHER_VARIANT; return SIS_SAME_OR_LATER; } SisRC SISFileHeader::fillFrom(uint8_t* buf, int* base, off_t len) { if (*base + 68 > len) return SIS_TRUNCATED; uint8_t* start = buf + *base; m_buf = buf; m_uid1 = read32(start); if (logLevel >= 1) printf(_("Got uid1 = %08x\n"), m_uid1); m_uid2 = read32(start + 4); if (m_uid2 != 0x1000006d) { printf("%s", _("Got bad uid2.\n")); return SIS_CORRUPTED; } if (logLevel >= 2) printf(_("Got uid2 = %08x\n"), m_uid2); m_uid3 = read32(start + 8); if (m_uid3 != 0x10000419) { printf("%s", _("Got bad uid3.\n")); return SIS_CORRUPTED; } if (logLevel >= 2) printf(_("Got uid3 = %08x\n"), m_uid3); m_uid4 = read32(start + 12); if (logLevel >= 2) printf(_("Got uid4 = %08x\n"), m_uid4); uint16_t crc1 = 0; for (int i = 0; i < 12; i += 2) crc1 = updateCrc(crc1, buf[*base + i]); uint16_t crc2 = 0; for (int i = 0; i < 12; i += 2) crc2 = updateCrc(crc2, buf[*base + i + 1]); if (logLevel >= 2) printf(_("Got first crc = %08x, wanted %08x\n"), crc2 << 16 | crc1, m_uid4); if ((crc2 << 16 | crc1) != m_uid4) { printf("%s", _("Got bad crc.\n")); return SIS_CORRUPTED; } m_crc = read16(start + 16); m_nlangs = read16(start + 18); if (logLevel >= 2) printf(_("Got %d languages\n"), m_nlangs); m_nfiles = read16(start + 20); if (logLevel >= 2) printf(_("Got %d files\n"), m_nfiles); m_nreqs = read16(start + 22); if (logLevel >= 2) printf(_("Got %d reqs\n"), m_nreqs); m_installationLanguage = read16(start + 24); if (logLevel >= 2) printf(_("Selected language is %d\n"), m_installationLanguage); m_installationFiles = read16(start + OFF_NUMBER_OF_FILES); if (logLevel >= 2) printf(_("Installed files: %d / %d\n"), m_installationFiles, m_nfiles); m_installationDrive = read32(start + OFF_INSTALLATION_DRIVE); if (logLevel >= 2) printf(_("Installed on drive %c\n"), m_installationDrive); m_installerVersion = read32(start + 32); if (logLevel >= 2) printf(_("Got installer version: %08x\n"), m_installerVersion); m_options = read16(start + 36); if (logLevel >= 2) printf(_("Got options: %04x\n"), m_options); m_type = read16(start + 38); if (logLevel >= 2) printf(_("Got type: %04x\n"), m_type); m_major = read16(start + 40); if (logLevel >= 2) printf(_("Got major: %d\n"), m_major); m_minor = read16(start + 42); if (logLevel >= 2) printf(_("Got minor: %d\n"), m_minor); m_variant = read32(start + 44); if (logLevel >= 2) printf(_("Got variant: %d\n"), m_variant); m_languagePtr = read32(start + 48); if (logLevel >= 2) printf(_("Languages begin at %d\n"), m_languagePtr); // if (m_languagePtr >= len) // return SIS_TRUNCATED; m_filesPtr = read32(start + 52); if (logLevel >= 2) printf(_("Files begin at %d\n"), m_filesPtr); // if (m_filesPtr >= len) // return SIS_TRUNCATED; m_reqPtr = read32(start + 56); if (logLevel >= 2) printf(_("Requisites begin at %d\n"), m_reqPtr); // if (m_reqPtr >= len) // return SIS_TRUNCATED; m_unknown = read32(start + 60); m_componentPtr = read32(start + 64); if (logLevel >= 2) printf(_("Components begin at %d\n"), m_componentPtr); // if (m_componentPtr >= len) // return SIS_TRUNCATED; *base += 68; return SIS_OK; } void SISFileHeader::setDrive(char drive) { m_installationDrive = drive; m_buf[OFF_INSTALLATION_DRIVE] = drive; m_buf[OFF_INSTALLATION_DRIVE + 1] = m_buf[OFF_INSTALLATION_DRIVE + 2] = m_buf[OFF_INSTALLATION_DRIVE + 3] = 0; } void SISFileHeader::setFiles(int nFiles) { m_installationFiles = nFiles; write16(m_buf + OFF_NUMBER_OF_FILES, nFiles); } plptools-1.0.26/lib/sisfileheader.h000066400000000000000000000046261504470754400172240ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 2002 Daniel Brahneborg * * 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 * along with this program; if not, see . */ #ifndef _SISFILEHEADER_H #define _SISFILEHEADER_H #include /** * The first part of a SISFile. * * This file header is referenced from most other parts of the sis file, * mainly since it contains the list of languages. */ class SISFileHeader { public: /** * Compare uid and version number of this file, with another. */ SisRC compareApp(SISFileHeader* other); /** * Populate the fields. * * @param buf The buffer to read from. * @param base The index to start reading from, which is updated * when the header is successfully read. * @param len The length of the buffer. */ SisRC fillFrom(uint8_t* buf, int* base, off_t len); /** * Update the drive letter, and patch the parsed buffer. */ void setDrive(char drive); /** * Update the number of installed files, and patch the parsed buffer. */ void setFiles(int nFiles); enum FileOptions { op_isUnicode = 1, op_isDistributable = 2 #ifdef EPOC6 , op_noCompress = 8, op_shutdownApps = 16 #endif }; enum FileType { FT_App = 0 #ifdef EPOC6 , FT_System = 1, FT_Option = 2, FT_Config = 3, FT_Patch = 4, FT_Upgrade = 5 #endif }; uint32_t m_uid1; uint32_t m_uid2; uint32_t m_uid3; uint32_t m_uid4; uint16_t m_crc; uint16_t m_nlangs; uint16_t m_nfiles; uint16_t m_nreqs; uint16_t m_installationLanguage; uint16_t m_installationFiles; uint32_t m_installationDrive; uint32_t m_installerVersion; uint16_t m_options; uint16_t m_type; uint16_t m_major; uint16_t m_minor; uint32_t m_variant; uint32_t m_languagePtr; uint32_t m_filesPtr; uint32_t m_reqPtr; uint32_t m_unknown; uint32_t m_componentPtr; private: uint8_t* m_buf; }; #endif plptools-1.0.26/lib/sisfilerecord.cpp000066400000000000000000000076241504470754400176060ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 2002 Daniel Brahneborg * * 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 * along with this program; if not, see . */ #include "config.h" #include "sisfilerecord.h" #include "sisfile.h" #include "plpintl.h" #include SisRC SISFileRecord::fillFrom(uint8_t* buf, int* base, off_t len, SISFile* sisFile) { if (*base + 28 + 4 * 2 > len) return SIS_TRUNCATED; SisRC rc = SIS_OK; m_buf = buf; m_len = len; uint8_t* p = buf + *base; int size = 0; m_flags = read32(p); if (logLevel >= 2) printf(_("Got flags %d\n"), m_flags); m_fileType = read32(p + 4); if (logLevel >= 2) printf(_("Got file type %d\n"), m_fileType); m_fileDetails = read32(p + 8); if (logLevel >= 2) printf(_("Got file details %d\n"), m_fileDetails); m_sourceLength = read32(p + 12); m_sourcePtr = read32(p + 16); // printf(_("Got source length = %d, source name ptr = %d\n"), // m_sourceLength, m_sourcePtr); if (logLevel >= 2) if (m_sourceLength > 0) printf(_("Got source name %.*s\n"), m_sourceLength, buf + m_sourcePtr); m_destLength = read32(p + 20); m_destPtr = read32(p + 24); // printf(_("Got dest length = %d, dest name ptr = %d\n"), // m_destLength, m_destPtr); if (logLevel >= 2) printf(_("Got destination name %.*s\n"), m_destLength, buf + m_destPtr); size = 28; switch (m_flags) { case 0: // Only one file. m_fileLengths = new uint32_t[1]; m_filePtrs = new uint32_t[1]; m_fileLengths[0] = read32(p + size); m_filePtrs[0] = read32(p + size + 4); size += 8; if (logLevel >= 2) printf(_("File is %d bytes long (at %d) (to %d)\n"), m_fileLengths[0], m_filePtrs[0], m_fileLengths[0] + m_filePtrs[0]); if (logLevel >= 1) printf(_("%d .. %d (%d bytes): Single file record type %d, %.*s\n"), m_filePtrs[0], m_filePtrs[0] + m_fileLengths[0], m_fileLengths[0], m_fileType, m_destLength, buf + m_destPtr); break; case 1: // One file per language. { int n = sisFile->m_header.m_nlangs; m_fileLengths = new uint32_t[n]; m_filePtrs = new uint32_t[n]; if (*base + size + n * 8 > len) return SIS_TRUNCATED; for (int i = 0; i < n; ++i) { m_fileLengths[i] = read32(p + size); // if (m_fileLengths[i] > len) // rc = SIS_TRUNCATEDDATA; size += 4; } for (int i = 0; i < n; ++i) { m_filePtrs[i] = read32(p + size); int fileLen = m_fileLengths[i]; // if (m_filePtrs[i] + fileLen > len) // rc = SIS_TRUNCATEDDATA; size += 4; if (logLevel >= 2) printf(_("File %d (for %s) is %d bytes long (at %d)\n"), i, sisFile->getLanguage(i)->m_name, fileLen, m_filePtrs[i]); if (logLevel >= 1) printf(_("%d .. %d (%d bytes): File record (%s) for %.*s\n"), m_filePtrs[i], m_filePtrs[i] + fileLen, fileLen, sisFile->getLanguage(i)->m_name, m_destLength, buf + m_destPtr); } break; } default: if (logLevel >= 2) printf(_("Unknown file flags %d\n"), m_flags); } *base += size; return rc; } uint8_t* SISFileRecord::getFilePtr(int fileNo) { if (fileNo < 0) return 0; if (m_filePtrs[fileNo] >= m_len) return 0; return &m_buf[m_filePtrs[fileNo]]; } void SISFileRecord::setMainDrive(char drive) { if (m_buf[m_destPtr] == '!') m_buf[m_destPtr] = drive; } plptools-1.0.26/lib/sisfilerecord.h000066400000000000000000000051571504470754400172520ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 2002 Daniel Brahneborg * * 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 * along with this program; if not, see . */ #ifndef _SISFILERECORD_H #define _SISFILERECORD_H #include class SISFile; /** * Information about a file component in a SIS file. * * The file can be for multiple languages, in which case a single * instance holds pointers to contents for all languages. */ class SISFileRecord { public: /** * Populate the fields. * * @param buf The buffer to read from. * @param base The index to start reading from, which is updated * when the record is successfully read. * @param len The length of the buffer. * @param sisFile The container SISFile. */ SisRC fillFrom(uint8_t* buf, int* base, off_t len, SISFile* sisFile); uint8_t* getDestPtr() { return m_destPtr < m_len ? &m_buf[m_destPtr] : 0; } /** * Return a pointer to the file data for the file for the specified * language. */ uint8_t* getFilePtr(int fileNo); void setMainDrive(char drive); /** * 1 if multiple language versions, otherwise 0. */ uint32_t m_flags; /** * Type of file. * * - 0. Standard file. * - 1. Text file to display during installation. * - 2. SIS component. * - 3. File to run during installation/removal. * - 4. Does not exist yet, but will be created when app is run, so * it should not be removed during an upgrade. */ uint32_t m_fileType; /** * If file type is 1: * * - 0. Continue. * - 1. Yes, No (skip next file). * - 2. Yes, No (abort installation). * * If file type is 3: * * - 0. Run during installation. * - 1. Run during removal. * - 2. Run during both installation and removal. */ uint32_t m_fileDetails; uint32_t m_sourceLength; uint32_t m_sourcePtr; uint32_t m_destLength; uint32_t* m_fileLengths; private: uint32_t m_destPtr; uint32_t* m_filePtrs; /** * The buffer we belong to. * Used for updating the destination file name. */ uint8_t* m_buf; int m_len; }; #endif plptools-1.0.26/lib/sislangrecord.cpp000066400000000000000000000024571504470754400176070ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 2002 Daniel Brahneborg * * 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 * along with this program; if not, see . */ #include "config.h" #include "sislangrecord.h" #include "plpintl.h" #include SisRC SISLangRecord::fillFrom(uint8_t* buf, int* base, off_t len) { if (*base + 2 > len) return SIS_TRUNCATED; m_lang = read16(buf + *base); if (m_lang > 33) // Thai, last language return SIS_CORRUPTED; if (logLevel >= 2) printf(_("Got language %d (%s)\n"), m_lang, langTable[m_lang].m_name); if (logLevel >= 1) printf(_("%d .. %d (%d bytes): Language record for %s\n"), *base, *base + 2, 2, langTable[m_lang].m_name); *base += 2; return SIS_OK; } plptools-1.0.26/lib/sislangrecord.h000066400000000000000000000025001504470754400172410ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 2002 Daniel Brahneborg * * 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 * along with this program; if not, see . */ #ifndef _SISLANGRECORD_H #define _SISLANGRECORD_H #include /** * A simple language record, only containing the epoc specific 16 bit * language number. */ class SISLangRecord { public: /** * Populate the fields. * * @param buf The buffer to read from. * @param base The index to start reading from, which is updated * when the record is successfully read. * @param len The length of the buffer. */ SisRC fillFrom(uint8_t* buf, int* base, off_t len); /** * The language number. * * @see langTable */ uint16_t m_lang; }; #endif plptools-1.0.26/lib/sisreqrecord.cpp000066400000000000000000000043051504470754400174470ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 2002 Daniel Brahneborg * * 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 * along with this program; if not, see . */ #include "config.h" #include "sisreqrecord.h" #include "sisfile.h" #include "plpintl.h" #include SisRC SISReqRecord::fillFrom(uint8_t* buf, int* base, off_t len, SISFile* sisFile) { int n = sisFile->m_header.m_nlangs; if (*base + 12 + n * 4 * 2 > len) return SIS_TRUNCATED; uint8_t* p = buf + *base; int size = 0; m_uid = read32(p); m_major = read16(p + 4); m_minor = read16(p + 6); m_variant = read32(p + 8); m_nameLengths = new uint32_t[n]; m_namePtrs = new uint32_t[n]; if (logLevel >= 2) printf(_("Requisite: uid=%08x, version=%d.%d-%d.\n"), m_uid, m_major, m_minor, m_variant); // First read lengths. // size = 12; for (int i = 0; i < n; ++i) { m_nameLengths[i] = read32(p + size); if (logLevel >= 2) printf(_("Got namelength %d\n"), m_nameLengths[i]); size += 4; } // Then read ptrs. // for (int i = 0; i < n; ++i) { m_namePtrs[i] = read32(p + size); if (logLevel >= 2) printf(_("Got namepos %d\n"), m_namePtrs[i]); if (m_namePtrs[i] + m_nameLengths[i] > len) { printf(_("Position/length too large for req record %d.\n"), i); return SIS_CORRUPTED; } size += 4; if (logLevel >= 2) printf(_("Name of requisite for %s is %.*s\n"), sisFile->getLanguage(i)->m_name, m_nameLengths[i], buf + m_namePtrs[i]); } if (logLevel >= 1) printf(_("%d .. %d (%d bytes): Req record for uid %08x\n"), *base, *base + size, size, m_uid); *base += size; return SIS_OK; } plptools-1.0.26/lib/sisreqrecord.h000066400000000000000000000026671504470754400171250ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 2002 Daniel Brahneborg * * 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 * along with this program; if not, see . */ #ifndef _SISREQRECORD_H #define _SISREQRECORD_H #include class SISFile; /** * Information about an application that must be installed prior to the * current one. */ class SISReqRecord { public: /** * Populate the fields. * * @param buf The buffer to read from. * @param base The index to start reading from, which is updated * when the record is successfully read. * @param len The length of the buffer. * @param sisFile The container SISFile. */ SisRC fillFrom(uint8_t* buf, int* base, off_t len, SISFile* sisFile); uint32_t m_uid; uint16_t m_major; uint16_t m_minor; uint32_t m_variant; uint32_t* m_nameLengths; uint32_t* m_namePtrs; }; #endif plptools-1.0.26/lib/sistypes.cpp000066400000000000000000000053501504470754400166260ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 2002 Daniel Brahneborg * * 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 * along with this program; if not, see . */ #include "config.h" #include "sistypes.h" static unsigned int s_crcTable[256]; int logLevel = 0; void createCRCTable() { const unsigned int polynomial = 0x1021; unsigned int index; s_crcTable[0] = 0; for (index = 0; index < 128; index++) { unsigned int carry = s_crcTable[index] & 0x8000; unsigned int temp = (s_crcTable[index] << 1) & 0xffff; s_crcTable[index * 2 + (carry ? 0 : 1)] = temp ^ polynomial; s_crcTable[index * 2 + (carry ? 1 : 0)] = temp; } } uint16_t updateCrc(uint16_t crc, uint8_t value) { return (crc << 8) ^ s_crcTable[((crc >> 8) ^ value) & 0xff]; } uint16_t calcCRC(uint8_t* data, int len) { uint16_t crc = 0; for (int i = 0; i < len; ++i) { uint8_t value = data[i]; crc = (crc << 8) ^ s_crcTable[((crc >> 8) ^ value) & 0xff]; } return crc; } uint16_t read16(uint8_t* p) { return p[0] | (p[1] << 8); } uint32_t read32(uint8_t* p) { return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); } void write16(uint8_t* p, int val) { p[0] = val & 255; p[1] = (val >> 8) & 255; } LangTableEntry langTable[] = { { 0, "", "Test" }, { 1, "EN", "UK English" }, { 2, "FR", "French" }, { 3, "GE", "German" }, { 4, "SP", "Spanish" }, { 5, "IT", "Italian" }, { 6, "SW", "Swedish" }, { 7, "DA", "Danish" }, { 8, "NO", "Norwegian" }, { 9, "FI", "Finnish" }, { 10, "AM", "American English" }, { 11, "SF", "Swiss French" }, { 12, "SG", "Swiss German" }, { 13, "PO", "Portuguese" }, { 14, "TU", "Turkish" }, { 15, "IC", "Icelandic" }, { 16, "RU", "Russian" }, { 17, "HU", "Hungarian" }, { 18, "DU", "Dutch" }, { 19, "BL", "Belgian Flemish" }, { 20, "AU", "Australian English" }, { 21, "BG", "Belgian French" }, { 22, "AS", "Austrian German" }, { 23, "NZ", "New Zealand" }, { 24, "IF", "International French" }, { 25, "CS", "Czech" }, { 26, "SK", "Slovak" }, { 27, "PL", "Polish" }, { 28, "SL", "Slovenian" }, { 29, "TC", "Taiwan Chinese" }, { 30, "HK", "Hong Kong" }, { 31, "ZH", "PRC Chinese" }, { 32, "JA", "Japanese" }, { 33, "TH", "Thai" }, }; plptools-1.0.26/lib/sistypes.h000066400000000000000000000027421504470754400162750ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 2002 Daniel Brahneborg * * 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 * along with this program; if not, see . */ #ifndef _SISTYPES_H #define _SISTYPES_H #include #include /** * Return Codes. */ enum SisRC { SIS_OK = 0, SIS_TRUNCATED, SIS_TRUNCATEDDATA, SIS_CORRUPTED, SIS_FAILED, SIS_ABORTED, SIS_DIFFERENT_APP, SIS_VER_EARLIER, SIS_SAME_OR_LATER, SIS_OTHER_VARIANT }; extern uint16_t read16(uint8_t* p); extern uint32_t read32(uint8_t* p); extern void write16(uint8_t* p, int val); extern void createCRCTable(); extern uint16_t updateCrc(uint16_t crc, uint8_t value); extern int logLevel; /** * Holder of a language entry, translating from language numbers to * names. */ struct LangTableEntry { uint16_t m_no; char m_code[3]; const char* m_name; }; extern LangTableEntry langTable[]; #endif plptools-1.0.26/lib/wprt.cc000066400000000000000000000063561504470754400155510ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999-2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #include "config.h" #include "wprt.h" #include "bufferstore.h" #include "ppsocket.h" #include "bufferarray.h" #include "Enum.h" #include #include #include using namespace std; wprt::wprt(ppsocket * _skt) { skt = _skt; reset(); } wprt::~wprt() { skt->closeSocket(); } // // public common API // void wprt:: reconnect(void) { //skt->closeSocket(); skt->reconnect(); reset(); } void wprt:: reset(void) { bufferStore a; status = rfsv::E_PSI_FILE_DISC; a.addStringT(getConnectName()); if (skt->sendBufferStore(a)) { if (skt->getBufferStore(a) == 1) { if (!strcmp(a.getString(0), "Ok")) status = rfsv::E_PSI_GEN_NONE; } } } Enum wprt:: getStatus(void) { return status; } const char *wprt:: getConnectName(void) { return "SYS$WPRT"; } // // protected internals // bool wprt:: sendCommand(enum commands cc, bufferStore & data) { if (status == rfsv::E_PSI_FILE_DISC) { reconnect(); if (status == rfsv::E_PSI_FILE_DISC) return false; } bool result; bufferStore a; a.addByte(cc); a.addBuff(data); result = skt->sendBufferStore(a); if (!result) { reconnect(); result = skt->sendBufferStore(a); if (!result) status = rfsv::E_PSI_FILE_DISC; } return result; } Enum wprt:: initPrinter() { Enum ret; bufferStore a; a.addByte(2); // Major printer version a.addByte(0); // Minor printer version sendCommand(WPRT_INIT, a); if ((ret = getResponse(a)) != rfsv::E_PSI_GEN_NONE) cerr << "WPRT ERR:" << a << endl; else { if (a.getLen() != 3) ret = rfsv::E_PSI_GEN_FAIL; if ((a.getByte(0) != 0) || (a.getWord(1) != 2)) ret = rfsv::E_PSI_GEN_FAIL; } return ret; } Enum wprt:: getData(bufferStore &buf) { Enum ret; sendCommand(WPRT_GET, buf); if ((ret = getResponse(buf)) != rfsv::E_PSI_GEN_NONE) cerr << "WPRT ERR:" << buf << endl; return ret; } Enum wprt:: cancelJob() { Enum ret; bufferStore a; sendCommand(WPRT_CANCEL, a); if ((ret = getResponse(a)) != rfsv::E_PSI_GEN_NONE) cerr << "WPRT ERR:" << a << endl; return ret; } bool wprt:: stop() { bufferStore a; return sendCommand(WPRT_STOP, a); } Enum wprt:: getResponse(bufferStore & data) { Enum ret = rfsv::E_PSI_GEN_NONE; if (skt->getBufferStore(data) == 1) return ret; else status = rfsv::E_PSI_FILE_DISC; return status; } plptools-1.0.26/lib/wprt.h000066400000000000000000000053331504470754400154050ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999-2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #ifndef _WPRT_H_ #define _WPRT_H_ #include #include class ppsocket; class bufferStore; class bufferArray; /** * Remote Print services via PLP * */ class wprt { public: wprt(ppsocket *); /** * Virtual destructor. */ ~wprt(); /** * Initializes a connection to the remote * machine. */ void reset(); /** * Attempts to re-establish a remote * connection by first closing the socket, * then connecting again to the ncpd daemon * and finally calling @ref reset. */ void reconnect(); /** * Retrieves the current status of the * connection. * * @returns The connection status. */ Enum getStatus(); /** * Get Print Data */ Enum getData(bufferStore &buf); /** * Init Printer */ Enum initPrinter(); /** * Cancels a running job. */ Enum cancelJob(); /** * Stops the WPRT server. */ bool stop(); protected: /** * The possible commands. */ enum commands { WPRT_INIT = 0x00, WPRT_GET = 0xf0, WPRT_CANCEL = 0xf1, WPRT_STOP = 0xff }; /** * The socket, used for communication * with ncpd. */ ppsocket *skt; /** * The current status of the connection. */ Enum status; /** * Sends a command to the remote side. * * If communication fails, a reconnect is triggered * and a second attempt to transmit the request * is attempted. If that second attempt fails, * the function returns an error an sets rpcs::status * to E_PSI_FILE_DISC. * * @param cc The command to execute on the remote side. * @param data Additional data for this command. * * @returns true on success, false on failure. */ bool sendCommand(enum commands cc, bufferStore &data); Enum getResponse(bufferStore &data); const char *getConnectName(); }; #endif plptools-1.0.26/m4/000077500000000000000000000000001504470754400140065ustar00rootroot00000000000000plptools-1.0.26/m4/.gitignore000066400000000000000000000060151504470754400160000ustar00rootroot00000000000000!/ax_*.m4 /*.m4/00gnulib.m4 /codeset.m4 /extern-inline.m4 /fcntl-o.m4 /gettext.m4 /gnulib-cache.m4 /gnulib-common.m4 /gnulib-comp.m4 /gnulib-tool.m4 /iconv.m4 /intdiv0.m4 /intl.m4 /intldir.m4 /intlmacosx.m4 /intmax.m4 /inttypes_h.m4 /inttypes-pri.m4 /lcmessage.m4 /lib-ld.m4 /lib-link.m4 /lib-prefix.m4 /libtool.m4 /lock.m4 /ltoptions.m4 /ltsugar.m4 /ltversion.m4 /lt~obsolete.m4 /nls.m4 /po.m4 /printf-posix.m4 /progtest.m4 /size_max.m4 /stdint_h.m4 /threadlib.m4 /visibility.m4 /xsize.m4 /wchar_t.m4 /wint_t.m4 /zzgnulib.m4 /00gnulib.m4 /largefile.m4 /absolute-header.m4 /errno_h.m4 /extensions.m4 /hostent.m4 /include_next.m4 /msvc-inval.m4 /msvc-nothrow.m4 /off_t.m4 /pid_t.m4 /socketlib.m4 /sockets.m4 /socklen.m4 /sockpfaf.m4 /ssize_t.m4 /stdalign.m4 /stddef_h.m4 /sys_socket_h.m4 /sys_types_h.m4 /sys_uio_h.m4 /unistd_h.m4 /warn-on-use.m4 /manywarnings-c++.m4 /manywarnings.m4 /warnings.m4 /locale_h.m4 /alloca.m4 /assert_h.m4 /atomic-cas.m4 /c-bool.m4 /dup2.m4 /gettimeofday.m4 /limits-h.m4 /multiarch.m4 /nanosleep.m4 /pselect.m4 /pthread-cond.m4 /pthread-mutex.m4 /pthread-once.m4 /pthread-rwlock.m4 /pthread-spin.m4 /pthread-thread.m4 /pthread-tss.m4 /pthread_h.m4 /pthread_mutex_timedlock.m4 /pthread_sigmask.m4 /raise.m4 /sched_h.m4 /select.m4 /signal_h.m4 /signalblocking.m4 /std-gnu11.m4 /stdint.m4 /sys_select_h.m4 /sys_time_h.m4 /time_h.m4 /chdir-long.m4 /close.m4 /closedir.m4 /d-ino.m4 /dirent_h.m4 /dirfd.m4 /double-slash-root.m4 /dup.m4 /eealloc.m4 /error.m4 /error_h.m4 /fchdir.m4 /fcntl.m4 /fcntl_h.m4 /fdopendir.m4 /filenamecat.m4 /free.m4 /fstat.m4 /fstatat.m4 /getcwd-abort-bug.m4 /getcwd-path-max.m4 /getcwd.m4 /getdtablesize.m4 /getpagesize.m4 /getprogname.m4 /inttypes.m4 /locale-fr.m4 /lstat.m4 /malloc.m4 /malloca.m4 /memchr.m4 /mempcpy.m4 /memrchr.m4 /mmap-anon.m4 /mode_t.m4 /musl.m4 /open-cloexec.m4 /open-slash.m4 /open.m4 /openat.m4 /opendir.m4 /pathmax.m4 /pipe.m4 /readdir.m4 /realloc.m4 /rewinddir.m4 /save-cwd.m4 /stat-time.m4 /stat.m4 /stdio_h.m4 /stdlib_h.m4 /strdup.m4 /strerror.m4 /string_h.m4 /sys_stat_h.m4 /unistd-safer.m4 /vararrays.m4 /wchar_h.m4 /calloc.m4 /exponentd.m4 /float_h.m4 /intmax_t.m4 /locale-ja.m4 /locale-zh.m4 /math_h.m4 /mbrtowc.m4 /mbstate_t.m4 /minmax.m4 /printf.m4 /reallocarray.m4 /stdarg.m4 /vasnprintf.m4 /vasprintf.m4 /xalloc.m4 /xvasprintf.m4 /exponentf.m4 /exponentl.m4 /fpieee.m4 /frexp.m4 /frexpl.m4 /isnand.m4 /isnanf.m4 /isnanl.m4 /ldexpl.m4 /nocrash.m4 /printf-frexp.m4 /printf-frexpl.m4 /signbit.m4 /vsnprintf-posix.m4 /vsnprintf.m4 /__inline.m4 /btowc.m4 /builtin-expect.m4 /ctype_h.m4 /flexmember.m4 /fnmatch.m4 /fnmatch_h.m4 /inline.m4 /isblank.m4 /iswblank.m4 /iswctype.m4 /iswdigit.m4 /iswpunct.m4 /iswxdigit.m4 /libunistring-base.m4 /localcharset.m4 /mbrtoc32.m4 /mbsinit.m4 /mbsrtowcs.m4 /mbtowc.m4 /setlocale_null.m4 /strnlen.m4 /uchar_h.m4 /unicase_h.m4 /unictype_h.m4 /uninorm_h.m4 /wctype.m4 /wctype_h.m4 /wmemchr.m4 /wmempcpy.m4 /getdelim.m4 /getline.m4 /langinfo_h.m4 /localeconv.m4 /nl_langinfo.m4 /pthread_rwlock_rdlock.m4 /regex.m4 /rpmatch.m4 /wcrtomb.m4 /yesno.m4 /servent.m4 /strsep.m4 plptools-1.0.26/m4/ax_check_gnu_make.m4000066400000000000000000000077271504470754400177000ustar00rootroot00000000000000# =========================================================================== # https://www.gnu.org/software/autoconf-archive/ax_check_gnu_make.html # =========================================================================== # # SYNOPSIS # # AX_CHECK_GNU_MAKE([run-if-true],[run-if-false]) # # DESCRIPTION # # This macro searches for a GNU version of make. If a match is found: # # * The makefile variable `ifGNUmake' is set to the empty string, otherwise # it is set to "#". This is useful for including a special features in a # Makefile, which cannot be handled by other versions of make. # * The makefile variable `ifnGNUmake' is set to #, otherwise # it is set to the empty string. This is useful for including a special # features in a Makefile, which can be handled # by other versions of make or to specify else like clause. # * The variable `_cv_gnu_make_command` is set to the command to invoke # GNU make if it exists, the empty string otherwise. # * The variable `ax_cv_gnu_make_command` is set to the command to invoke # GNU make by copying `_cv_gnu_make_command`, otherwise it is unset. # * If GNU Make is found, its version is extracted from the output of # `make --version` as the last field of a record of space-separated # columns and saved into the variable `ax_check_gnu_make_version`. # * Additionally if GNU Make is found, run shell code run-if-true # else run shell code run-if-false. # # Here is an example of its use: # # Makefile.in might contain: # # # A failsafe way of putting a dependency rule into a makefile # $(DEPEND): # $(CC) -MM $(srcdir)/*.c > $(DEPEND) # # @ifGNUmake@ ifeq ($(DEPEND),$(wildcard $(DEPEND))) # @ifGNUmake@ include $(DEPEND) # @ifGNUmake@ else # fallback code # @ifGNUmake@ endif # # Then configure.in would normally contain: # # AX_CHECK_GNU_MAKE() # AC_OUTPUT(Makefile) # # Then perhaps to cause gnu make to override any other make, we could do # something like this (note that GNU make always looks for GNUmakefile # first): # # if ! test x$_cv_gnu_make_command = x ; then # mv Makefile GNUmakefile # echo .DEFAULT: > Makefile ; # echo \ $_cv_gnu_make_command \$@ >> Makefile; # fi # # Then, if any (well almost any) other make is called, and GNU make also # exists, then the other make wraps the GNU make. # # LICENSE # # Copyright (c) 2008 John Darrington # Copyright (c) 2015 Enrico M. Crisostomo # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 12 AC_DEFUN([AX_CHECK_GNU_MAKE],dnl [AC_PROG_AWK AC_CACHE_CHECK([for GNU make],[_cv_gnu_make_command],[dnl _cv_gnu_make_command="" ; dnl Search all the common names for GNU make for a in "$MAKE" make gmake gnumake ; do if test -z "$a" ; then continue ; fi ; if "$a" --version 2> /dev/null | grep GNU 2>&1 > /dev/null ; then _cv_gnu_make_command=$a ; AX_CHECK_GNU_MAKE_HEADLINE=$("$a" --version 2> /dev/null | grep "GNU Make") ax_check_gnu_make_version=$(echo ${AX_CHECK_GNU_MAKE_HEADLINE} | ${AWK} -F " " '{ print $(NF); }') break ; fi done ;]) dnl If there was a GNU version, then set @ifGNUmake@ to the empty string, '#' otherwise AS_VAR_IF([_cv_gnu_make_command], [""], [AS_VAR_SET([ifGNUmake], ["#"])], [AS_VAR_SET([ifGNUmake], [""])]) AS_VAR_IF([_cv_gnu_make_command], [""], [AS_VAR_SET([ifnGNUmake], [""])], [AS_VAR_SET([ifnGNUmake], ["#"])]) AS_VAR_IF([_cv_gnu_make_command], [""], [AS_UNSET(ax_cv_gnu_make_command)], [AS_VAR_SET([ax_cv_gnu_make_command], [${_cv_gnu_make_command}])]) AS_VAR_IF([_cv_gnu_make_command], [""],[$2],[$1]) AC_SUBST([ifGNUmake]) AC_SUBST([ifnGNUmake]) ]) plptools-1.0.26/m4/ax_lib_readline.m4000066400000000000000000000121551504470754400173550ustar00rootroot00000000000000# =========================================================================== # https://www.gnu.org/software/autoconf-archive/ax_lib_readline.html # =========================================================================== # # SYNOPSIS # # AX_LIB_READLINE # AX_LIB_READLINE([MIN-VERSION]) # # DESCRIPTION # # Searches for a readline compatible library. If found, defines # `HAVE_LIBREADLINE'. If the found library has the `add_history' function, # sets also `HAVE_READLINE_HISTORY'. Also checks for the locations of the # necessary include files and sets `HAVE_READLINE_H' or # `HAVE_READLINE_READLINE_H' and `HAVE_READLINE_HISTORY_H' or # 'HAVE_HISTORY_H' if the corresponding include files exists. # # If given, MIN-VERSION specifies the minimum acceptable version; if that # version is not found, HAVE_LIBREADLINE and HAVE_READLINE_HISTORY will # not be defined. # # The libraries that may be readline compatible are `libedit', # `libeditline' and `libreadline'. Sometimes we need to link a termcap # library for readline to work, this macro tests these cases too by trying # to link with `libtermcap', `libcurses' or `libncurses' before giving up. # # Here is an example of how to use the information provided by this macro # to perform the necessary includes or declarations in a C file: # # #ifdef HAVE_LIBREADLINE # # if defined(HAVE_READLINE_READLINE_H) # # include # # elif defined(HAVE_READLINE_H) # # include # # else /* !defined(HAVE_READLINE_H) */ # extern char *readline (); # # endif /* !defined(HAVE_READLINE_H) */ # char *cmdline = NULL; # #else /* !defined(HAVE_READLINE_READLINE_H) */ # /* no readline */ # #endif /* HAVE_LIBREADLINE */ # # #ifdef HAVE_READLINE_HISTORY # # if defined(HAVE_READLINE_HISTORY_H) # # include # # elif defined(HAVE_HISTORY_H) # # include # # else /* !defined(HAVE_HISTORY_H) */ # extern void add_history (); # extern int write_history (); # extern int read_history (); # # endif /* defined(HAVE_READLINE_HISTORY_H) */ # /* no history */ # #endif /* HAVE_READLINE_HISTORY */ # # LICENSE # # Copyright (c) 2024 Reuben Thomas # Copyright (c) 2008 Ville Laurikari # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 9 AU_ALIAS([VL_LIB_READLINE], [AX_LIB_READLINE]) AC_DEFUN([AX_LIB_READLINE], [ AC_CACHE_CHECK([for a readline compatible library], ax_cv_lib_readline, [ ORIG_LIBS="$LIBS" for readline_lib in readline edit editline; do for termcap_lib in "" termcap curses ncurses; do if test -z "$termcap_lib"; then TRY_LIB="-l$readline_lib" else TRY_LIB="-l$readline_lib -l$termcap_lib" fi LIBS="$ORIG_LIBS $TRY_LIB" AC_LINK_IFELSE([AC_LANG_CALL([], [readline])], [ax_cv_lib_readline="$TRY_LIB"]) if test -n "$ax_cv_lib_readline"; then break fi done if test -n "$ax_cv_lib_readline"; then break fi done if test -z "$ax_cv_lib_readline"; then ax_cv_lib_readline="no" fi LIBS="$ORIG_LIBS" ]) if test "$ax_cv_lib_readline" != "no"; then LIBS="$LIBS $ax_cv_lib_readline" AC_CHECK_HEADERS(readline.h readline/readline.h) m4_if([$1], [], [ dnl No version given, so assume it is OK ax_cv_lib_readline_version_ok=yes ], [ dnl Check against given version AC_CACHE_CHECK([check readline is at least version $1], ax_cv_lib_readline_version_ok, [ AC_RUN_IFELSE([ AC_LANG_SOURCE([[ #include #if defined(HAVE_READLINE_READLINE_H) # include #elif defined(HAVE_READLINE_H) # include #endif int main(void) { float min_version = $1; int min_version_major = (int)min_version; int min_version_minor = (int)(min_version * 100.0); int min_version_hex = min_version_minor + (min_version_major * 256); return !(RL_READLINE_VERSION >= min_version_hex); } ]]) ], [ax_cv_lib_readline_version_ok=yes], [ax_cv_lib_readline_version_ok=no], [ax_cv_lib_readline_version_ok=no]) ]) ] ) if test "$ax_cv_lib_readline_version_ok" = "yes"; then AC_DEFINE(HAVE_LIBREADLINE, 1, [Define if you have a readline compatible library]) AC_CACHE_CHECK([whether readline supports history], ax_cv_lib_readline_history, [ ax_cv_lib_readline_history="no" AC_LINK_IFELSE([AC_LANG_CALL([], [add_history])], [ax_cv_lib_readline_history="yes"]) ]) if test "$ax_cv_lib_readline_history" = "yes"; then AC_DEFINE(HAVE_READLINE_HISTORY, 1, [Define if your readline library has \`add_history']) AC_CHECK_HEADERS(history.h readline/history.h) fi else LIBS="$ORIG_LIBS" fi fi ])dnl plptools-1.0.26/m4/glibc2.m4000066400000000000000000000014761504470754400154220ustar00rootroot00000000000000# glibc2.m4 serial 3 dnl Copyright (C) 2000-2002, 2004, 2008, 2010-2013 Free Software Foundation, dnl Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. # Test for the GNU C Library, version 2.0 or newer. # From Bruno Haible. AC_DEFUN([gt_GLIBC2], [ AC_CACHE_CHECK([whether we are using the GNU C Library 2 or newer], [ac_cv_gnu_library_2], [AC_EGREP_CPP([Lucky GNU user], [ #include #ifdef __GNU_LIBRARY__ #if (__GLIBC__ >= 2) && !defined __UCLIBC__ Lucky GNU user #endif #endif ], [ac_cv_gnu_library_2=yes], [ac_cv_gnu_library_2=no]) ] ) AC_SUBST([GLIBC2]) GLIBC2="$ac_cv_gnu_library_2" ] ) plptools-1.0.26/m4/glibc21.m4000066400000000000000000000016131504470754400154740ustar00rootroot00000000000000# glibc21.m4 serial 5 dnl Copyright (C) 2000-2002, 2004, 2008, 2010-2013 Free Software Foundation, dnl Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. # Test for the GNU C Library, version 2.1 or newer, or uClibc. # From Bruno Haible. AC_DEFUN([gl_GLIBC21], [ AC_CACHE_CHECK([whether we are using the GNU C Library >= 2.1 or uClibc], [ac_cv_gnu_library_2_1], [AC_EGREP_CPP([Lucky], [ #include #ifdef __GNU_LIBRARY__ #if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1) || (__GLIBC__ > 2) Lucky GNU user #endif #endif #ifdef __UCLIBC__ Lucky user #endif ], [ac_cv_gnu_library_2_1=yes], [ac_cv_gnu_library_2_1=no]) ] ) AC_SUBST([GLIBC21]) GLIBC21="$ac_cv_gnu_library_2_1" ] ) plptools-1.0.26/m4/longlong.m4000066400000000000000000000112031504470754400160640ustar00rootroot00000000000000# longlong.m4 serial 17 dnl Copyright (C) 1999-2007, 2009-2013 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl From Paul Eggert. # Define HAVE_LONG_LONG_INT if 'long long int' works. # This fixes a bug in Autoconf 2.61, and can be faster # than what's in Autoconf 2.62 through 2.68. # Note: If the type 'long long int' exists but is only 32 bits large # (as on some very old compilers), HAVE_LONG_LONG_INT will not be # defined. In this case you can treat 'long long int' like 'long int'. AC_DEFUN([AC_TYPE_LONG_LONG_INT], [ AC_REQUIRE([AC_TYPE_UNSIGNED_LONG_LONG_INT]) AC_CACHE_CHECK([for long long int], [ac_cv_type_long_long_int], [ac_cv_type_long_long_int=yes if test "x${ac_cv_prog_cc_c99-no}" = xno; then ac_cv_type_long_long_int=$ac_cv_type_unsigned_long_long_int if test $ac_cv_type_long_long_int = yes; then dnl Catch a bug in Tandem NonStop Kernel (OSS) cc -O circa 2004. dnl If cross compiling, assume the bug is not important, since dnl nobody cross compiles for this platform as far as we know. AC_RUN_IFELSE( [AC_LANG_PROGRAM( [[@%:@include @%:@ifndef LLONG_MAX @%:@ define HALF \ (1LL << (sizeof (long long int) * CHAR_BIT - 2)) @%:@ define LLONG_MAX (HALF - 1 + HALF) @%:@endif]], [[long long int n = 1; int i; for (i = 0; ; i++) { long long int m = n << i; if (m >> i != n) return 1; if (LLONG_MAX / 2 < m) break; } return 0;]])], [], [ac_cv_type_long_long_int=no], [:]) fi fi]) if test $ac_cv_type_long_long_int = yes; then AC_DEFINE([HAVE_LONG_LONG_INT], [1], [Define to 1 if the system has the type 'long long int'.]) fi ]) # Define HAVE_UNSIGNED_LONG_LONG_INT if 'unsigned long long int' works. # This fixes a bug in Autoconf 2.61, and can be faster # than what's in Autoconf 2.62 through 2.68. # Note: If the type 'unsigned long long int' exists but is only 32 bits # large (as on some very old compilers), AC_TYPE_UNSIGNED_LONG_LONG_INT # will not be defined. In this case you can treat 'unsigned long long int' # like 'unsigned long int'. AC_DEFUN([AC_TYPE_UNSIGNED_LONG_LONG_INT], [ AC_CACHE_CHECK([for unsigned long long int], [ac_cv_type_unsigned_long_long_int], [ac_cv_type_unsigned_long_long_int=yes if test "x${ac_cv_prog_cc_c99-no}" = xno; then AC_LINK_IFELSE( [_AC_TYPE_LONG_LONG_SNIPPET], [], [ac_cv_type_unsigned_long_long_int=no]) fi]) if test $ac_cv_type_unsigned_long_long_int = yes; then AC_DEFINE([HAVE_UNSIGNED_LONG_LONG_INT], [1], [Define to 1 if the system has the type 'unsigned long long int'.]) fi ]) # Expands to a C program that can be used to test for simultaneous support # of 'long long' and 'unsigned long long'. We don't want to say that # 'long long' is available if 'unsigned long long' is not, or vice versa, # because too many programs rely on the symmetry between signed and unsigned # integer types (excluding 'bool'). AC_DEFUN([_AC_TYPE_LONG_LONG_SNIPPET], [ AC_LANG_PROGRAM( [[/* For now, do not test the preprocessor; as of 2007 there are too many implementations with broken preprocessors. Perhaps this can be revisited in 2012. In the meantime, code should not expect #if to work with literals wider than 32 bits. */ /* Test literals. */ long long int ll = 9223372036854775807ll; long long int nll = -9223372036854775807LL; unsigned long long int ull = 18446744073709551615ULL; /* Test constant expressions. */ typedef int a[((-9223372036854775807LL < 0 && 0 < 9223372036854775807ll) ? 1 : -1)]; typedef int b[(18446744073709551615ULL <= (unsigned long long int) -1 ? 1 : -1)]; int i = 63;]], [[/* Test availability of runtime routines for shift and division. */ long long int llmax = 9223372036854775807ll; unsigned long long int ullmax = 18446744073709551615ull; return ((ll << 63) | (ll >> 63) | (ll < i) | (ll > i) | (llmax / ll) | (llmax % ll) | (ull << 63) | (ull >> 63) | (ull << i) | (ull >> i) | (ullmax / ull) | (ullmax % ull));]]) ]) plptools-1.0.26/m4/uintmax_t.m4000066400000000000000000000021311504470754400162550ustar00rootroot00000000000000# uintmax_t.m4 serial 12 dnl Copyright (C) 1997-2004, 2007-2010 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl From Paul Eggert. AC_PREREQ([2.13]) # Define uintmax_t to 'unsigned long' or 'unsigned long long' # if it is not already defined in or . AC_DEFUN([gl_AC_TYPE_UINTMAX_T], [ AC_REQUIRE([gl_AC_HEADER_INTTYPES_H]) AC_REQUIRE([gl_AC_HEADER_STDINT_H]) if test $gl_cv_header_inttypes_h = no && test $gl_cv_header_stdint_h = no; then AC_REQUIRE([AC_TYPE_UNSIGNED_LONG_LONG_INT]) test $ac_cv_type_unsigned_long_long_int = yes \ && ac_type='unsigned long long' \ || ac_type='unsigned long' AC_DEFINE_UNQUOTED([uintmax_t], [$ac_type], [Define to unsigned long or unsigned long long if and don't define.]) else AC_DEFINE([HAVE_UINTMAX_T], [1], [Define if you have the 'uintmax_t' type in or .]) fi ]) plptools-1.0.26/ncpd/000077500000000000000000000000001504470754400144125ustar00rootroot00000000000000plptools-1.0.26/ncpd/.gitignore000066400000000000000000000000061504470754400163760ustar00rootroot00000000000000/ncpd plptools-1.0.26/ncpd/Makefile.am000066400000000000000000000024501504470754400164470ustar00rootroot00000000000000# ncpd/Makefile.am # # This file is part of plptools. # # Copyright (C) 1999-2002 Fritz Elfert # Copyright (C) 2007-2024 Reuben Thomas # # 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 # along with this program; if not, see . sbin_PROGRAMS = ncpd ncpd_CPPFLAGS = -I$(top_srcdir)/lib -I$(top_srcdir)/libgnu -I$(top_builddir)/libgnu ncpd_CFLAGS = $(THREADED_CFLAGS) ncpd_CXXFLAGS = $(THREADED_CXXFLAGS) ncpd_LDADD = $(LIB_PLP) $(INTLLIBS) $(LIBPMULTITHREAD) $(LIBTHREAD) $(NANOSLEEP_LIB) $(PTHREAD_SIGMASK_LIB) $(SELECT_LIB) $(top_builddir)/libgnu/libgnu.a ncpd_SOURCES = channel.cc link.cc linkchan.cc main.cc \ ncp.cc packet.cc socketchan.cc mp_serial.c \ channel.h link.h linkchan.h main.h mp_serial.h ncp.h packet.h \ socketchan.h plptools-1.0.26/ncpd/channel.cc000066400000000000000000000054011504470754400163310ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999 Philip Proudman * Copyright (C) 1999-2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #include "config.h" #include #include #include #include #include "channel.h" #include "ncp.h" channel::channel(ncp * _ncpController) { verbose = 0; ncpChannel = 0; connectName = 0; ncpController = _ncpController; _terminate = false; } channel::~channel() { if (connectName) free((void *)connectName); } void channel:: ncpSend(bufferStore & a) { ncpController->send(ncpChannel, a); } bool channel:: terminate() { return _terminate; } void channel:: terminateWhenAsked() { _terminate = true; } void channel:: ncpConnect() { ncpController->connect(this); } void channel:: ncpRegister() { ncpController->Register(this); } void channel:: ncpDoRegisterAck(int ch, const char *name) { ncpController->RegisterAck(ch, name); } void channel:: ncpDisconnect() { ncpController->disconnect(ncpChannel); } PcServer *channel:: ncpFindPcServer(const char *name) { return ncpController->findPcServer(name); } void channel:: ncpRegisterPcServer(ppsocket *skt, const char *name) { ncpController->registerPcServer(skt, name); } void channel:: ncpUnregisterPcServer(PcServer *server) { ncpController->unregisterPcServer(server); } int channel:: ncpGetSpeed() { return ncpController->getSpeed(); } short int channel:: ncpProtocolVersion() { return ncpController->getProtocolVersion(); } void channel:: setNcpChannel(int chan) { ncpChannel = chan; } int channel:: getNcpChannel() { return ncpChannel; } void channel:: newNcpController(ncp * _ncpController) { ncpController = _ncpController; } void channel:: setVerbose(short int _verbose) { verbose = _verbose; } short int channel:: getVerbose() { return verbose; } const char * channel:: getNcpConnectName() { return connectName; } void channel:: setNcpConnectName(const char *name) { if (name) { if (connectName) free((void *)connectName); connectName = strdup(name); } } plptools-1.0.26/ncpd/channel.h000066400000000000000000000043651504470754400162030ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999 Philip Proudman * Copyright (C) 1999-2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #ifndef _channel_h_ #define _channel_h_ #include "config.h" #include class ncp; class bufferStore; class PcServer; class ppsocket; class channel { public: channel(ncp *ncpController); virtual ~channel() = 0; void newNcpController(ncp *ncpController); void setNcpChannel(int chan); int getNcpChannel(void); void ncpSend(bufferStore &a); void setVerbose(short int _verbose); short int getVerbose(); virtual void ncpDataCallback(bufferStore &a) = 0; virtual const char *getNcpRegisterName() = 0; void ncpConnect(); void ncpRegister(); void ncpDoRegisterAck(int ch, const char *name); virtual void ncpConnectAck() = 0; virtual void ncpConnectTerminate() = 0; virtual void ncpConnectNak() = 0; virtual void ncpRegisterAck() = 0; void ncpDisconnect(); short int ncpProtocolVersion(); const char *getNcpConnectName(); void setNcpConnectName(const char *); // The following two calls are used for destructing an instance bool terminate(); // Mainloop will terminate this class if true void terminateWhenAsked(); PcServer *ncpFindPcServer(const char *name); void ncpRegisterPcServer(ppsocket *skt, const char *name); void ncpUnregisterPcServer(PcServer *server); int ncpGetSpeed(); protected: short int verbose; const char *connectName; private: ncp *ncpController; int ncpChannel; bool _terminate; }; #endif plptools-1.0.26/ncpd/link.cc000066400000000000000000000365771504470754400157000ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999 Philip Proudman * Copyright (C) 1999-2002 Fritz Elfert * * 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 * along with this program; if not, see . * */ #include "config.h" #include #include #include #include #include #include #include #include #include "link.h" #include "packet.h" #include "ncp.h" #include "main.h" extern "C" { static void *expire_check(void *arg) { Link *l = (Link *)arg; pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); while (1) { usleep(l->retransTimeout() * 500); l->retransmit(); } } }; using namespace std; ENUM_DEFINITION_BEGIN(Link::link_type, Link::LINK_TYPE_UNKNOWN) stringRep.add(Link::LINK_TYPE_UNKNOWN, N_("Unknown")); stringRep.add(Link::LINK_TYPE_SIBO, N_("SIBO")); stringRep.add(Link::LINK_TYPE_EPOC, N_("EPOC")); ENUM_DEFINITION_END(Link::link_type) Link::Link(const char *fname, int baud, ncp *_ncp, unsigned short _verbose) : p(0) { theNCP = _ncp; verbose = _verbose; txSequence = 1; rxSequence = -1; failed = false; seqMask = 7; maxOutstanding = 1; linkType = LINK_TYPE_UNKNOWN; for (int i = 0; i < 256; i++) xoff[i] = false; // generate magic number for sendReqCon() srandom(time(NULL)); conMagic = random(); p = new packet(fname, baud, this, _verbose); pthread_mutex_init(&queueMutex, NULL); pthread_create(&checkthread, NULL, expire_check, this); // submit a link request sendReqReq(); } Link::~Link() { flush(); pthread_cancel(checkthread); pthread_mutex_destroy(&queueMutex); delete p; } unsigned long Link:: retransTimeout() { return ((unsigned long)getSpeed() * 1000 / 13200) + 200; } void Link:: reset() { txSequence = 1; rxSequence = -1; failed = false; seqMask = 7; maxOutstanding = 1; linkType = LINK_TYPE_UNKNOWN; purgeAllQueues(); for (int i = 0; i < 256; i++) xoff[i] = false; p->reset(); // submit a link request sendReqReq(); } unsigned short Link:: getVerbose() { return verbose; } void Link:: setVerbose(unsigned short _verbose) { verbose = _verbose; p->setVerbose(verbose); } void Link:: send(const bufferStore & buff) { if (buff.getLen() > 300) { failed = true; } else transmit(buff); } void Link:: purgeAllQueues() { pthread_mutex_lock(&queueMutex); ackWaitQueue.clear(); holdQueue.clear(); pthread_mutex_unlock(&queueMutex); } void Link:: purgeQueue(int channel) { pthread_mutex_lock(&queueMutex); vector::iterator i; for (i = ackWaitQueue.begin(); i != ackWaitQueue.end(); i++) if (i->data.getByte(0) == channel) { ackWaitQueue.erase(i); i--; } vector::iterator j; for (j = holdQueue.begin(); j != holdQueue.end(); j++) if (j->getByte(0) == channel) { holdQueue.erase(j); j--; } pthread_mutex_unlock(&queueMutex); } void Link:: sendAck(int seq) { if (hasFailed()) return; bufferStore tmp; if (verbose & LNK_DEBUG_LOG) lout << "Link: >> ack seq=" << seq << endl; if (seq > 7) { int hseq = seq >> 3; int lseq = (seq & 7) | 8; seq = (hseq << 8) + lseq; tmp.prependWord(seq); } else tmp.prependByte(seq); p->send(tmp); } void Link:: sendReqCon() { if (hasFailed()) return; bufferStore tmp; if (verbose & LNK_DEBUG_LOG) lout << "Link: >> con seq=4" << endl; tmp.addByte(0x24); tmp.addDWord(conMagic); ackWaitQueueElement e; e.seq = 0; // expected ACK is 0, _NOT_ 4! gettimeofday(&e.stamp, NULL); e.data = tmp; e.txcount = 4; pthread_mutex_lock(&queueMutex); ackWaitQueue.push_back(e); pthread_mutex_unlock(&queueMutex); p->send(tmp); } void Link:: sendReqReq() { if (hasFailed()) return; bufferStore tmp; if (verbose & LNK_DEBUG_LOG) lout << "Link: >> con seq=1" << endl; tmp.addByte(0x21); ackWaitQueueElement e; e.seq = 0; // expected response is Ack with seq=0 or ReqCon gettimeofday(&e.stamp, NULL); e.data = tmp; e.txcount = 4; pthread_mutex_lock(&queueMutex); ackWaitQueue.push_back(e); pthread_mutex_unlock(&queueMutex); p->send(tmp); } void Link:: sendReq() { if (hasFailed()) return; bufferStore tmp; if (verbose & LNK_DEBUG_LOG) lout << "Link: >> con seq=1" << endl; tmp.addByte(0x20); // No Ack expected for this, so no new entry in ackWaitQueue p->send(tmp); } void Link:: receive(bufferStore buff) { if (!p) return; vector::iterator i; bool ackFound; bool conFound; int type = buff.getByte(0); int seq = type & 0x0f; type &= 0xf0; // Support for incoming extended sequence numbers if (seq & 8) { int tseq = buff.getByte(1); buff.discardFirstBytes(2); seq = (tseq << 3) + (seq & 0x07); } else buff.discardFirstBytes(1); switch (type) { case 0x30: // Normal data if (verbose & LNK_DEBUG_LOG) { lout << "Link: << dat seq=" << seq ; if (verbose & LNK_DEBUG_DUMP) lout << " " << buff << endl; else lout << " len=" << buff.getLen() << endl; } if (((rxSequence + 1) & seqMask) == seq) { rxSequence++; rxSequence &= seqMask; sendAck(rxSequence); // Must check for XOFF/XON ncp frames HERE! if ((buff.getLen() == 3) && (buff.getByte(0) == 0)) { switch (buff.getByte(2)) { case 1: // XOFF xoff[buff.getByte(1)] = true; if (verbose & LNK_DEBUG_LOG) lout << "Link: got XOFF for channel " << buff.getByte(1) << endl; break; case 2: // XON xoff[buff.getByte(1)] = false; if (verbose & LNK_DEBUG_LOG) lout << "Link: got XON for channel " << buff.getByte(1) << endl; // Transmit packets on hold queue transmitHoldQueue(buff.getByte(1)); break; default: theNCP->receive(buff); } } else theNCP->receive(buff); } else { sendAck(rxSequence); if (verbose & LNK_DEBUG_LOG) lout << "Link: DUP\n"; } break; case 0x00: // Incoming ack // Find corresponding packet in ackWaitQueue ackFound = false; struct timeval refstamp; pthread_mutex_lock(&queueMutex); for (i = ackWaitQueue.begin(); i != ackWaitQueue.end(); i++) if (i->seq == seq) { ackFound = true; refstamp = i->stamp; ackWaitQueue.erase(i); if (verbose & LNK_DEBUG_LOG) { lout << "Link: << ack seq=" << seq ; if (verbose & LNK_DEBUG_DUMP) lout << " " << buff; lout << endl; } break; } pthread_mutex_unlock(&queueMutex); if (ackFound) { if ((linkType == LINK_TYPE_UNKNOWN) && (seq == 0)) { // If the remote device runs SIBO protocol, this ACK // should be 0 (the Ack on our ReqReq request, which is // treated as a normal Req by the SIBO machine. failed = false; linkType = LINK_TYPE_SIBO; seqMask = 7; maxOutstanding = 1; rxSequence = 0; txSequence = 1; purgeAllQueues(); p->setEpoc(false); if (verbose & LNK_DEBUG_LOG) lout << "Link: 1-linkType set to " << linkType << endl; } // Older packets implicitely ack'ed multiAck(refstamp); // Transmit waiting packets transmitWaitQueue(); } else { // If packet with seq+1 is in ackWaitQueue, resend it immediately // (Receiving an ack for a packet not on our wait queue is a // hint by the Psion about which was the last packet it // received successfully.) pthread_mutex_lock(&queueMutex); struct timeval now; gettimeofday(&now, NULL); bool nextFound = false; for (i = ackWaitQueue.begin(); i != ackWaitQueue.end(); i++) if (i->seq == seq+1) { nextFound = true; if (i->txcount-- == 0) { // timeout, remove packet if (verbose & LNK_DEBUG_LOG) lout << "Link: >> TRANSMIT timeout seq=" << i->seq << endl; ackWaitQueue.erase(i); i--; } else { // retransmit it i->stamp = now; if (verbose & LNK_DEBUG_LOG) lout << "Link: >> RETRANSMIT seq=" << i->seq << endl; p->send(i->data); } break; } pthread_mutex_unlock(&queueMutex); if ((verbose & LNK_DEBUG_LOG) && (!nextFound)) { lout << "Link: << UNMATCHED ack seq=" << seq; if (verbose & LNK_DEBUG_DUMP) lout << " " << buff; lout << endl; } } break; case 0x20: // New link conFound = false; if (seq > 3) { // May be a link confirm packet (EPOC) pthread_mutex_lock(&queueMutex); for (i = ackWaitQueue.begin(); i != ackWaitQueue.end(); i++) if ((i->seq == 0) && (i->data.getByte(0) == 0x21)) { ackWaitQueue.erase(i); linkType = LINK_TYPE_EPOC; if (verbose & LNK_DEBUG_LOG) lout << "Link: 2-linkType set to " << linkType << endl; conFound = true; failed = false; // EPOC can handle extended sequence numbers seqMask = 0x7ff; // EPOC can handle up to 8 unacknowledged packets maxOutstanding = 8; p->setEpoc(true); if (verbose & LNK_DEBUG_LOG) { lout << "Link: << con seq=" << seq ; if (verbose & LNK_DEBUG_DUMP) lout << " " << buff; lout << endl; } break; } pthread_mutex_unlock(&queueMutex); } if (conFound) { rxSequence = 0; txSequence = 1; sendAck(rxSequence); } else { if (verbose & LNK_DEBUG_LOG) { lout << "Link: << req seq=" << seq; if (verbose & LNK_DEBUG_DUMP) lout << " " << buff; lout << endl; } rxSequence = txSequence = 0; if (seq > 0) { linkType = LINK_TYPE_EPOC; if (verbose & LNK_DEBUG_LOG) lout << "Link: 3-linkType set to " << linkType << endl; // EPOC can handle extended sequence numbers seqMask = 0x7ff; // EPOC can handle up to 8 unacknowledged packets maxOutstanding = 8; p->setEpoc(true); failed = false; sendReqCon(); } else { // SIBO linkType = LINK_TYPE_SIBO; failed = false; seqMask = 7; maxOutstanding = 1; if (verbose & LNK_DEBUG_LOG) lout << "Link: 4-linkType set to " << linkType << endl; rxSequence = 0; txSequence = 1; // Our ReqReq was seq 0 purgeAllQueues(); p->setEpoc(false); sendAck(rxSequence); } } break; case 0x10: // Disconnect if (verbose & LNK_DEBUG_LOG) lout << "Link: << DISC" << endl; failed = true; break; default: lerr << "Link: FATAL: Unknown packet type " << type << endl; } } void Link:: transmitHoldQueue(int channel) { vector tmpQueue; vector::iterator i; // First, move desired packets to a temporary queue pthread_mutex_lock(&queueMutex); for (i = holdQueue.begin(); i != holdQueue.end(); i++) if (i->getByte(0) == channel) { tmpQueue.push_back(*i); holdQueue.erase(i); i--; } pthread_mutex_unlock(&queueMutex); // ... then transmit the moved packets for (i = tmpQueue.begin(); i != tmpQueue.end(); i++) transmit(*i); } void Link:: transmitWaitQueue() { vector tmpQueue; vector::iterator i; // First, move desired packets to a temporary queue for (i = waitQueue.begin(); i != waitQueue.end(); i++) tmpQueue.push_back(*i); waitQueue.clear(); // transmit the moved packets. If the backlock gets // full, they are put into waitQueue again. for (i = tmpQueue.begin(); i != tmpQueue.end(); i++) transmit(*i); } void Link:: transmit(bufferStore buf) { if (hasFailed()) return; int remoteChan = buf.getByte(0); if (xoff[remoteChan]) { pthread_mutex_lock(&queueMutex); holdQueue.push_back(buf); pthread_mutex_unlock(&queueMutex); } else { // If backlock is full, put on waitQueue int ql; pthread_mutex_lock(&queueMutex); ql = ackWaitQueue.size(); pthread_mutex_unlock(&queueMutex); if (ql >= maxOutstanding) { waitQueue.push_back(buf); return; } ackWaitQueueElement e; e.seq = txSequence++; txSequence &= seqMask; gettimeofday(&e.stamp, NULL); // An empty buffer is considered a new link request if (buf.empty()) { // Request for new link e.txcount = 4; if (verbose & LNK_DEBUG_LOG) lout << "Link: >> req seq=" << e.seq << endl; buf.prependByte(0x20 + e.seq); } else { e.txcount = 8; if (verbose & LNK_DEBUG_LOG) { lout << "Link: >> dat seq=" << e.seq; if (verbose & LNK_DEBUG_DUMP) lout << " " << buf; lout << endl; } if (e.seq > 7) { int hseq = e.seq >> 3; int lseq = 0x30 + ((e.seq & 7) | 8); int seq = (hseq << 8) + lseq; buf.prependWord(seq); } else buf.prependByte(0x30 + e.seq); } e.data = buf; pthread_mutex_lock(&queueMutex); ackWaitQueue.push_back(e); pthread_mutex_unlock(&queueMutex); p->send(buf); } } static void timesub(struct timeval *tv, unsigned long millisecs) { uint64_t micros = tv->tv_sec; uint64_t sub = millisecs; micros <<= 32; micros += tv->tv_usec; micros -= (sub * 1000); tv->tv_usec = micros & 0xffffffff; tv->tv_sec = (micros >>= 32) & 0xffffffff; } static bool olderthan(struct timeval t1, struct timeval t2) { uint64_t m1 = t1.tv_sec; uint64_t m2 = t2.tv_sec; m1 <<= 32; m2 <<= 32; m1 += t1.tv_usec; m2 += t2.tv_usec; return (m1 < m2); } void Link:: multiAck(struct timeval refstamp) { vector::iterator i; pthread_mutex_lock(&queueMutex); for (i = ackWaitQueue.begin(); i != ackWaitQueue.end(); i++) if (olderthan(i->stamp, refstamp)) { ackWaitQueue.erase(i); i--; } pthread_mutex_unlock(&queueMutex); } void Link:: retransmit() { if (hasFailed()) { purgeAllQueues(); return; } pthread_mutex_lock(&queueMutex); vector::iterator i; struct timeval now; gettimeofday(&now, NULL); struct timeval expired = now; timesub(&expired, retransTimeout()); for (i = ackWaitQueue.begin(); i != ackWaitQueue.end(); i++) if (olderthan(i->stamp, expired)) { if (i->txcount-- == 0) { // timeout, remove packet if (verbose & LNK_DEBUG_LOG) lout << "Link: >> TRANSMIT timeout seq=" << i->seq << endl; ackWaitQueue.erase(i); failed = true; i--; } else { // retransmit it i->stamp = now; if (verbose & LNK_DEBUG_LOG) lout << "Link: >> RETRANSMIT seq=" << i->seq << endl; p->send(i->data); } } pthread_mutex_unlock(&queueMutex); } void Link:: flush() { while (stuffToSend()) sleep(1); } bool Link:: stuffToSend() { return ((!failed) && (!ackWaitQueue.empty())); } bool Link:: hasFailed() { bool lfailed = p->linkFailed(); if (failed || lfailed) { if (verbose & LNK_DEBUG_LOG) lout << "Link: hasFailed: " << failed << ", " << lfailed << endl; } failed |= lfailed; return failed; } Enum Link:: getLinkType() { return linkType; } int Link:: getSpeed() { return p->getSpeed(); } plptools-1.0.26/ncpd/link.h000066400000000000000000000107761504470754400155330ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999 Philip Proudman * Copyright (C) 1999-2002 Fritz Elfert * * 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 * along with this program; if not, see . * */ #ifndef _link_h_ #define _link_h_ #include "config.h" #include #include #include "bufferstore.h" #include "bufferarray.h" #include "Enum.h" #include #define LNK_DEBUG_LOG 4 #define LNK_DEBUG_DUMP 8 class ncp; class packet; /** * Describes a transmitted packet which has not yet * been acknowledged by the peer. */ typedef struct { /** * Original sequence number. */ int seq; /** * Number of remaining transmit retries. */ int txcount; /** * Time of last transmit. */ struct timeval stamp; /** * Packet content. */ bufferStore data; } ackWaitQueueElement; extern "C" { static void *expire_check(void *); } class Link { public: enum link_type { LINK_TYPE_UNKNOWN = 0, LINK_TYPE_SIBO = 1, LINK_TYPE_EPOC = 2, }; /** * Construct a new link instance. * * @param fname Name of serial device. * @param baud Speed of serial device. * @param ncp The calling ncp instance. * @_verbose Verbosity (for debugging/troubleshooting) */ Link(const char *fname, int baud, ncp *_ncp, unsigned short _verbose = 0); /** * Disconnects from device and destroys instance. */ ~Link(); /** * Send a PLP packet to the Peer. * * @param buff The contents of the PLP packet. */ void send(const bufferStore &buff); /** * Query outstanding packets. * * @returns true, if packets are outstanding (not yet acknowledged), false * otherwise. */ bool stuffToSend(); /** * Query connection failure. * * @returns true, if the peer could not be contacted or did not response, * false if everything is ok. */ bool hasFailed(); /** * Reset connection and attempt to reconnect to the peer. */ void reset(); /** * Wait, until all outstanding packets are acknowledged or timed out. */ void flush(); /** * Purge all outstanding packets for a specified remote channel. * * @param channel The of the channel for which to remove outstanding * packets. */ void purgeQueue(int channel); /** * Set verbosity of Link and underlying packet instance. * * @param _verbose Verbosity (a bitmapped value, see LINK_DEBUG_.. constants) */ void setVerbose(unsigned short _verbose); /** * Get current verbosity of Link. * * @returns The verbosity, specified at construction or last call to * setVerbosity(); */ unsigned short getVerbose(); /** * Get the current link type. * * @returns One of LINK_TYPE_... values. */ Enum getLinkType(); /** * Get current speed of the serial device * * @returns The current speed in baud. */ int getSpeed(); private: friend class packet; friend void * expire_check(void *); void receive(bufferStore buf); void transmit(bufferStore buf); void sendAck(int seq); void sendReqReq(); void sendReqCon(); void sendReq(); void multiAck(struct timeval); void retransmit(); void transmitHoldQueue(int channel); void transmitWaitQueue(); void purgeAllQueues(); unsigned long retransTimeout(); pthread_t checkthread; pthread_mutex_t queueMutex; ncp *theNCP; packet *p; int txSequence; int rxSequence; int seqMask; int maxOutstanding; unsigned long conMagic; unsigned short verbose; bool failed; Enum linkType; std::vector ackWaitQueue; std::vector holdQueue; std::vector waitQueue; bool xoff[256]; }; #endif plptools-1.0.26/ncpd/linkchan.cc000066400000000000000000000061201504470754400165070ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999 Philip Proudman * Copyright (C) 1999-2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #include "config.h" #include #include #include #include #include #include "linkchan.h" #include "ncp.h" #include "main.h" using namespace std; linkChan::linkChan(ncp * _ncpController, int _ncpChannel):channel(_ncpController) { registerSer = 0x1234; if (_ncpChannel != -1) setNcpChannel(_ncpChannel); ncpConnect(); } void linkChan:: ncpDataCallback(bufferStore & a) { int len = a.getLen(); if (verbose & LINKCHAN_DEBUG_LOG) { lout << "linkchan: << msg "; if (verbose & LINKCHAN_DEBUG_DUMP) lout << a << endl; else lout << len << endl; } if ((len >= 5) && (a.getByte(0) == 1)) { char srvName[20]; unsigned int ser = a.getWord(1); int res = a.getWord(3); // int dontknow = a.getWord(5); bufferArray newStack; bufferStore se; strncpy(srvName, a.getString(7), 17); if (verbose & LINKCHAN_DEBUG_LOG) lout << "linkchan: received registerAck: ser=0x" << hex << setw(4) << setfill('0') << ser << " res=" << res << " srvName=\"" << srvName << "\"" << endl; while (!registerStack.empty()) { se = registerStack.pop(); if (se.getWord(0) == ser) { if (verbose & LINKCHAN_DEBUG_LOG) lout << "linkchan: found ser=0x" << hex << setw(4) << setfill('0') << se.getWord(0) << " on stack -> callBack to waiting chan" << endl; if (strlen(srvName) < 4) strcat(srvName, ".*"); ncpDoRegisterAck((int)se.getWord(2), srvName); } else newStack += se; } registerStack = newStack; return; } lerr << "linkchan: unknown message " << a.getByte(0) << endl; } const char *linkChan:: getNcpRegisterName() { return "LINK"; } void linkChan:: ncpConnectAck() { if (verbose & LINKCHAN_DEBUG_LOG) lout << "linkchan: << cack" << endl; } void linkChan:: ncpConnectTerminate() { if (verbose & LINKCHAN_DEBUG_LOG) lout << "linkchan: << ctrm" << endl; terminateWhenAsked(); } void linkChan:: ncpConnectNak() { ncpConnectTerminate(); } void linkChan:: Register(channel *ch) { bufferStore a; bufferStore stack; stack.addWord(registerSer); stack.addWord(ch->getNcpChannel()); registerStack += stack; a.addByte(0); a.addWord(registerSer++); a.addString(ch->getNcpRegisterName()); a.addByte(0); ncpSend(a); } plptools-1.0.26/ncpd/linkchan.h000066400000000000000000000025511504470754400163550ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999 Philip Proudman * Copyright (C) 1999-2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #ifndef _linkchan_h_ #define _linkchan_h_ #include "channel.h" #include #define LINKCHAN_DEBUG_LOG 1 #define LINKCHAN_DEBUG_DUMP 2 class linkChan : public channel { public: linkChan(ncp *ncpController, int ncpChannel = -1); void ncpDataCallback(bufferStore &a); const char *getNcpRegisterName(); void ncpConnectAck(); void ncpConnectTerminate(); void ncpConnectNak(); void ncpRegisterAck() {} void Register(channel *); private: int registerSer; bufferArray registerStack; }; #endif plptools-1.0.26/ncpd/main.cc000066400000000000000000000233541504470754400156540ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999 Philip Proudman * Copyright (C) 1999-2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ignore-value.h" #include "ncp.h" #include "socketchan.h" #include "linkchan.h" #include "link.h" #include "packet.h" #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include using namespace std; static bool verbose = false; static bool active = true; static bool autoexit = false; static ncp *theNCP = NULL; static IOWatch iow; static IOWatch accept_iow; static ppsocket skt; static int numScp = 0; static socketChan *scp[257]; // MAX_CHANNELS_PSION + 1 logbuf ilog(LOG_INFO, STDOUT_FILENO); logbuf dlog(LOG_DEBUG, STDOUT_FILENO); logbuf elog(LOG_ERR, STDERR_FILENO); ostream linf(&ilog); ostream lout(&dlog); ostream lerr(&elog); static void term_handler(int) { linf << _("Got SIGTERM") << endl; signal(SIGTERM, term_handler); active = false; }; static void int_handler(int) { linf << _("Got SIGINT") << endl; signal(SIGINT, int_handler); active = false; }; void checkForNewSocketConnection() { string peer; if (accept_iow.watch(5,0) <= 0) { return; } ppsocket *next = skt.accept(&peer, &iow); if (next != NULL) { next->setWatch(&iow); // New connect if (verbose) lout << "New socket connection from " << peer << endl; if ((numScp >= theNCP->maxLinks()) || (!theNCP->gotLinkChannel())) { bufferStore a; // Give the client time to send its version request. next->dataToGet(1, 0); next->getBufferStore(a, false); a.init(); a.addStringT("No Psion Connected\n"); next->sendBufferStore(a); delete next; if (verbose) lout << "rejected" << endl; } else scp[numScp++] = new socketChan(next, theNCP); } } void * pollSocketConnections(void *) { while (active) { iow.watch(0, 10000); for (int i = 0; i < numScp; i++) { scp[i]->socketPoll(); if (scp[i]->terminate()) { // Requested channel termination delete scp[i]; numScp--; for (int j = i; j < numScp; j++) scp[j] = scp[j + 1]; i--; } } } return NULL; } static void help() { cout << _( "Usage: ncpd [OPTIONS]...\n" "\n" "Supported options:\n" "\n" " -d, --dontfork Run in foreground, don't fork\n" " -h, --help Display this text.\n" " -V, --version Print version and exit.\n" " -e, --autoexit Exit after device is disconnected.\n" " -v, --verbose=LOGCLASS Enable logging of LOGCLASS events\n" " Valid log classes are:\n" " m - main program\n" " nl - NCP protocol log\n" " nd - NCP protocol data dump\n" " ll - PLP protocol log\n" " ld - PLP protocol data dump\n" " pl - physical I/O log\n" " ph - physical I/O handshake\n" " pd - physical I/O data dump\n" " all - All of the above\n" " -s, --serial=DEV Use serial device DEV.\n" " -b, --baudrate=RATE Set serial speed to BAUD.\n" ); cout << #if DSPEED > 0 #define SPEEDSTR(x) #x _(" Default: ") << DSPEED << ".\n"; #else _(" Default: Autocycle 115.2k, 57.6k 38.4k, 19.2k\n"); #endif cout << _( " -p, --port=[HOST:]PORT Listen on host HOST, port PORT.\n" " Default for HOST: 127.0.0.1\n" " Default for PORT: " ) << DPORT << "\n\n"; } static void usage() { cerr << _("Try `ncpd --help' for more information") << endl; } static struct option opts[] = { {"dontfork", no_argument, 0, 'd'}, {"autoexit", no_argument, 0, 'e'}, {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, {"verbose", required_argument, 0, 'v'}, {"port", required_argument, 0, 'p'}, {"serial", required_argument, 0, 's'}, {"baudrate", required_argument, 0, 'b'}, {NULL, 0, 0, 0 } }; static void parse_destination(const char *arg, const char **host, int *port) { if (!arg) return; // We don't want to modify argv, therefore copy it first ... char *argcpy = strdup(arg); char *pp = strchr(argcpy, ':'); if (pp) { // host.domain:400 // 10.0.0.1:400 *pp ++= '\0'; *host = argcpy; } else { // 400 // host.domain // host // 10.0.0.1 if (strchr(argcpy, '.') || !isdigit(argcpy[0])) { *host = argcpy; pp = 0L; } else pp = argcpy; } if (pp) *port = atoi(pp); } static void * link_thread(void *arg) { while (active) { // psion iow.watch(1, 0); if (theNCP->hasFailed()) { if (autoexit) { active = false; break; } iow.watch(5, 0); if (verbose) lout << "ncp: restarting\n"; theNCP->reset(); } } return NULL; } int main(int argc, char **argv) { int pid; bool dofork = true; int sockNum = DPORT; int baudRate = DSPEED; const char *host = "127.0.0.1"; const char *serialDevice = NULL; unsigned short nverbose = 0; struct servent *se = getservbyname("psion", "tcp"); dlog.setOn(false); elog.setOn(false); endservent(); if (se != 0L) sockNum = ntohs(se->s_port); while (1) { int c = getopt_long(argc, argv, "hdeVb:s:p:v:", opts, NULL); if (c == -1) break; switch (c) { case '?': usage(); return -1; case 'V': cout << _("ncpd Version ") << VERSION << endl; return 0; case 'h': help(); return 0; case 'v': if (!strcmp(optarg, "nl")) nverbose |= NCP_DEBUG_LOG; if (!strcmp(optarg, "nd")) nverbose |= NCP_DEBUG_DUMP; if (!strcmp(optarg, "ll")) nverbose |= LNK_DEBUG_LOG; if (!strcmp(optarg, "ld")) nverbose |= LNK_DEBUG_DUMP; if (!strcmp(optarg, "pl")) nverbose |= PKT_DEBUG_LOG; if (!strcmp(optarg, "pd")) nverbose |= PKT_DEBUG_DUMP; if (!strcmp(optarg, "ph")) nverbose |= PKT_DEBUG_HANDSHAKE; if (!strcmp(optarg, "m")) verbose = true; if (!strcmp(optarg, "all")) { nverbose = NCP_DEBUG_LOG | NCP_DEBUG_DUMP | LNK_DEBUG_LOG | LNK_DEBUG_DUMP | PKT_DEBUG_LOG | PKT_DEBUG_DUMP | PKT_DEBUG_HANDSHAKE; verbose = true; } break; case 'd': dofork = 0; break; case 'e': autoexit = true; break; case 'b': if (!strcmp(optarg, "auto")) baudRate = -1; else baudRate = atoi(optarg); break; case 's': serialDevice = optarg; break; case 'p': parse_destination(optarg, &host, &sockNum); break; } } if (optind < argc) { usage(); return -1; } if (serialDevice == NULL) serialDevice = DDEV; if (dofork) pid = fork(); else pid = 0; switch (pid) { case 0: signal(SIGTERM, term_handler); signal(SIGINT, int_handler); skt.setWatch(&accept_iow); if (!skt.listen(host, sockNum)) cerr << "listen on " << host << ":" << sockNum << ": " << strerror(errno) << endl; else { if (dofork) { openlog("ncpd", LOG_CONS|LOG_PID, LOG_DAEMON); dlog.setOn(true); elog.setOn(true); ilog.setOn(true); linf << _("daemon started. Listening at ") << host << ":" << sockNum << _(" using device ") << serialDevice << endl; setsid(); ignore_value(chdir("/")); int devnull = open("/dev/null", O_RDWR, 0); if (devnull != -1) { dup2(devnull, STDIN_FILENO); dup2(devnull, STDOUT_FILENO); dup2(devnull, STDERR_FILENO); if (devnull > 2) close(devnull); } } memset(scp, 0, sizeof(scp)); theNCP = new ncp(serialDevice, baudRate, nverbose); if (!theNCP) { lerr << "Could not create NCP object" << endl; exit(-1); } pthread_t thr_a, thr_b; if (pthread_create(&thr_a, NULL, link_thread, NULL) != 0) { lerr << "Could not create Link thread" << endl; exit(-1); } if (pthread_create(&thr_b, NULL, pollSocketConnections, NULL) != 0) { lerr << "Could not create Socket thread" << endl; exit(-1); } while (active) checkForNewSocketConnection(); linf << _("terminating") << endl; void *ret; pthread_join(thr_a, &ret); linf << _("joined Link thread") << endl; pthread_join(thr_b, &ret); linf << _("joined Socket thread") << endl; delete theNCP; linf << _("shut down NCP") << endl; } skt.closeSocket(); linf << _("socket closed") << endl; break; case -1: lerr << "fork: " << strerror(errno) << endl; break; default: exit(0); } linf << _("normal exit") << endl; return 0; } plptools-1.0.26/ncpd/main.h000066400000000000000000000017301504470754400155100ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999 Philip Proudman * Copyright (C) 1999-2002 Fritz Elfert * * 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 * along with this program; if not, see . * */ #ifndef _main_h_ #define _main_h_ #include extern std::ostream lout; extern std::ostream lerr; extern std::ostream linf; #endif plptools-1.0.26/ncpd/mp_serial.c000066400000000000000000000100761504470754400165350ustar00rootroot00000000000000/* // PLP - An implementation of the PSION link protocol // // The code in this file was written by Rudolf Koenig // (rfkoenig@immd4.informatik.uni-erlangen.de). (from his p3nfs code) // The Copyright remains his. // // 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 // along with this program; if not, see . // // e-mail philip.proudman@btinternet.com */ #include "config.h" #include #include #include /* for usleep() */ #include /* for bzero() */ #include #if defined(linux) || defined(_IBMR2) || \ (defined(__APPLE__) && defined(__MACH__)) || \ defined(__NetBSD__) || defined(__FreeBSD__) #include /* for ioctl() */ #endif #include #ifdef sun #include /* sun has TIOCEXCL there */ #endif #include #ifdef hpux #include #include #endif #include "mp_serial.h" #ifdef __sgi #define CRTSCTS CNEW_RTSCTS #endif #ifndef O_NOCTTY #define O_NOCTTY 0 #endif int init_serial(const char *dev, int speed, int debug) { int fd, baud; int uid, euid; struct termios ti; #ifdef hpux struct termiox tx; #endif static struct baud { int speed, baud; } btable[] = { { 9600, B9600 }, #ifdef B19200 { 19200, B19200 }, #else #ifdef EXTA { 19200, EXTA }, #endif #endif #ifdef B38400 { 38400, B38400 }, #else #ifdef EXTB { 38400, EXTB }, #endif #endif #ifdef B57600 { 57600, B57600 }, #endif #ifdef B115200 { 115200, B115200 }, #endif { 4800, B4800 }, { 2400, B2400 }, { 1200, B1200 }, { 300, B300 }, { 75, B75 }, { 50, B50 }, { 0, 0 } }, *bptr; if (speed) { for (bptr = btable; bptr->speed; bptr++) if (bptr->speed == speed) break; if (!bptr->baud) { fprintf(stderr, "Cannot match selected speed %d\n", speed); exit(1); } baud = bptr->baud; } else baud = 0; if (debug) printf("using %s...\n", dev); euid = geteuid(); uid = getuid(); #ifdef hpux #define seteuid(a) setresuid(-1, a, -1) #endif if (seteuid(uid)) { perror("seteuid"); exit(1); } if ((fd = open(dev, O_RDWR | O_NOCTTY, 0)) < 0) { perror(dev); exit(1); } if (seteuid(euid)) { perror("seteuid back"); exit(1); } if (debug) printf("open done\n"); #ifdef TIOCEXCL ioctl(fd, TIOCEXCL, (char *) 0); /* additional open() calls shall fail */ #else fprintf(stderr, "WARNING: opened %s non-exclusive!\n", dev); #endif memset(&ti, 0, sizeof(struct termios)); ti.c_cflag = CS8 | HUPCL | CLOCAL | CREAD; #if defined(sun) || defined(linux) || defined(__sgi) || \ (defined(__APPLE__) && defined(__MACH__)) || \ defined(__NetBSD__) || defined(__FreeBSD__) ti.c_cflag |= CRTSCTS; ti.c_iflag = IGNBRK | IGNPAR; ti.c_cc[VMIN] = 1; ti.c_cc[VTIME] = 0; #endif cfsetispeed(&ti, baud); cfsetospeed(&ti, baud); if (tcsetattr(fd, TCSADRAIN, &ti) < 0) perror("tcsetattr TCSADRAIN"); #ifdef hpux bzero(&tx, sizeof(struct termiox)); tx.x_hflag = RTSXOFF | CTSXON; if (ioctl(fd, TCSETXW, &tx) < 0) perror("TCSETXW"); #endif #if defined(_IBMR2) ioctl(fd, TXDELCD, "dtr"); ioctl(fd, TXDELCD, "xon"); ioctl(fd, TXADDCD, "rts"); /* That's how AIX does CRTSCTS */ #endif return fd; } void ser_exit(int fd) { struct termios ti; #ifdef TIOCNXCL ioctl(fd, TIOCNXCL, (char *) 0); #endif if (tcgetattr(fd, &ti) < 0) perror("tcgetattr"); ti.c_cflag &= ~CRTSCTS; if (tcsetattr(fd, TCSANOW, &ti) < 0) perror("tcsetattr"); (void) close(fd); } plptools-1.0.26/ncpd/mp_serial.h000066400000000000000000000020171504470754400165360ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999 Philip Proudman * Copyright (C) 1999-2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #ifndef _mp_serial_h #define _mp_serial_h #ifdef __cplusplus extern "C" { #endif int init_serial(const char *dev, int speed, int debug); void ser_exit(int fd); #ifdef __cplusplus } #endif #endif plptools-1.0.26/ncpd/ncp.cc000066400000000000000000000311411504470754400155010ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999 Philip Proudman * Copyright (C) 1999-2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #include "config.h" #include #include #include #include #include #include #include "ncp.h" #include "linkchan.h" #include "link.h" #include "main.h" #define MAX_CHANNELS_PSION 256 #define MAX_CHANNELS_SIBO 8 #define NCP_SENDLEN 250 using namespace std; ncp::ncp(const char *fname, int baud, unsigned short _verbose) { channelPtr = new channel*[MAX_CHANNELS_PSION + 1]; assert(channelPtr); messageList = new bufferStore[MAX_CHANNELS_PSION + 1]; assert(messageList); remoteChanList = new int[MAX_CHANNELS_PSION + 1]; assert(remoteChanList); failed = false; verbose = _verbose; // until detected on receipt of INFO we use these. maxChannels = MAX_CHANNELS_SIBO; protocolVersion = PV_SERIES_5; lChan = NULL; // init channels for (int i = 0; i < MAX_CHANNELS_PSION; i++) channelPtr[i] = NULL; l = new Link(fname, baud, this, verbose); assert(l); } ncp::~ncp() { bufferStore b; for (int i = 0; i < maxLinks(); i++) { if (isValidChannel(i)) { bufferStore b2; b2.addByte(remoteChanList[i]); controlChannel(i, NCON_MSG_CHANNEL_DISCONNECT, b2); } channelPtr[i] = NULL; } controlChannel(0, NCON_MSG_NCP_END, b); delete l; delete [] channelPtr; delete [] remoteChanList; delete [] messageList; } int ncp:: maxLinks() { return maxChannels; } void ncp:: reset() { for (int i = 0; i < maxLinks(); i++) { if (isValidChannel(i)) channelPtr[i]->terminateWhenAsked(); channelPtr[i] = NULL; } failed = false; if (lChan) delete(lChan); lChan = NULL; protocolVersion = PV_SERIES_5; // until detected on receipt of INFO l->reset(); } unsigned short ncp:: getVerbose() { return verbose; } void ncp:: setVerbose(unsigned short _verbose) { verbose = _verbose; l->setVerbose(verbose); } short int ncp:: getProtocolVersion() { return protocolVersion; } void ncp:: receive(bufferStore s) { if (s.getLen() > 1) { int channel = s.getByte(0); s.discardFirstBytes(1); if (channel == 0) { decodeControlMessage(s); } else { int allData = s.getByte(1); s.discardFirstBytes(2); if (protocolVersion == PV_SERIES_3) { channel = lastSentChannel; } if (!isValidChannel(channel)) { lerr << "ncp: Got message for unknown channel\n"; } else { messageList[channel].addBuff(s); if (allData == LAST_MESS) { channelPtr[channel]->ncpDataCallback(messageList[channel]); messageList[channel].init(); } else if (allData != NOT_LAST_MESS) { lerr << "ncp: bizarre third byte!\n"; } } } } else lerr << "Got null message\n"; } void ncp:: controlChannel(int chan, enum interControllerMessageType t, bufferStore & command) { bufferStore open; open.addByte(0); // control open.addByte(chan); open.addByte(t); open.addBuff(command); if (verbose & NCP_DEBUG_LOG) lout << "ncp: >> " << ctrlMsgName(t) << " " << chan << endl; l->send(open); } PcServer *ncp:: findPcServer(const char *name) { if (name) { vector::iterator i; for (i = pcServers.begin(); i != pcServers.end(); i++) if (i->getName() == name) return i->self(); } return NULL; } void ncp:: registerPcServer(ppsocket *skt, const char *name) { pcServers.push_back(PcServer(skt, name)); } void ncp:: unregisterPcServer(PcServer *server) { if (server) { vector::iterator i; for (i = pcServers.begin(); i != pcServers.end(); i++) if (i->self() == server) { pcServers.erase(i); return; } } } void ncp:: decodeControlMessage(bufferStore & buff) { int remoteChan = buff.getByte(0); interControllerMessageType imt = (interControllerMessageType)buff.getByte(1); buff.discardFirstBytes(2); if (verbose & NCP_DEBUG_LOG) lout << "ncp: << " << ctrlMsgName(imt) << " " << remoteChan; bufferStore b; int localChan; switch (imt) { case NCON_MSG_CONNECT_TO_SERVER: if (verbose & NCP_DEBUG_LOG) { if (verbose & NCP_DEBUG_DUMP) lout << " [" << buff << "]"; lout << endl; } failed = false; if (!strcmp(buff.getString(0), "LINK.*")) { if (lChan) localChan = lChan->getNcpChannel(); else localChan = getFirstUnusedChan(); // Ack with connect response b.addByte(remoteChan); b.addByte(0); controlChannel(localChan, NCON_MSG_CONNECT_RESPONSE, b); if (verbose & NCP_DEBUG_LOG) lout << "ncp: Link UP" << endl; linf << _("Connected with a S") << ((protocolVersion == PV_SERIES_5) ? 5 : 3) << _(" at ") << getSpeed() << _("baud") << endl; // Create linkchan if it does not yet exist if (!lChan) { if (verbose & NCP_DEBUG_LOG) lout << "ncp: new passive linkChan" << endl; channelPtr[localChan] = lChan = new linkChan(this, localChan); lChan->setVerbose(verbose); } lChan->ncpConnectAck(); } else { PcServer *s = findPcServer(buff.getString(0)); bool ok = false; if (s) { localChan = getFirstUnusedChan(); ok = s->clientConnect(localChan, remoteChan); if (!ok) // release channel ptr channelPtr[localChan] = NULL; } b.addByte(remoteChan); if (ok) { b.addByte(rfsv::E_PSI_GEN_NONE); if (verbose & NCP_DEBUG_LOG) lout << "ncp: ACCEPT client connect" << endl; } else { localChan = 0; b.addByte(rfsv::E_PSI_FILE_NXIST); if (verbose & NCP_DEBUG_LOG) lout << "ncp: REJECT client connect" << endl; } controlChannel(localChan, NCON_MSG_CONNECT_RESPONSE, b); // Create linkchan if it does not yet exist if (!lChan) { if (verbose & NCP_DEBUG_LOG) lout << "ncp: new active linkChan" << endl; channelPtr[localChan] = lChan = new linkChan(this, -1); lChan->setVerbose(verbose); } } break; case NCON_MSG_CONNECT_RESPONSE: int forChan; failed = false; forChan = buff.getByte(0); if (verbose & NCP_DEBUG_LOG) lout << " ch=" << forChan << " stat="; if (buff.getByte(1) == 0) { if (verbose & NCP_DEBUG_LOG) lout << "OK" << endl; if (isValidChannel(forChan)) { remoteChanList[forChan] = remoteChan; channelPtr[forChan]->ncpConnectAck(); } else { if (verbose & NCP_DEBUG_LOG) lout << "ncp: message for unknown channel" << endl; } } else { if (verbose & NCP_DEBUG_LOG) lout << "Unknown " << (int) buff.getByte(1) << endl; if (isValidChannel(forChan)) channelPtr[forChan]->ncpConnectNak(); } break; case NCON_MSG_NCP_INFO: int ver; failed = false; ver = buff.getByte(0); // Series 3c returns '3', as does mclink. PsiWin 1.1 // returns version 2. We return whatever version we're // sent, which is rather crude, but works for Series 3 // and probably 5. If Symbian have changed EPOC Connect // for the Series 5mx/7, this may need to change. // if (ver == PV_SERIES_5 || ver == PV_SERIES_3) { bufferStore b; protocolVersion = ver; if (verbose & NCP_DEBUG_LOG) { if (verbose & NCP_DEBUG_DUMP) lout << " [" << buff << "]"; lout << endl; } // Fake NCP version 2 for a Series 3 (behave like PsiWin 1.1) if (ver == PV_SERIES_3) { ver = 2; } else { // Series 5 supports more channels maxChannels = MAX_CHANNELS_PSION; } b.addByte(ver); // Do we send a time of 0 or a real time? // The Psion uses this to determine whether to // restart. (See protocol docs for details) b.addDWord(time(NULL)); controlChannel(0, NCON_MSG_NCP_INFO, b); } else { lout << "ALERT!!!! Unexpected Protocol Version!! (Not Series 3/5?)!" << endl; failed = true; } break; case NCON_MSG_CHANNEL_DISCONNECT: if (verbose & NCP_DEBUG_LOG) lout << " ch=" << (int) buff.getByte(0) << endl; disconnect(buff.getByte(0)); l->purgeQueue(remoteChan); break; case NCON_MSG_DATA_XOFF: case NCON_MSG_DATA_XON: case NCON_MSG_CHANNEL_CLOSED: case NCON_MSG_NCP_END: default: if (verbose & NCP_DEBUG_LOG) { if (verbose & NCP_DEBUG_DUMP) lout << " [" << buff << "]"; lout << endl; } } } int ncp:: getFirstUnusedChan() { for (int cNum = 1; cNum < maxLinks(); cNum++) { if (channelPtr[cNum] == NULL) { if (verbose & NCP_DEBUG_LOG) lout << "ncp: getFirstUnusedChan=" << cNum << endl; channelPtr[cNum] = (channel *)0xdeadbeef; return cNum; } } return 0; } bool ncp:: isValidChannel(int channel) { return (channelPtr[channel] && ((long)channelPtr[channel] != 0xdeadbeef)); } void ncp:: RegisterAck(int chan, const char *name) { if (verbose & NCP_DEBUG_LOG) lout << "ncp: RegisterAck: chan=" << chan << endl; for (int cNum = 1; cNum < maxLinks(); cNum++) { channel *ch = channelPtr[cNum]; if (isValidChannel(cNum) && ch->getNcpChannel() == chan) { ch->setNcpConnectName(name); ch->ncpRegisterAck(); return; } } lerr << "ncp: RegisterAck: no channel to deliver" << endl; } void ncp:: Register(channel * ch) { if (lChan) { int cNum = ch->getNcpChannel(); if (cNum == 0) cNum = getFirstUnusedChan(); if (cNum > 0) { channelPtr[cNum] = ch; ch->setNcpChannel(cNum); lChan->Register(ch); } else lerr << "ncp: Out of channels in register" << endl; } else lerr << "ncp: Register without established lChan" << endl; } int ncp:: connect(channel * ch) { // look for first unused chan int cNum = ch->getNcpChannel(); if (cNum == 0) cNum = getFirstUnusedChan(); if (cNum > 0) { channelPtr[cNum] = ch; ch->setNcpChannel(cNum); bufferStore b; if (ch->getNcpConnectName()) b.addString(ch->getNcpConnectName()); else { b.addString(ch->getNcpRegisterName()); b.addString(".*"); } b.addByte(0); controlChannel(cNum, NCON_MSG_CONNECT_TO_SERVER, b); return cNum; } return -1; } void ncp:: send(int channel, bufferStore & a) { bool last; do { last = true; if (a.getLen() > NCP_SENDLEN) last = false; bufferStore out; out.addByte(remoteChanList[channel]); out.addByte(channel); if (last) { out.addByte(LAST_MESS); } else { out.addByte(NOT_LAST_MESS); } out.addBuff(a, NCP_SENDLEN); a.discardFirstBytes(NCP_SENDLEN); l->send(out); } while (!last); lastSentChannel = channel; } void ncp:: disconnect(int channel) { if (!isValidChannel(channel)) { lerr << "ncp: Ignored disconnect for unknown channel #" << channel << endl; return; } channelPtr[channel]->terminateWhenAsked(); if (verbose & NCP_DEBUG_LOG) lout << "ncp: disconnect: channel=" << channel << endl; channelPtr[channel] = NULL; bufferStore b; b.addByte(remoteChanList[channel]); controlChannel(channel, NCON_MSG_CHANNEL_DISCONNECT, b); } bool ncp:: stuffToSend() { return l->stuffToSend(); } bool ncp:: hasFailed() { bool lfailed = l->hasFailed(); if (failed || lfailed) { if (verbose & NCP_DEBUG_LOG) lout << "ncp: hasFailed: " << failed << ", " << lfailed << endl; } failed |= lfailed; if (failed) { if (lChan) { channelPtr[lChan->getNcpChannel()] = NULL; delete lChan; } lChan = NULL; } return failed; } bool ncp:: gotLinkChannel() { return (lChan != NULL); } int ncp:: getSpeed() { return l->getSpeed(); } const char *ncp:: ctrlMsgName(unsigned char msgType) { switch (msgType) { case NCON_MSG_DATA_XOFF: return "NCON_MSG_DATA_XOFF"; case NCON_MSG_DATA_XON: return "NCON_MSG_DATA_XON"; case NCON_MSG_CONNECT_TO_SERVER: return "NCON_MSG_CONNECT_TO_SERVER"; case NCON_MSG_CONNECT_RESPONSE: return "NCON_MSG_CONNECT_RESPONSE"; case NCON_MSG_CHANNEL_CLOSED: return "NCON_MSG_CHANNEL_CLOSED"; case NCON_MSG_NCP_INFO: return "NCON_MSG_NCP_INFO"; case NCON_MSG_CHANNEL_DISCONNECT: return "NCON_MSG_CHANNEL_DISCONNECT"; case NCON_MSG_NCP_END: return "NCON_MSG_NCP_END"; } return "NCON_MSG_UNKNOWN"; } plptools-1.0.26/ncpd/ncp.h000066400000000000000000000061431504470754400153470ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999 Philip Proudman * Copyright (C) 1999-2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #ifndef _ncp_h_ #define _ncp_h_ #include "config.h" #include #include "bufferstore.h" #include "linkchan.h" #include "ppsocket.h" class Link; class channel; #define NCP_DEBUG_LOG 1 #define NCP_DEBUG_DUMP 2 /** * Representation of a server process on the PC * A dummy which does not allow connects for now. */ class PcServer { public: PcServer(ppsocket *, std::string _name) { name = _name; } ~PcServer() {} bool clientConnect(int, int) { return false; } std::string getName() { return name; } PcServer *self() { return this; } private: std::string name; }; class ncp { public: ncp(const char *fname, int baud, unsigned short _verbose = 0); ~ncp(); int connect(channel *c); // returns channel, or -1 if failure void Register(channel *c); void RegisterAck(int, const char *); void disconnect(int channel); void send(int channel, bufferStore &a); void reset(); int maxLinks(); bool stuffToSend(); bool hasFailed(); bool gotLinkChannel(); PcServer *findPcServer(const char *name); void registerPcServer(ppsocket *skt, const char *name); void unregisterPcServer(PcServer *server); void setVerbose(unsigned short); unsigned short getVerbose(); short int getProtocolVersion(); int getSpeed(); private: friend class Link; enum c { MAX_LEN = 200, LAST_MESS = 1, NOT_LAST_MESS = 2 }; enum interControllerMessageType { // Inter controller message types NCON_MSG_DATA_XOFF=1, NCON_MSG_DATA_XON=2, NCON_MSG_CONNECT_TO_SERVER=3, NCON_MSG_CONNECT_RESPONSE=4, NCON_MSG_CHANNEL_CLOSED=5, NCON_MSG_NCP_INFO=6, NCON_MSG_CHANNEL_DISCONNECT=7, NCON_MSG_NCP_END=8 }; enum protocolVersionType { PV_SERIES_5 = 6, PV_SERIES_3 = 3 }; void receive(bufferStore s); int getFirstUnusedChan(); bool isValidChannel(int); void decodeControlMessage(bufferStore &buff); void controlChannel(int chan, enum interControllerMessageType t, bufferStore &command); const char * ctrlMsgName(unsigned char); Link *l; unsigned short verbose; channel **channelPtr; bufferStore *messageList; int *remoteChanList; bool failed; short int protocolVersion; linkChan *lChan; int maxChannels; std::vector pcServers; int lastSentChannel; }; #endif plptools-1.0.26/ncpd/packet.cc000066400000000000000000000253411504470754400161750ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999 Philip Proudman * Copyright (C) 1999-2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mp_serial.h" #include "packet.h" #include "link.h" #include "main.h" #define BUFLEN 4096 // Must be a power of 2 #define BUFMASK (BUFLEN-1) #define hasSpace(dir) (((dir##Write + 1) & BUFMASK) != dir##Read) #define hasData(dir) (dir##Write != dir##Read) #define inca(idx,amount) do { \ idx = (idx + amount) & BUFMASK; \ } while (0) #define inc1(idx) inca(idx, 1) #define normalize(idx) do { idx &= BUFMASK; } while (0) static unsigned short pumpverbose = 0; extern "C" { /** * Signal handler does nothing. It just exists * for having the select() below return an * interrupted system call. */ static void usr1handler(int sig) { signal(SIGUSR1, usr1handler); } static void *pump_run(void *arg) { packet *p = (packet *)arg; pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); while (1) { if (p->fd != -1) { fd_set r_set; fd_set w_set; int res; int count; FD_ZERO(&r_set); w_set = r_set; if (hasSpace(p->in)) FD_SET(p->fd, &r_set); if (hasData(p->out)) FD_SET(p->fd, &w_set); res = select(p->fd+1, &r_set, &w_set, NULL, NULL); switch (res) { case 0: break; case -1: break; default: if (FD_ISSET(p->fd, &w_set)) { count = p->outWrite - p->outRead; if (count < 0) count = (BUFLEN - p->outRead); res = write(p->fd, &p->outBuffer[p->outRead], count); if (res > 0) { if (pumpverbose & PKT_DEBUG_DUMP) { int i; printf("pump: wrote %d bytes: (", res); for (i = 0; ioutBuffer[p->outRead + i]); printf(")\n"); } int hadSpace = hasSpace(p->out); inca(p->outRead, res); if (!hadSpace) pthread_kill(p->thisThread, SIGUSR1); } } if (FD_ISSET(p->fd, &r_set)) { count = p->inRead - p->inWrite; if (count <= 0) count = (BUFLEN - p->inWrite); res = read(p->fd, &p->inBuffer[p->inWrite], count); if (res > 0) { if (pumpverbose & PKT_DEBUG_DUMP) { int i; printf("pump: read %d bytes: (", res); for (i = 0; iinBuffer[p->inWrite + i]); printf(")\n"); } inca(p->inWrite, res); p->findSync(); } } else { if (hasData(p->in)) p->findSync(); } break; } } } } }; static const int baud_table[] = { 115200, 57600, 38400, 19200, // Lower rates don't make sense ?! }; #define BAUD_TABLE_SIZE (sizeof(baud_table) / sizeof(int)) using namespace std; packet:: packet(const char *fname, int _baud, Link *_link, unsigned short _verbose) { verbose = pumpverbose = _verbose; devname = strdup(fname); assert(devname); baud = _baud; theLINK = _link; isEPOC = false; justStarted = true; // Initialize CRC table crc_table[0] = 0; for (int i = 0; i < 128; i++) { unsigned int carry = crc_table[i] & 0x8000; unsigned int tmp = (crc_table[i] << 1) & 0xffff; crc_table[i * 2 + (carry ? 0 : 1)] = tmp ^ 0x1021; crc_table[i * 2 + (carry ? 1 : 0)] = tmp; } inRead = inWrite = outRead = outWrite = 0; inBuffer = new unsigned char[BUFLEN + 1]; outBuffer = new unsigned char[BUFLEN + 1]; assert(inBuffer); assert(outBuffer); esc = false; lastFatal = false; serialStatus = -1; lastSYN = startPkt = -1; crcIn = crcOut = 0; thisThread = pthread_self(); realBaud = baud; if (baud < 0) { baud_index = 1; realBaud = baud_table[0]; } fd = init_serial(devname, realBaud, 0); if (fd == -1) lastFatal = true; else { signal(SIGUSR1, usr1handler); pthread_create(&datapump, NULL, pump_run, this); } } packet:: ~packet() { if (fd != -1) { pthread_cancel(datapump); pthread_join(datapump, NULL); ser_exit(fd); } fd = -1; delete []inBuffer; delete []outBuffer; free(devname); } void packet:: reset() { if (fd != -1) { pthread_cancel(datapump); pthread_join(datapump, NULL); } outRead = outWrite = 0; internalReset(); if (fd != -1) { pthread_create(&datapump, NULL, pump_run, this); realWrite(); } } void packet:: internalReset() { if (verbose & PKT_DEBUG_LOG) lout << "resetting serial connection" << endl; if (fd != -1) { ser_exit(fd); fd = -1; } usleep(100000); inRead = inWrite = 0; esc = false; lastFatal = false; serialStatus = -1; lastSYN = startPkt = -1; crcIn = crcOut = 0; realBaud = baud; justStarted = true; if (baud < 0) { realBaud = baud_table[baud_index++]; if (baud_index >= BAUD_TABLE_SIZE) baud_index = 0; } fd = init_serial(devname, realBaud, 0); if (verbose & PKT_DEBUG_LOG) lout << "serial connection set to " << dec << realBaud << " baud, fd=" << fd << endl; if (fd != -1) lastFatal = false; } short int packet:: getVerbose() { return verbose; } void packet:: setVerbose(short int _verbose) { verbose = pumpverbose = _verbose; } void packet:: setEpoc(bool _epoc) { isEPOC = _epoc; } int packet:: getSpeed() { return realBaud; } void packet:: send(bufferStore &b) { opByte(0x16); opByte(0x10); opByte(0x02); crcOut = 0; long len = b.getLen(); if (verbose & PKT_DEBUG_LOG) { lout << "packet: >> "; if (verbose & PKT_DEBUG_DUMP) lout << b; else lout << " len=" << dec << len; lout << endl; } for (int i = 0; i < len; i++) { unsigned char c = b.getByte(i); switch (c) { case 0x03: if (isEPOC) { opByte(0x10); opByte(0x04); addToCrc(0x03, &crcOut); } else opCByte(c, &crcOut); break; case 0x10: opByte(0x10); // fall thru default: opCByte(c, &crcOut); } } opByte(0x10); opByte(0x03); opByte(crcOut >> 8); opByte(crcOut & 0xff); realWrite(); } void packet:: opByte(unsigned char a) { if (!hasSpace(out)) realWrite(); outBuffer[outWrite] = a; inc1(outWrite); } void packet:: opCByte(unsigned char a, unsigned short *crc) { addToCrc(a, crc); if (!hasSpace(out)) realWrite(); outBuffer[outWrite] = a; inc1(outWrite); } void packet:: realWrite() { pthread_kill(datapump, SIGUSR1); while (!hasSpace(out)) { sigset_t sigs; int dummy; sigemptyset(&sigs); sigaddset(&sigs, SIGUSR1); sigwait(&sigs, &dummy); } } void packet:: findSync() { int inw = inWrite; int p; outerLoop: p = (lastSYN >= 0) ? lastSYN : inRead; if (startPkt < 0) { while (p != inw) { normalize(p); if (inBuffer[p++] != 0x16) continue; lastSYN = p - 1; normalize(p); if (p == inw) break; if (inBuffer[p++] != 0x10) continue; normalize(p); if (p == inw) break; if (inBuffer[p++] != 0x02) continue; normalize(p); lastSYN = startPkt = p; crcIn = inCRCstate = 0; rcv.init(); esc = false; break; } } if (startPkt >= 0) { justStarted = false; while (p != inw) { unsigned char c = inBuffer[p]; switch (inCRCstate) { case 0: if (esc) { esc = false; switch (c) { case 0x03: inCRCstate = 1; break; case 0x04: addToCrc(0x03, &crcIn); rcv.addByte(0x03); break; default: addToCrc(c, &crcIn); rcv.addByte(c); break; } } else { if (c == 0x10) esc = true; else { addToCrc(c, &crcIn); rcv.addByte(c); } } break; case 1: receivedCRC = c; receivedCRC <<= 8; inCRCstate = 2; break; case 2: receivedCRC |= c; inc1(p); inRead = p; startPkt = lastSYN = -1; inCRCstate = 0; if (receivedCRC != crcIn) { if (verbose & PKT_DEBUG_LOG) lout << "packet: BAD CRC" << endl; } else { if (verbose & PKT_DEBUG_LOG) { lout << "packet: << "; if (verbose & PKT_DEBUG_DUMP) lout << rcv; else lout << "len=" << dec << rcv.getLen(); lout << endl; } theLINK->receive(rcv); } rcv.init(); if (hasData(out)) return; goto outerLoop; } inc1(p); } lastSYN = p; } else { // If we get here, no sync was found. // If we are just started and the amount of received data exceeds // 15 bytes, the baudrate is obviously wrong. // (or the connected device is not an EPOC device). Reset the // serial connection and try next baudrate, if auto-baud is set. if (justStarted) { int rx_amount = (inw > inRead) ? inw - inRead : BUFLEN - inRead + inw; if (rx_amount > 15) reset(); } } } bool packet:: linkFailed() { int arg; int res; bool failed = false; if (fd == -1) return false; res = ioctl(fd, TIOCMGET, &arg); if (res < 0) lastFatal = true; if ((serialStatus == -1) || (arg != serialStatus)) { if (verbose & PKT_DEBUG_HANDSHAKE) lout << "packet: < DTR:" << ((arg & TIOCM_DTR)?1:0) << " RTS:" << ((arg & TIOCM_RTS)?1:0) << " DCD:" << ((arg & TIOCM_CAR)?1:0) << " DSR:" << ((arg & TIOCM_DSR)?1:0) << " CTS:" << ((arg & TIOCM_CTS)?1:0) << endl; if (!((arg & TIOCM_RTS) && (arg & TIOCM_DTR))) { arg |= (TIOCM_DTR | TIOCM_RTS); res = ioctl(fd, TIOCMSET, &arg); if (res < 0) lastFatal = true; if (verbose & PKT_DEBUG_HANDSHAKE) lout << "packet: > DTR:" << ((arg & TIOCM_DTR)?1:0) << " RTS:" << ((arg & TIOCM_RTS)?1:0) << " DCD:" << ((arg & TIOCM_CAR)?1:0) << " DSR:" << ((arg & TIOCM_DSR)?1:0) << " CTS:" << ((arg & TIOCM_CTS)?1:0) << endl; } serialStatus = arg; } // TODO: Check for a solution on Solaris. if ((arg & TIOCM_DSR) == 0) { failed = true; } if ((verbose & PKT_DEBUG_LOG) && lastFatal) lout << "packet: linkFATAL\n"; if ((verbose & PKT_DEBUG_LOG) && failed) lout << "packet: linkFAILED\n"; return (lastFatal || failed); } plptools-1.0.26/ncpd/packet.h000066400000000000000000000047131504470754400160370ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999 Philip Proudman * Copyright (C) 1999-2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #ifndef _packet_h #define _packet_h #include "config.h" #include #include #include "bufferstore.h" #include "bufferarray.h" #define PKT_DEBUG_LOG 16 #define PKT_DEBUG_DUMP 32 #define PKT_DEBUG_HANDSHAKE 64 extern "C" { static void *pump_run(void *); } class Link; class packet { public: packet(const char *fname, int baud, Link *_link, unsigned short verbose = 0); ~packet(); /** * Send a buffer out to serial line */ void send(bufferStore &b); void setEpoc(bool); void setVerbose(short int); short int getVerbose(); int getSpeed(); bool linkFailed(); void reset(); private: friend void * pump_run(void *); inline void addToCrc(unsigned char a, unsigned short *crc) { *crc = (*crc << 8) ^ crc_table[((*crc >> 8) ^ a) & 0xff]; } void findSync(); void opByte(unsigned char a); void opCByte(unsigned char a, unsigned short *crc); void realWrite(); void internalReset(); Link *theLINK; pthread_t datapump; pthread_t thisThread; unsigned int crc_table[256]; unsigned short crcOut; unsigned short crcIn; unsigned short receivedCRC; unsigned short inCRCstate; unsigned char *inBuffer; int inWrite; int inRead; unsigned char *outBuffer; int outWrite; int outRead; int startPkt; int lastSYN; bufferArray inQueue; bufferStore rcv; int foundSync; int fd; int serialStatus; int baud_index; int realBaud; short int verbose; bool esc; bool lastFatal; bool isEPOC; bool justStarted; char *devname; int baud; }; #endif plptools-1.0.26/ncpd/socketchan.cc000066400000000000000000000147001504470754400170450ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999 Philip Proudman * Copyright (C) 1999-2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #include "config.h" #include #include #include #include #include #include "socketchan.h" #include "ncp.h" #include "main.h" using namespace std; socketChan:: socketChan(ppsocket * _skt, ncp * _ncpController): channel(_ncpController) { skt = _skt; registerName = 0; connectTry = 0; connected = false; } socketChan::~socketChan() { skt->closeSocket(); delete skt; skt = 0; if (registerName) free(registerName); } void socketChan:: ncpDataCallback(bufferStore & a) { if (registerName != 0) { skt->sendBufferStore(a); } else lerr << "socketchan: Connect without name!!!\n"; } const char *socketChan:: getNcpRegisterName() { return registerName; } // NCP Command processing bool socketChan:: ncpCommand(bufferStore & a) { // str is guaranteed to begin with NCP$, and all NCP commands are // greater than or equal to 8 characters in length. const char *str = a.getString(4); bool ok = false; if (!strncmp(str, "INFO", 4)) { // Send information discovered during the receipt of the // NCON_MSG_NCP_INFO message. a.init(); switch (ncpProtocolVersion()) { case PV_SERIES_3: a.addStringT("Series 3"); break; case PV_SERIES_5: a.addStringT("Series 5"); break; default: lerr << "ncpd: protocol version not known" << endl; a.addStringT("Unknown!"); break; } skt->sendBufferStore(a); ok = true; } else if (!strncmp(str, "CONN", 4)) { // Connect to a channel that was placed in 'pending' mode, by // checking the channel table against the ID... // DO ME LATER ok = true; } else if (!strncmp(str, "CHAL", 4)) { // Challenge // The idea is that the channel stays in 'secure' mode until a // valid RESP is received // DO ME LATER ok = true; } else if (!strncmp(str, "RESP", 4)) { // Reponse - here is the plaintext as sent in the CHAL - the // channel will only open up if this matches what ncpd has for // the encrypted plaintext. // DO ME LATER ok = true; } else if (!strncmp(str, "GSPD", 4)) { // Get Speed of serial device a.init(); a.addByte(rfsv::E_PSI_GEN_NONE); a.addDWord(ncpGetSpeed()); skt->sendBufferStore(a); ok = true; } else if (!strncmp(str, "REGS", 4)) { // Register a server-process on the PC side. a.init(); const char *name = a.getString(8); if (ncpFindPcServer(name)) a.addByte(rfsv::E_PSI_FILE_EXIST); else { ncpRegisterPcServer(skt, name); a.addByte(rfsv::E_PSI_GEN_NONE); } skt->sendBufferStore(a); ok = true; } if (!ok) { lerr << "socketChan:: received unknown NCP command (" << a << ")" << endl; a.init(); a.addByte(rfsv::E_PSI_GEN_NSUP); skt->sendBufferStore(a); } return ok; } void socketChan:: ncpConnectAck() { bufferStore a; a.addStringT("Ok"); skt->sendBufferStore(a); connected = true; connectTry = 3; } void socketChan:: ncpConnectTerminate() { bufferStore a; a.addStringT("NAK"); skt->sendBufferStore(a); ncpDisconnect(); } void socketChan:: ncpRegisterAck() { connectTry++; ncpConnect(); } void socketChan:: ncpConnectNak() { if (!registerName || (connectTry > 1)) ncpConnectTerminate(); else { connectTry++; tryStamp = time(0); ncpRegister(); } } void socketChan:: socketPoll() { int res; if (registerName == 0) { bufferStore a; res = skt->getBufferStore(a, false); switch (res) { case 1: // A client has connected, and is announcing who it // is... e.g. "SYS$RFSV.*" // // An NCP Channel can be in 'Control' or 'Data' mode. // Initially, it is in 'Control' mode, and can accept // certain commands. // // When a command is received that ncpd does not // understand, this is assumed to be a request to // connect to the remote service of that name, and enter // 'data' mode. // // Later, there might be an explicit command to enter // 'data' mode, and also a challenge-response protocol // before any connection can be made. // // All commands begin with "NCP$". if (memchr(a.getString(), 0, a.getLen()) == 0) { // Not 0 terminated, -> invalid lerr << "ncpd: command " << a << " unrecognized." << endl; return; } // There is a magic process name called "NCP$INFO.*" // which is announced by the rfsvfactory. This causes a // response to be issued containing the NCP version // number. The rfsvfactory will create the correct type // of RFSV protocol handler, which will then announce // itself. So, first time in here, we might get the // NCP$INFO.* if (a.getLen() > 8 && !strncmp(a.getString(), "NCP$", 4)) { if (!ncpCommand(a)) lerr << "ncpd: command " << a << " unrecognized." << endl; return; } // This isn't a command, it's a remote process. Connect. registerName = strdup(a.getString()); connectTry++; // If this is SYS$RFSV, we immediately connect. In all // other cases, we first perform a registration. Connect // is then triggered by RegisterAck and uses the name // we received from the Psion. tryStamp = time(0); if (strncmp(registerName, "SYS$RFSV", 8) == 0) ncpConnect(); else ncpRegister(); break; case -1: terminateWhenAsked(); break; } } else if (connected) { bufferStore a; res = skt->getBufferStore(a, false); if (res == -1) { ncpDisconnect(); skt->closeSocket(); } else if (res == 1) { if (a.getLen() > 8 && !strncmp(a.getString(), "NCP$", 4)) { if (!ncpCommand(a)) lerr << "ncpd: command " << a << " unrecognized." << endl; return; } ncpSend(a); } } else if (time(0) > (tryStamp + 15)) terminateWhenAsked(); } bool socketChan:: isConnected() const { return connected; } plptools-1.0.26/ncpd/socketchan.h000066400000000000000000000030041504470754400167020ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999 Philip Proudman * Copyright (C) 1999-2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #ifndef _socketchan_h_ #define _socketchan_h_ #include "config.h" #include "channel.h" class ppsocket; class socketChan : public channel { public: socketChan(ppsocket* comms, ncp* ncpController); virtual ~socketChan(); void ncpDataCallback(bufferStore& a); const char* getNcpRegisterName(); void ncpConnectAck(); void ncpRegisterAck(); void ncpDoRegisterAck(int) {} void ncpConnectTerminate(); void ncpConnectNak(); bool isConnected() const; void socketPoll(); private: enum protocolVersionType { PV_SERIES_5 = 6, PV_SERIES_3 = 3 }; bool ncpCommand(bufferStore &a); ppsocket* skt; char* registerName; bool connected; int connectTry; int tryStamp; }; #endif plptools-1.0.26/plpftp/000077500000000000000000000000001504470754400147735ustar00rootroot00000000000000plptools-1.0.26/plpftp/.gitignore000066400000000000000000000000101504470754400167520ustar00rootroot00000000000000/plpftp plptools-1.0.26/plpftp/Makefile.am000066400000000000000000000020661504470754400170330ustar00rootroot00000000000000# plpftp/Makefile.am # # This file is part of plptools. # # Copyright (C) 1999-2002 Fritz Elfert # Copyright (C) 2007-2025 Reuben Thomas # # 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 # along with this program; if not, see . bin_PROGRAMS = plpftp plpftp_CPPFLAGS = -I$(top_srcdir)/lib -I$(top_srcdir)/libgnu -I$(top_builddir)/libgnu plpftp_CXXFLAGS = $(WARN_CXXFLAGS) plpftp_LDADD = $(LIB_PLP) $(INTLLIBS) $(SERVENT_LIB) $(top_builddir)/libgnu/libgnu.a plpftp_SOURCES = ftp.cc main.cc ftp.h plptools-1.0.26/plpftp/ftp.cc000066400000000000000000001217151504470754400161020ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999 Philip Proudman * Copyright (C) 1999-2002 Fritz Elfert * Copyright (C) 2006-2025 Reuben Thomas * * 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 * along with this program; if not, see . * */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ignore-value.h" #include "string-buffer.h" #include "xalloc.h" #include "xvasprintf.h" #include "ftp.h" extern "C" { #include "yesno.h" #if defined(HAVE_READLINE_READLINE_H) # include #elif defined(HAVE_READLINE_H) # include #else /* !defined(HAVE_READLINE_H) */ extern char *readline (); #endif /* !defined(HAVE_READLINE_H) */ #ifdef HAVE_READLINE_HISTORY # if defined(HAVE_READLINE_HISTORY_H) # include # elif defined(HAVE_HISTORY_H) # include # endif /* !defined(HAVE_READLINE_HISTORY_H) */ #endif /* !defined(HAVE_READLINE_HISTORY) */ } using namespace std; static char *psionDir; static rfsv *comp_a; static int continueRunning; #define CLIPFILE "C:/System/Data/Clpboard.cbd" void ftp:: resetUnixWd() { localDir = getcwd(NULL, 0); assert(localDir != NULL); } ftp::ftp() { resetUnixWd(); } ftp::~ftp() { free(localDir); } void ftp::usage() { cout << _("Known FTP commands:") << endl << endl; cout << " pwd" << endl; cout << " ren " << endl; cout << " touch " << endl; cout << " gtime " << endl; cout << " test " << endl; cout << " gattr " << endl; cout << " sattr [[-|+]rwhsa] " << endl; cout << " devs" << endl; cout << " dir|ls" << endl; cout << " dircnt" << endl; cout << " cd " << endl; cout << " lcd " << endl; cout << " !" << endl; cout << " get " << endl; cout << " put " << endl; cout << " mget " << endl; cout << " mput " << endl; cout << " cp " << endl; cout << " del|rm " << endl; cout << " mkdir " << endl; cout << " rmdir " << endl; cout << " volname " << endl; cout << " prompt" << endl; cout << " hash" << endl; cout << " bye" << endl; cout << endl << _("Known RPC commands:") << endl << endl; cout << " ps" << endl; cout << " kill " << endl; cout << " getclip " << endl; cout << " putclip " << endl; cout << " run [args]" << endl; cout << " killsave " << endl; cout << " runrestore " << endl; cout << " machinfo" << endl; cout << " ownerinfo" << endl; cout << " settime" << endl; cout << " setupinfo" << endl; } static char *join_string_vector(vector argv, const char *sep) { struct string_buffer sb; sb_init(&sb); int argc = argv.size(); for (int i = 0; i < argc; i++) { sb_append(&sb, argv[i]); if (i < argc - 1) sb_append(&sb, sep); } return sb_dupfree(&sb); } static int checkAbortNoHash(void *, uint32_t) { return continueRunning; } static int checkAbortHash(void *, uint32_t) { if (continueRunning) { printf("#"); fflush(stdout); } return continueRunning; } static void sigint_handler(int i) { continueRunning = 0; signal(SIGINT, sigint_handler); } static void sigint_handler2(int i) { continueRunning = 0; fclose(stdin); signal(SIGINT, sigint_handler2); } static int stopPrograms(rpcs & r, const char *file) { Enum res; processList tmp; FILE *fp = fopen(file, "w"); if (fp == NULL) { cerr << _("Could not open command list file ") << file << endl; return 1; } fputs("#plpftp processlist\n", fp); if ((res = r.queryPrograms(tmp)) != rfsv::E_PSI_GEN_NONE) { cerr << _("Could not get process list, Error: ") << res << endl; return 1; } for (processList::iterator i = tmp.begin(); i != tmp.end(); i++) { fputs(i->getArgs(), fp); fputc('\n', fp); } fclose(fp); time_t tstart = time(0) + 5; while (!tmp.empty()) { for (processList::iterator i = tmp.begin(); i != tmp.end(); i++) { r.stopProgram(i->getProcId()); } usleep(100000); if (time(0) > tstart) { cerr << _( "Could not stop all processes. Please stop running\n" "programs manually on the Psion, then hit return.") << flush; cin.getline((char *)&tstart, 1); tstart = time(0) + 5; } if ((res = r.queryPrograms(tmp)) != rfsv::E_PSI_GEN_NONE) { cerr << _("Could not get process list, Error: ") << res << endl; return 1; } } return 0; } static char * get_upto(FILE *fp, const char *term, size_t *final_len) { size_t len = 256; int c; char *l = (char *)malloc(len), *s = l; assert(l); for (c = getc(fp); c != EOF && strchr(term, c) == NULL; c = getc(fp)) { if (s == l + len) { l = (char *)realloc(l, len * 2); assert(l); len *= 2; } *s++ = c; } if (s == l + len) { l = (char *)realloc(l, len + 1); assert(l); } if (final_len) *final_len = s - l; *s++ = '\0'; l = (char *)realloc(l, s - l); assert(l); return l; } static char* getln(FILE *fp) { return get_upto(fp, "\n", NULL); } static int startPrograms(rpcs & r, rfsv & a, const char *file) { Enum res; FILE *fp = fopen(file, "r"); string cmd; if (fp == NULL) { cerr << _("Could not open command list file ") << file << endl; return 1; } cmd = string(getln(fp)); if (strcmp(cmd.c_str(), "#plpftp processlist")) { fclose(fp); cerr << _("Error: ") << file << _(" is not a process list saved with killsave") << endl; return 1; } for (cmd = string(getln(fp)); cmd.length() > 0; cmd = string(getln(fp))) { int firstBlank = cmd.find(' '); string prog = string(cmd, 0, firstBlank); string arg = string(cmd, firstBlank + 1); if (!prog.empty()) { // Workaround for broken programs like Backlite. These do not store // the full program path. In that case we try running the arg1 which // results in starting the program via recog. facility. if ((arg.size() > 2) && (arg[1] == ':') && (arg[0] >= 'A') && (arg[0] <= 'Z')) res = r.execProgram(arg.c_str(), ""); else res = r.execProgram(prog.c_str(), arg.c_str()); if (res != rfsv::E_PSI_GEN_NONE) { // If we got an error here, that happened probably because // we have no path at all (e.g. Macro5) and the program is // not registered in the Psion's path properly. Now try // the usual \System\Apps\\.app // on all drives. if (prog.find('\\') == prog.npos) { uint32_t devbits; if ((res = a.devlist(devbits)) == rfsv::E_PSI_GEN_NONE) { int i; for (i = 0; i < 26; i++) { if (devbits & (1 << i)) { string tmp = string(); tmp += ('A' + i); tmp += ":\\System\\Apps\\" + prog + "\\" + prog + ".app"; res = r.execProgram(tmp.c_str(), ""); if (res == rfsv::E_PSI_GEN_NONE) break; } } } } } if (res != rfsv::E_PSI_GEN_NONE) { cerr << _("Could not start ") << cmd << endl; cerr << _("Error: ") << res << endl; } } } return 0; } bool ftp::checkClipConnection(rfsv &a, rclip & rc, ppsocket & rclipSocket) { if (canClip == false) return false; if (a.getProtocolVersion() == 3) { cerr << _("Clipboard protocol not supported by Psion Series 3.") << endl; return false; } Enum ret; if ((ret = rc.initClipbd()) == rfsv::E_PSI_GEN_NONE) return true; else if (ret == rfsv::E_PSI_GEN_NSUP) cerr << _("Your Psion does not support the clipboard protocol.\n\ The reason for that is usually a missing server library.\n\ Make sure that C:\\System\\Libs\\clipsvr.rsy exists.\n\ This file is part of PsiWin and usually gets copied to\n\ your Psion when you enable CopyAnywhere in PsiWin.\n\ You can also get it from a PsiWin installation directory\n\ and copy it to your Psion manually.") << endl; return false; } static void psiText2ascii(char *buf, int len) { char *p; for (p = buf; len; len--, p++) switch (*p) { case 6: case 7: *p = '\n'; break; case 8: *p = '\f'; break; case 10: *p = '\t'; break; case 11: case 12: *p = '-'; break; case 15: case 16: *p = ' '; break; } } static void ascii2PsiText(char *buf, int len) { char *p; for (p = buf; len; len--, p++) switch (*p) { case '\0': *p = ' '; case '\n': *p = 6; break; case '\f': *p = 8; break; case '-': *p = 11; break; } } static char * slurp(FILE *fp, size_t *final_len) { return get_upto(fp, "", final_len); } int ftp::putClipText(rpcs & r, rfsv & a, rclip & rc, ppsocket & rclipSocket, const char *file) { Enum res; uint32_t fh; uint32_t l; const unsigned char *p; bufferStore b; char *data; FILE *fp; if (!checkClipConnection(a, rc, rclipSocket)) return 1; if ((fp = fopen(file, "r")) == NULL) return 1; size_t len; data = slurp(fp, &len); fclose(fp); ascii2PsiText(data, len); res = a.freplacefile(0x200, CLIPFILE, fh); if (res == rfsv::E_PSI_GEN_NONE) { // Base Header b.addDWord(0x10000037); // @00 UID 0 b.addDWord(0x1000003b); // @04 UID 1 b.addDWord(0); // @08 UID 3 b.addDWord(0x4739d53b); // @0c Checksum the above // Section Table b.addDWord(0x00000014); // @10 Offset of Section Table b.addByte(2); // @14 Section Table, length in DWords b.addDWord(0x10000033); // @15 Section Type (ASCII) b.addDWord(0x0000001d); // @19 Section Offset // Data b.addDWord(strlen(data)); // @1e Section (String) length b.addStringT(data); // @22 Data (Psion Word seems to need a // terminating 0. p = (const unsigned char *)b.getString(0); a.fwrite(fh, p, b.getLen(), l); a.fclose(fh); a.fsetattr(CLIPFILE, 0x20, 0x07); } return 0; } // FIXME: Make this work as putclipimg // static QImage *putImage; // static int // getGrayPixel(int x, int y) // { // return qGray(putImage->pixel(x, y)); // } // void TopLevel:: // putClipImage(QImage &img) { // Enum res; // uint32_t fh; // uint32_t l; // const unsigned char *p; // bufferStore b; // res = rf->freplacefile(0x200, CLIPFILE, fh); // if (res == rfsv::E_PSI_GEN_NONE) { // while ((res = rc->checkNotify()) != rfsv::E_PSI_GEN_NONE) { // if (res != rfsv::E_PSI_FILE_EOF) { // rf->fclose(fh); // closeConnection(); // return; // } // } // // Base Header // b.addDWord(0x10000037); // @00 UID 0 // b.addDWord(0x1000003b); // @04 UID 1 // b.addDWord(0); // @08 UID 3 // b.addDWord(0x4739d53b); // @0c Checksum the above // // Section Table // b.addDWord(0x00000014); // @10 Offset of Section Table // b.addByte(2); // @14 Section Table, length in DWords // b.addDWord(0x1000003d); // @15 Section Type (Image) // b.addDWord(0x0000001d); // @19 Section Offset // // Data // bufferStore ib; // putImage = &img; // encodeBitmap(img.width(), img.height(), getGrayPixel, false, ib); // b.addBuff(ib); // p = (const unsigned char *)b.getString(0); // rf->fwrite(fh, p, b.getLen(), l); // rf->fclose(fh); // rf->fsetattr(CLIPFILE, 0x20, 0x07); // } else // closeConnection(); // } // QImage *TopLevel:: // decode_image(const unsigned char *p) // { // bufferStore out; // bufferStore hout; // QImage *img = 0L; // int xPixels; // int yPixels; // if (!decodeBitmap(p, xPixels, yPixels, out)) // return img; // QString hdr = QString("P5\n%1 %2\n255\n").arg(xPixels).arg(yPixels); // hout.addString(hdr.latin1()); // hout.addBuff(out); // img = new QImage(xPixels, yPixels, 8); // if (!img->loadFromData((const uchar *)hout.getString(0), hout.getLen())) { // delete img; // img = 0L; // } // return img; // } int ftp::getClipData(rpcs & r, rfsv & a, rclip & rc, ppsocket & rclipSocket, const char *file) { Enum res; PlpDirent de; uint32_t fh; string clipText; res = a.fgeteattr(CLIPFILE, de); if (res == rfsv::E_PSI_GEN_NONE) { if (de.getAttr() & rfsv::PSI_A_ARCHIVE) { uint32_t len = de.getSize(); char *buf = (char *)malloc(len); if (!buf) { cerr << "Out of memory in getClipData" << endl; return 1; } res = a.fopen(a.opMode(rfsv::PSI_O_RDONLY | rfsv::PSI_O_SHARE), CLIPFILE, fh); if (res == rfsv::E_PSI_GEN_NONE) { uint32_t tmp; res = a.fread(fh, (unsigned char *)buf, len, tmp); a.fclose(fh); if ((res == rfsv::E_PSI_GEN_NONE) && (tmp == len)) { char *p = buf; int lcount; uint32_t *ti = (uint32_t*)buf; // Check base header if (*ti++ != 0x10000037) { free(buf); return 1; } if (*ti++ != 0x1000003b) { free(buf); return 1; } if (*ti++ != 0) { free(buf); return 1; } if (*ti++ != 0x4739d53b) { free(buf); return 1; } // Start of section table p = buf + *ti; // Length of section table (in DWords) lcount = *p++; uint32_t *td = (uint32_t*)p; while (lcount > 0) { uint32_t sType = *td++; if (sType == 0x10000033) { // An ASCII section p = buf + *td; len = *((uint32_t*)p); p += 4; psiText2ascii(p, len); clipText += (char *)p; } if (sType == 0x1000003d) { // FIXME: Implement this // // A paint data section // p = buf + *td; // if (clipImg) // delete clipImg; // clipImg = decode_image((const unsigned char *)p); } td++; lcount -= 2; } } } free(buf); } } FILE *fp = fopen(file, "w"); if (fp == NULL) return 1; fwrite(clipText.c_str(), 1, clipText.length(), fp); return 0; } /* Compute parent directory of an EPOC directory. */ static char *epoc_dirname(const char *path) { char *f1 = xstrdup(path); char *p = f1 + strlen(f1); /* Skip trailing slash. */ if (p > f1) *--p = '\0'; /* Skip backwards to next slash. */ while ((p > f1) && (*p != '/') && (*p != '\\')) p--; /* If we have just a drive letter, colon and slash, keep exactly that. */ if (p - f1 < 3) p = f1 + 3; /* Truncate the string at the current point, and return it. */ *p = '\0'; return f1; } /* Compute new directory from path, which may be absolute or relative, and cwd. */ static char *epoc_dir_from(const char *path) { char *f1; /* If we have asked for parent dir, get dirname of cwd. */ if (!strcmp(path, "..")) { f1 = epoc_dirname(psionDir); } else { /* If path is relative, append it to cwd. */ if ((path[0] != '/') && (path[0] != '\\') && (path[1] != ':')) f1 = xasprintf("%s%s", psionDir, path); /* Otherwise, path is absolute, so duplicate it. */ else f1 = xstrdup(path); } /* Ensure path ends with a slash. */ if ((f1[strlen(f1) - 1] != '/') && (f1[strlen(f1) - 1] != '\\')) { char *f2 = xasprintf("%s%s", f1, "\\"); free(f1); f1 = f2; } /* Convert forward slashes in new path to backslashes. */ for (char *p = f1; *p; p++) if (*p == '/') *p = '\\'; return f1; } int ftp:: session(rfsv & a, rpcs & r, rclip & rc, ppsocket & rclipSocket, vector argv) { Enum res; bool prompt = true; bool hash = false; cpCallback_t cab = checkAbortNoHash; bool once = false; unsigned argc = argv.size(); if (argc > 0) once = true; { Enum machType; bufferArray b; if ((res = r.getOwnerInfo(b)) == rfsv::E_PSI_GEN_NONE) { r.getMachineType(machType); if (!once) { int speed = a.getSpeed(); cout << _("Connected to a ") << machType << _(" at ") << speed << _(" baud, OwnerInfo:") << endl; while (!b.empty()) cout << " " << b.pop().getString() << endl; cout << endl; } } else cerr << _("OwnerInfo returned error ") << res << endl; } if (!strcmp(DDRIVE, "AUTO")) { uint32_t devbits; int i; strcpy(defDrive, "::"); if (a.devlist(devbits) == rfsv::E_PSI_GEN_NONE) { for (i = 0; i < 26; i++) { PlpDrive drive; if ((devbits & 1) && a.devinfo(i + 'A', drive) == rfsv::E_PSI_GEN_NONE) { defDrive[0] = 'A' + i; break; } devbits >>= 1; } } if (!strcmp(defDrive, "::")) { cerr << _("FATAL: Couldn't find default Drive") << endl; return -1; } } else strcpy(defDrive, DDRIVE); free(psionDir); psionDir = xasprintf("%s%s", defDrive, DBASEDIR); comp_a = &a; if (!once) { cout << _("Psion dir is: \"") << psionDir << "\"" << endl; initReadline(); } continueRunning = 1; signal(SIGINT, sigint_handler); do { if (!once) { argv = getCommand(); argc = argv.size(); } if (argc == 0) { continue; } if ((!strcmp(argv[0], "help")) || (!strcmp(argv[0], "?"))) { usage(); continue; } if (!strcmp(argv[0], "prompt")) { prompt = !prompt; cout << _("Prompting now ") << (prompt? _("on") : _("off")) << endl; continue; } if (!strcmp(argv[0], "hash")) { hash = !hash; cout << _("Hash printing now ") << (hash? _("on") : _("off")) << endl; cab = (hash) ? checkAbortHash : checkAbortNoHash; continue; } if (!strcmp(argv[0], "pwd")) { cout << _("Local dir: \"") << localDir << "\"" << endl; cout << _("Psion dir: \"") << psionDir << "\"" << endl; continue; } if (!strcmp(argv[0], "volname") && (argc == 3) && (strlen(argv[1]) == 1)) { if ((res = a.setVolumeName(toupper(argv[1][0]), argv[2])) != rfsv::E_PSI_GEN_NONE) cerr << _("Error: ") << res << endl; continue; } if (!strcmp(argv[0], "ren") && (argc == 3)) { char *f1 = xasprintf("%s%s", psionDir, argv[1]); char *f2 = xasprintf("%s%s", psionDir, argv[2]); if ((res = a.rename(f1, f2)) != rfsv::E_PSI_GEN_NONE) cerr << _("Error: ") << res << endl; free(f1); free(f2); continue; } if (!strcmp(argv[0], "cp") && (argc == 3)) { char *f1 = xasprintf("%s%s", psionDir, argv[1]); char *f2 = xasprintf("%s%s", psionDir, argv[2]); if ((res = a.copyOnPsion(f1, f2, NULL, cab)) != rfsv::E_PSI_GEN_NONE) cerr << _("Error: ") << res << endl; free(f1); free(f2); continue; } if (!strcmp(argv[0], "touch") && (argc == 2)) { char *f1 = xasprintf("%s%s", psionDir, argv[1]); PsiTime pt; if ((res = a.fsetmtime(f1, pt)) != rfsv::E_PSI_GEN_NONE) cerr << _("Error: ") << res << endl; free(f1); continue; } if (!strcmp(argv[0], "test") && (argc == 2)) { PlpDirent e; char *f1 = xasprintf("%s%s", psionDir, argv[1]); if ((res = a.fgeteattr(f1, e)) != rfsv::E_PSI_GEN_NONE) cerr << _("Error: ") << res << endl; else cout << e << endl; free(f1); continue; } if (!strcmp(argv[0], "gattr") && (argc == 2)) { uint32_t attr; char *f1 = xasprintf("%s%s", psionDir, argv[1]); if ((res = a.fgetattr(f1, attr)) != rfsv::E_PSI_GEN_NONE) cerr << _("Error: ") << res << endl; else { cout << hex << setw(4) << setfill('0') << attr; cout << " (" << a.attr2String(attr) << ")" << endl; } free(f1); continue; } if (!strcmp(argv[0], "gtime") && (argc == 2)) { PsiTime mtime; char *f1 = xasprintf("%s%s", psionDir, argv[1]); if ((res = a.fgetmtime(f1, mtime)) != rfsv::E_PSI_GEN_NONE) cerr << _("Error: ") << res << endl; else cout << mtime << "(" << hex << setw(8) << setfill('0') << mtime.getPsiTimeHi() << ":" << setw(8) << setfill('0') << mtime.getPsiTimeLo() << ")" << endl; free(f1); continue; } if (!strcmp(argv[0], "sattr") && (argc == 3)) { long attr[2]; int aidx = 0; char *p = argv[1]; char *f1 = xasprintf("%s%s", psionDir, argv[2]); attr[0] = attr[1] = 0; while (*p) { switch (*p) { case '+': aidx = 0; break; case '-': aidx = 1; break; case 'r': attr[aidx] |= rfsv::PSI_A_READ; attr[aidx] &= ~rfsv::PSI_A_READ; break; case 'w': attr[1 - aidx] |= rfsv::PSI_A_RDONLY; attr[aidx] &= ~rfsv::PSI_A_RDONLY; break; case 'h': attr[aidx] |= rfsv::PSI_A_HIDDEN; attr[1 - aidx] &= ~rfsv::PSI_A_HIDDEN; break; case 's': attr[aidx] |= rfsv::PSI_A_SYSTEM; attr[1 - aidx] &= ~rfsv::PSI_A_SYSTEM; break; case 'a': attr[aidx] |= rfsv::PSI_A_ARCHIVE; attr[1 - aidx] &= ~rfsv::PSI_A_ARCHIVE; break; } p++; } if ((res = a.fsetattr(f1, attr[0], attr[1])) != rfsv::E_PSI_GEN_NONE) cerr << _("Error: ") << res << endl; free(f1); continue; } if (!strcmp(argv[0], "dircnt")) { uint32_t cnt; if ((res = a.dircount(psionDir, cnt)) != rfsv::E_PSI_GEN_NONE) cerr << _("Error: ") << res << endl; else cout << cnt << _(" Entries") << endl; continue; } if (!strcmp(argv[0], "devs")) { uint32_t devbits; if ((res = a.devlist(devbits)) == rfsv::E_PSI_GEN_NONE) { cout << _("Drive Type Volname Total Free UniqueID") << endl; for (int i = 0; i < 26; i++) { PlpDrive drive; if ((devbits & 1) != 0) { if (a.devinfo(i + 'A', drive) == rfsv::E_PSI_GEN_NONE) cout << (char) ('A' + i) << " " << hex << setw(4) << setfill('0') << drive.getMediaType() << " " << setw(12) << setfill(' ') << setiosflags(ios::left) << drive.getName().c_str() << resetiosflags(ios::left) << dec << setw(9) << drive.getSize() << setw(9) << drive.getSpace() << " " << setw(8) << setfill('0') << hex << drive.getUID() << dec << endl; } devbits >>= 1; } } else cerr << _("Error: ") << res << endl; continue; } if (!strcmp(argv[0], "ls") || !strcmp(argv[0], "dir")) { PlpDir files; char *dname = argc > 1 ? epoc_dir_from(argv[1]) : xstrdup(psionDir); if ((res = a.dir(dname, files)) != rfsv::E_PSI_GEN_NONE) cerr << _("Error: ") << res << endl; else while (!files.empty()) { cout << files[0] << endl; files.pop_front(); } free(dname); continue; } if (!strcmp(argv[0], "lcd")) { if (argc == 1) resetUnixWd(); else { if (chdir(argv[1]) == 0) { resetUnixWd(); } else cerr << _("No such directory") << endl << _("Keeping original directory \"") << localDir << "\"" << endl; } continue; } if (!strcmp(argv[0], "cd")) { if (argc == 1) { free(psionDir); psionDir = xasprintf("%s%s", defDrive, DBASEDIR); } else { char *newDir = epoc_dir_from(argv[1]); uint32_t tmp; if ((res = a.dircount(newDir, tmp)) != rfsv::E_PSI_GEN_NONE) { cerr << _("Error: ") << res << endl; cerr << _("Keeping original directory \"") << psionDir << "\"" << endl; free(newDir); } else { free(psionDir); psionDir = newDir; } } continue; } if ((!strcmp(argv[0], "get")) && (argc > 1)) { struct timeval stime; struct timeval etime; struct stat stbuf; char *f1 = xasprintf("%s%s", psionDir, argv[1]); char *f2 = xasprintf("%s%s%s", localDir, "/", argc == 2 ? argv[1] : argv[2]); gettimeofday(&stime, 0L); if ((res = a.copyFromPsion(f1, f2, NULL, cab)) != rfsv::E_PSI_GEN_NONE) { if (hash) cout << endl; continueRunning = 1; cerr << _("Error: ") << res << endl; } else { if (hash) cout << endl; gettimeofday(&etime, 0L); long dsec = etime.tv_sec - stime.tv_sec; long dhse = (etime.tv_usec / 10000) - (stime.tv_usec /10000); if (dhse < 0) { dsec--; dhse = 100 + dhse; } float dt = dhse; dt /= 100.0; dt += dsec; stat(f2, &stbuf); float cps = (float)(stbuf.st_size) / dt; cout << _("Transfer complete, (") << dec << stbuf.st_size << _(" bytes in ") << dsec << "." << dhse << _(" secs = ") << cps << " cps)\n"; } free(f1); free(f2); continue; } else if ((!strcmp(argv[0], "mget")) && (argc == 2)) { char *pattern = argv[1]; PlpDir files; if ((res = a.dir(psionDir, files)) != rfsv::E_PSI_GEN_NONE) { cerr << _("Error: ") << res << endl; continue; } for (int i = 0; i < files.size(); i++) { PlpDirent e = files[i]; long attr = e.getAttr(); if (attr & (rfsv::PSI_A_DIR | rfsv::PSI_A_VOLUME)) continue; if (fnmatch(pattern, e.getName(), FNM_NOESCAPE) == FNM_NOMATCH) continue; cout << _("Get \"") << e.getName() << "\" (y,n): "; bool yes = false; if (prompt) { cout.flush(); yes = yesno(); } else { yes = true; cout << "y "; cout.flush(); } if (yes) { char *f1 = xasprintf("%s%s", psionDir, e.getName()); char *f2 = xasprintf("%s%s%s", localDir, "/", e.getName()); if ((res = a.copyFromPsion(f1, f2, NULL, cab)) != rfsv::E_PSI_GEN_NONE) { if (hash) cout << endl; continueRunning = 1; cerr << _("Error: ") << res << endl; break; } else { if (hash) cout << endl; cout << _("Transfer complete\n"); } free(f1); free(f2); } } continue; } if (!strcmp(argv[0], "put") && (argc >= 2)) { struct timeval stime; struct timeval etime; struct stat stbuf; char *f1 = xasprintf("%s%s%s", localDir, "/", argv[1]); char *f2 = xasprintf("%s%s", psionDir, argc == 2 ? argv[1] : argv[2]); gettimeofday(&stime, 0L); if ((res = a.copyToPsion(f1, f2, NULL, cab)) != rfsv::E_PSI_GEN_NONE) { if (hash) cout << endl; continueRunning = 1; cerr << _("Error: ") << res << endl; } else { if (hash) cout << endl; gettimeofday(&etime, 0L); long dsec = etime.tv_sec - stime.tv_sec; long dhse = (etime.tv_usec / 10000) - (stime.tv_usec /10000); if (dhse < 0) { dsec--; dhse = 100 + dhse; } float dt = dhse; dt /= 100.0; dt += dsec; stat(f1, &stbuf); float cps = (float)(stbuf.st_size) / dt; cout << _("Transfer complete, (") << dec << stbuf.st_size << _(" bytes in ") << dsec << "." << dhse << _(" secs = ") << cps << " cps)\n"; } free(f1); free(f2); continue; } if ((!strcmp(argv[0], "mput")) && (argc == 2)) { char *pattern = argv[1]; DIR *d = opendir(localDir); if (d) { struct dirent *de; do { de = readdir(d); if (de) { struct stat st; if (fnmatch(pattern, de->d_name, FNM_NOESCAPE) == FNM_NOMATCH) continue; char *f1 = xasprintf("%s%s%s", localDir, "/", de->d_name); if (stat(f1, &st) == 0 && S_ISREG(st.st_mode)) { cout << _("Put \"") << de->d_name << "\" y,n: "; bool yes = false; if (prompt) { cout.flush(); yes = yesno(); } else { cout << "y "; cout.flush(); } if (yes) { char *f2 = xasprintf("%s%s", psionDir, de->d_name); if ((res = a.copyToPsion(f1, f2, NULL, cab)) != rfsv::E_PSI_GEN_NONE) { if (hash) cout << endl; continueRunning = 1; cerr << _("Error: ") << res << endl; free(f1); free(f2); break; } else { if (hash) cout << endl; free(f2); cout << _("Transfer complete\n"); } } } free(f1); } } while (de); closedir(d); } else cerr << _("Error in directory name \"") << localDir << "\"\n"; continue; } if ((!strcmp(argv[0], "del") || !strcmp(argv[0], "rm")) && (argc == 2)) { char *f1 = xasprintf("%s%s", psionDir, argv[1]); if ((res = a.remove(f1)) != rfsv::E_PSI_GEN_NONE) cerr << _("Error: ") << res << endl; free(f1); continue; } if (!strcmp(argv[0], "mkdir") && (argc == 2)) { char *f1 = xasprintf("%s%s", psionDir, argv[1]); if ((res = a.mkdir(f1)) != rfsv::E_PSI_GEN_NONE) cerr << _("Error: ") << res << endl; free(f1); continue; } if (!strcmp(argv[0], "rmdir") && (argc == 2)) { char *f1 = xasprintf("%s%s", psionDir, argv[1]); if ((res = a.rmdir(f1)) != rfsv::E_PSI_GEN_NONE) cerr << _("Error: ") << res << endl; free(f1); continue; } if (argv[0][0] == '!') { char *cmd = join_string_vector(argv, " "); if (strlen(cmd)) ignore_value(system(cmd)); else { const char *sh; cout << _("Starting subshell ...\n"); sh = getenv("SHELL"); if (!sh) sh = "/bin/sh"; ignore_value(system(sh)); } free(cmd); continue; } // RPCS commands if (!strcmp(argv[0], "settime")) { if ((res = r.setTime(time(NULL))) != rfsv::E_PSI_GEN_NONE) cerr << _("Error: ") << res << endl; continue; } if (!strcmp(argv[0], "setupinfo")) { Enum res; bufferStore db; if ((res = r.configRead(0, db)) != rfsv::E_PSI_GEN_NONE) { cerr << _("Error: ") << res << endl; continue; } if (db.getLen() < 1152) { cerr << _("Unknown setup info received") << endl; continue; } cout << _("Setup information:") << endl; cout << _(" Screen contrast: ") << dec << db.getDWord(0x4c) + 1 << endl; cout << _(" Keyboard click: ") << (db.getDWord(0x200) ? (db.getDWord(0x204) ? _("high") : _("low")) : _("off")) << endl; cout << _(" Screen click: ") << (db.getDWord(0x20c) ? (db.getDWord(0x210) ? _("high") : _("low")) : _("off")) << endl; cout << _(" Error sound: ") << (db.getDWord(0x214) ? (db.getDWord(0x218) ? _("high") : _("low")) : _("off")) << endl; cout << _(" Auto-switch off: "); switch (db.getDWord(0x228)) { case 0: cout << _("never"); break; case 1: cout << _("if running on battery power"); break; case 2: cout << _("always"); break; } cout << endl; if (db.getDWord(0x228) != 0) cout << _(" Switch off after: ") << dec << db.getDWord(0x22c) << _(" seconds") << endl; cout << _(" Backlight off after: ") << dec << db.getDWord(0x234) << _(" seconds") << endl; cout << _(" Switch on when tapping on screen: ") << (db.getDWord(0x238) ? _("yes") : _("no")) << endl; cout << _(" Switch on when opening: ") << (db.getDWord(0x23c) ? _("yes") : _("no")) << endl; cout << _(" Switch off when closing: ") << (db.getDWord(0x23c) ? _("yes") : _("no")) << endl; cout << _(" Ask for password on startup: ") << (db.getDWord(0x29c) ? _("yes") : _("no")) << endl; cout << _(" Show Owner info on startup: "); switch (db.getByte(0x3b0)) { case 0x31: cout << _("never"); break; case 0x32: cout << _("once a day"); break; case 0x33: cout << _("always"); break; } cout << endl; continue; } if (!strcmp(argv[0], "run") && (argc >= 2)) { vector args = {argv.begin() + 1, argv.end()}; char *arg = join_string_vector(args, " "); char *cmd; if (!strchr(argv[1], ':')) cmd = xasprintf("%s%s", psionDir, argv[1]); else cmd = xstrdup(argv[1]); r.execProgram(cmd, arg); free(arg); free(cmd); continue; } if (!strcmp(argv[0], "ownerinfo")) { bufferArray b; if ((res = r.getOwnerInfo(b)) != rfsv::E_PSI_GEN_NONE) { cerr << _("Error: ") << res << endl; continue; } while (!b.empty()) cout << " " << b.pop().getString() << endl; continue; } if (!strcmp(argv[0], "machinfo")) { rpcs::machineInfo mi; if ((res = r.getMachineInfo(mi)) != rfsv::E_PSI_GEN_NONE) { cerr << _("Error: ") << res << endl; continue; } cout << _("General:") << endl; cout << _(" Machine Type: ") << mi.machineType << endl; cout << _(" Machine Name: ") << mi.machineName << endl; cout << _(" Machine UID: ") << hex << mi.machineUID << dec << endl; cout << _(" UI Language: ") << mi.uiLanguage << endl; cout << _("ROM:") << endl; cout << _(" Version: ") << mi.romMajor << "." << setw(2) << setfill('0') << mi.romMinor << "(" << mi.romBuild << ")" << endl; cout << _(" Size: ") << mi.romSize / 1024 << "k" << endl; cout << _(" Programmable: ") << (mi.romProgrammable ? _("yes") : _("no")) << endl; cout << _("RAM:") << endl; cout << _(" Size: ") << mi.ramSize / 1024 << "k" << endl; cout << _(" Free: ") << mi.ramFree / 1024 << "k" << endl; cout << _(" Free max: ") << mi.ramMaxFree / 1024 << "k" << endl; cout << _("RAM disk size: ") << mi.ramDiskSize / 1024 << "k" << endl; cout << _("Registry size: ") << mi.registrySize << endl; cout << _("Display size: ") << mi.displayWidth << "x" << mi.displayHeight << endl; cout << _("Time:") << endl; PsiTime pt(&mi.time, &mi.tz); cout << _(" Current time: ") << pt << endl; cout << _(" UTC offset: ") << mi.tz.utc_offset << _(" seconds") << endl; cout << _(" DST: ") << (mi.tz.dst_zones & PsiTime::PSI_TZ_HOME ? _("yes") : _("no")) << endl; cout << _(" Timezone: ") << mi.tz.home_zone << endl; cout << _(" Country Code: ") << mi.countryCode << endl; cout << _("Main battery:") << endl; pt.setPsiTime(&mi.mainBatteryInsertionTime); cout << _(" Changed at: ") << pt << endl; cout << _(" Used for: ") << mi.mainBatteryUsedTime << endl; cout << _(" Status: ") << mi.mainBatteryStatus << endl; cout << _(" Current: ") << mi.mainBatteryCurrent << " mA" << endl; cout << _(" UsedPower: ") << mi.mainBatteryUsedPower << " mAs" << endl; cout << _(" Voltage: ") << mi.mainBatteryVoltage << " mV" << endl; cout << _(" Max. voltage: ") << mi.mainBatteryMaxVoltage << " mV" << endl; cout << _("Backup battery:") << endl; cout << _(" Status: ") << mi.backupBatteryStatus << endl; cout << _(" Voltage: ") << mi.backupBatteryVoltage << " mV" << endl; cout << _(" Max. voltage: ") << mi.backupBatteryMaxVoltage << " mV" << endl; cout << _("External power:") << endl; cout << _(" Supplied: ") << (mi.externalPower ? _("yes") : _("no")) << endl; cout << _(" Used for: ") << mi.externalPowerUsedTime << endl; continue; } if (!strcmp(argv[0], "runrestore") && (argc == 2)) { startPrograms(r, a, argv[1]); continue; } if (!strcmp(argv[0], "killsave") && (argc == 2)) { stopPrograms(r, argv[1]); continue; } if (!strcmp(argv[0], "putclip") && (argc == 2)) { if (putClipText(r, a, rc, rclipSocket, argv[1])) cerr << _("Error setting clipboard") << endl; continue; } if (!strcmp(argv[0], "getclip") && (argc == 2)) { if (getClipData(r, a, rc, rclipSocket, argv[1])) cerr << _("Error getting clipboard") << endl; continue; } if (!strcmp(argv[0], "kill") && (argc >= 2)) { processList tmp; bool anykilled = false; if ((res = r.queryPrograms(tmp)) != rfsv::E_PSI_GEN_NONE) cerr << _("Error: ") << res << endl; else { for (int i = 1; i < argc; i++) { int kpid; if (!strcmp(argv[i], "all")) kpid = -1; else sscanf(argv[i], "%d", &kpid); processList::iterator j; for (j = tmp.begin(); j != tmp.end(); j++) { if (kpid == -1 || kpid == j->getPID()) { r.stopProgram(j->getProcId()); anykilled = true; } } if (kpid == -1) break; } if (!anykilled) cerr << _("no such process") << endl; } continue; } if (!strcmp(argv[0], "ps")) { processList tmp; if ((res = r.queryPrograms(tmp)) != rfsv::E_PSI_GEN_NONE) cerr << _("Error: ") << res << endl; else { cout << "PID CMD ARGS" << endl; for (processList::iterator i = tmp.begin(); i != tmp.end(); i++) cout << *i << endl; } continue; } if (strcmp(argv[0], "bye") == 0 || strcmp(argv[0], "quit") == 0) continueRunning = 0; else cerr << _("syntax error. Try \"help\"") << endl; } while (!once && continueRunning); return a.getStatus(); } #define MATCHFUNCTION rl_completion_matches static const char *all_commands[] = { "pwd", "ren", "touch", "gtime", "test", "gattr", "sattr", "devs", "dir", "ls", "dircnt", "cd", "lcd", "get", "put", "mget", "mput", "del", "rm", "mkdir", "rmdir", "prompt", "bye", "cp", "volname", "ps", "kill", "killsave", "runrestore", "run", "machinfo", "ownerinfo", "help", "settime", "setupinfo", NULL }; static const char *localfile_commands[] = { "lcd ", "put ", "mput ", "killsave ", "runrestore ", NULL }; static const char *remote_dir_commands[] = { "cd ", "rmdir ", NULL }; static PlpDir comp_files; static long maskAttr; static char *cplPath; static char* filename_generator(const char *text, int state) { static int len; string tmp; if (!state) { Enum res; len = strlen(text); tmp = psionDir; // cplPath will always be non-NULL here, having been initialized in // do_completion. tmp += cplPath; tmp = rfsv::convertSlash(tmp); if ((res = comp_a->dir(tmp.c_str(), comp_files)) != rfsv::E_PSI_GEN_NONE) { cerr << _("Error: ") << res << endl; return NULL; } } while (!comp_files.empty()) { PlpDirent e = comp_files.front(); long attr = e.getAttr(); comp_files.pop_front(); if ((attr & maskAttr) == 0) continue; tmp = cplPath; tmp += e.getName(); if (!(strncmp(tmp.c_str(), text, len))) { if (attr & rfsv::PSI_A_DIR) { rl_completion_append_character = '\0'; tmp += '/'; } return (strdup(tmp.c_str())); } } return NULL; } static char * command_generator( const char *text, int state) { static int idx, len; const char *name; if (!state) { idx = 0; len = strlen(text); } while ((name = all_commands[idx])) { idx++; if (!strncmp(name, text, len)) return (strdup(name)); } return NULL; } extern "C" { static char * null_completion(const char *, int) { static char null[1] = ""; return null; } static char ** do_completion(const char *text, int start, int end) { char **matches = NULL; rl_completion_entry_function = null_completion; rl_completion_append_character = ' '; rl_attempted_completion_over = 1; if (start == 0) matches = MATCHFUNCTION(text, command_generator); else { int idx = 0; const char *name; char *p; rl_filename_quoting_desired = 1; while ((name = localfile_commands[idx])) { idx++; if (!strncmp(name, rl_line_buffer, strlen(name))) { rl_completion_entry_function = NULL; return NULL; } } maskAttr = 0xffff; idx = 0; free(cplPath); cplPath = xstrdup(text); p = strrchr(cplPath, '/'); if (p) *(++p) = '\0'; else cplPath[0] = '\0'; while ((name = remote_dir_commands[idx])) { idx++; if (!strncmp(name, rl_line_buffer, strlen(name))) maskAttr = rfsv::PSI_A_DIR; } matches = MATCHFUNCTION(text, filename_generator); } return matches; } } void ftp:: initReadline(void) { rl_readline_name = "plpftp"; rl_completion_entry_function = null_completion; rl_attempted_completion_function = do_completion; rl_basic_word_break_characters = " \t\n\"\\'`@><=;|&{("; rl_completer_quote_characters = "\""; } vector ftp:: getCommand() { int ws, quote; static char *buf; vector argv; // Free existing buffer, and reinitialize it. free(buf); buf = NULL; // Get new command. signal(SIGINT, sigint_handler2); buf = readline("> "); if (buf) { add_history(buf); // Parse command into argv. ws = 1; quote = 0; for (char *p = buf; *p; p++) switch (*p) { case ' ': case '\t': if (!quote) { ws = 1; *p = 0; } break; case '"': quote = 1 - quote; if (!quote) *p = 0; break; default: if (ws) argv.push_back(p); ws = 0; } } else { cout << "bye" << endl; } signal(SIGINT, sigint_handler); return argv; } plptools-1.0.26/plpftp/ftp.h000066400000000000000000000032021504470754400157320ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999 Philip Proudman * Copyright (C) 1999-2001 Fritz Elfert * Copyright (C) 2025 Reuben Thomas * * 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 * along with this program; if not, see . * */ #ifndef _ftp_h_ #define _ftp_h_ #include "config.h" #include #include "rfsv.h" #include "Enum.h" class rpcs; class bufferStore; class bufferArray; class ftp { public: ftp(); ~ftp(); int session(rfsv & a, rpcs & r, rclip & rc, ppsocket & rclipSocket, std::vector argv); bool canClip; private: std::vector getCommand(); void initReadline(void); int putClipText(rpcs & r, rfsv & a, rclip & rc, ppsocket & rclipSocket, const char *data); int getClipData(rpcs & r, rfsv & a, rclip & rc, ppsocket & rclipSocket, const char *file); bool checkClipConnection(rfsv &a, rclip & rc, ppsocket & rclipSocket); // utilities void resetUnixWd(); void usage(); char defDrive[9]; char *localDir; }; #endif plptools-1.0.26/plpftp/main.cc000066400000000000000000000122561504470754400162340ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999 Philip Proudman * Copyright (C) 1999-2002 Fritz Elfert * * 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 * along with this program; if not, see . * */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ftp.h" #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include using namespace std; static void help() { cout << _( "Usage: plpftp [OPTIONS]... [FTPCOMMAND]\n" "\n" "If FTPCOMMAND is given, connect; run FTPCOMMAND and\n" "terminate afterwards. If no FTPCOMMAND is given, start up\n" "in interactive mode. For help on supported FTPCOMMANDs,\n" "use `?' or `help' as FTPCOMMAND.\n" "\n" "Supported options:\n" "\n" " -h, --help Display this text.\n" " -V, --version Print version and exit.\n" " -p, --port=[HOST:]PORT Connect to port PORT on host HOST.\n" " Default for HOST is 127.0.0.1\n" " Default for PORT is " ) << DPORT << "\n\n"; } static void usage() { cerr << _("Try `plpftp --help' for more information") << endl; } static struct option opts[] = { {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, {"port", required_argument, 0, 'p'}, {NULL, 0, 0, 0 } }; void ftpHeader() { cout << _("PLPFTP Version ") << VERSION; cout << _(" Copyright (C) 1999 Philip Proudman") << endl; cout << _(" Additions Copyright (C) 1999-2002 Fritz Elfert ") << endl; cout << _(" & (C) 1999 Matt Gumbley ") << endl; cout << _(" & (C) 2006-2025 Reuben Thomas ") << endl; cout << _("PLPFTP comes with ABSOLUTELY NO WARRANTY.") << endl; cout << _("This is free software, and you are welcome to redistribute it") << endl; cout << _("under GPL conditions; see the COPYING file in the distribution.") << endl; cout << endl; cout << _("FTP like interface started. Type \"?\" for help.") << endl; } static void parse_destination(const char *arg, const char **host, int *port) { if (!arg) return; // We don't want to modify argv, therefore copy it first ... char *argcpy = strdup(arg); char *pp = strchr(argcpy, ':'); if (pp) { // host.domain:400 // 10.0.0.1:400 *pp ++= '\0'; *host = argcpy; } else { // 400 // host.domain // host // 10.0.0.1 if (strchr(argcpy, '.') || !isdigit(argcpy[0])) { *host = argcpy; pp = 0L; } else pp = argcpy; } if (pp) *port = atoi(pp); } int main(int argc, char **argv) { ppsocket *skt; ppsocket *skt2; rfsv *a; rpcs *r; ppsocket *rclipSocket; rclip *rc; ftp f; const char *host = "127.0.0.1"; int status = 0; int sockNum = DPORT; setlocale (LC_ALL, ""); textdomain(PACKAGE); struct servent *se = getservbyname("psion", "tcp"); endservent(); if (se != 0L) sockNum = ntohs(se->s_port); while (1) { int c = getopt_long(argc, argv, "hVp:", opts, NULL); if (c == -1) break; switch (c) { case '?': usage(); return -1; case 'V': cout << _("plpftp Version ") << VERSION << endl; return 0; case 'h': help(); return 0; case 'p': parse_destination(optarg, &host, &sockNum); break; } } if (optind == argc) ftpHeader(); skt = new ppsocket(); if (!skt->connect(host, sockNum)) { cout << _("plpftp: could not connect to ncpd") << endl; return 1; } skt2 = new ppsocket(); if (!skt2->connect(host, sockNum)) { cout << _("plpftp: could not connect to ncpd") << endl; return 1; } rfsvfactory *rf = new rfsvfactory(skt); rpcsfactory *rp = new rpcsfactory(skt2); a = rf->create(false); r = rp->create(false); rclipSocket = new ppsocket(); rclipSocket->connect(NULL, sockNum); if (rclipSocket) rc = new rclip(rclipSocket); f.canClip = rclipSocket && rc ? true : false; if ((a != NULL) && (r != NULL)) { vector args(argv + optind, argv + argc); status = f.session(*a, *r, *rc, *rclipSocket, args); delete r; delete a; delete skt; delete skt2; if (rclipSocket) delete rclipSocket; if (rc) delete rc; } else { cerr << "plpftp: " << rf->getError() << endl; status = 1; } delete rf; delete rp; return status; } plptools-1.0.26/plpfuse/000077500000000000000000000000001504470754400151445ustar00rootroot00000000000000plptools-1.0.26/plpfuse/.gitignore000066400000000000000000000000101504470754400171230ustar00rootroot00000000000000/plpfuseplptools-1.0.26/plpfuse/Makefile.am000066400000000000000000000021201504470754400171730ustar00rootroot00000000000000# plpfuse/Makefile.am # # This file is part of plptools. # # Copyright (C) 2007-2025 Reuben Thomas # # 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 # along with this program; if not, see . sbin_PROGRAMS = plpfuse plpfuse_CPPFLAGS = -I$(top_srcdir)/lib -I$(top_srcdir)/libgnu -I$(top_builddir)/libgnu plpfuse_CFLAGS = $(FUSE_CFLAGS) $(WARN_CFLAGS) plpfuse_CXXFLAGS = $(FUSE_CFLAGS) $(WARN_CXXFLAGS) plpfuse_LDADD = $(LIB_PLP) $(INTLLIBS) $(FUSE_LIBS) $(top_builddir)/libgnu/libgnu.a plpfuse_SOURCES = main.cc fuse.c rfsv_api.h plpfuse.h plptools-1.0.26/plpfuse/fuse.c000066400000000000000000000337631504470754400162660ustar00rootroot00000000000000/* plpfuse: Expose EPOC's file system via FUSE Copyright (C) 2001-2007 Miklos Szeredi Copyright (C) 2007-2024 Reuben Thomas This program can be distributed under the terms of the GNU GPL. See the file COPYING. */ #include "config.h" #include #include #include #include #include #include #include #include #include #ifdef HAVE_ATTR_XATTR_H #include #elif defined(HAVE_SYS_XATTR_H) #include #endif #ifndef ENOATTR #define ENOATTR ENODATA #endif #ifdef HAVE_ATTR_ATTRIBUTES_H #include #endif #include "plpfuse.h" #include "rfsv_api.h" /* Name of our extended attribute */ #define XATTR_NAME "user.epoc" /* Maximum length of a generated psion xattr string */ #define XATTR_MAXLEN 3 #ifndef ENOMEDIUM #define ENOMEDIUM ENODEV #endif int debug; void debuglog(const char *fmt, ...) { va_list ap; char *buf; if (!debug) return; va_start(ap, fmt); if (vasprintf(&buf, fmt, ap) == -1) syslog(LOG_DEBUG, "vasprintf error in debuglog"); else { syslog(LOG_DEBUG, "%s", buf); free(buf); } va_end(ap); } static void xattr2pattr(long *psisattr, long *psidattr, const char *oxattr, const char *nxattr) { if ((strchr(oxattr, 'a') == NULL) != (strchr(nxattr, 'a') == NULL)) { /* a = archive */ if (strchr(nxattr, 'a')) *psisattr |= PSI_A_ARCHIVE; else *psidattr |= PSI_A_ARCHIVE; } if ((strchr(oxattr, 'h') == NULL) != (strchr(nxattr, 'h') == NULL)) { /* h = hidden */ if (strchr(nxattr, 'h')) *psisattr |= PSI_A_HIDDEN; else *psidattr |= PSI_A_HIDDEN; } if ((strchr(oxattr, 's') == NULL) != (strchr(nxattr, 's') == NULL)) { /* s = system */ if (strchr(nxattr, 's')) *psisattr |= PSI_A_SYSTEM; else *psidattr |= PSI_A_SYSTEM; } } static void attr2pattr(long oattr, long nattr, char *oxattr, char *nxattr, long *psisattr, long *psidattr) { *psisattr = *psidattr = 0; if ((oattr & 0400) != (nattr & 0400)) { if (nattr & 0400) /* readable */ *psisattr |= PSI_A_READ; else *psidattr |= PSI_A_READ; } if ((oattr & 0200) != (nattr & 0200)) { if (nattr & 0200) /* Not writable -> readonly */ *psidattr |= PSI_A_RDONLY; else *psisattr |= PSI_A_RDONLY; } xattr2pattr(psisattr, psidattr, oxattr, nxattr); } static void pattr2xattr(long psiattr, char *xattr) { *xattr = '\0'; if (psiattr & PSI_A_HIDDEN) strcat(xattr, "h"); if (psiattr & PSI_A_SYSTEM) strcat(xattr, "s"); if (psiattr & PSI_A_ARCHIVE) strcat(xattr, "a"); } static void pattr2attr(long psiattr, long size, long ftime, struct stat *st, char *xattr) { struct fuse_context *ct = fuse_get_context(); memset(st, 0, sizeof(*st)); st->st_uid = ct->uid; st->st_gid = ct->gid; if (psiattr & PSI_A_DIR) { st->st_mode = 0700 | S_IFDIR; st->st_blocks = 1; st->st_size = BLOCKSIZE; st->st_nlink = 2; /* Call getlinks for more accurate count */ } else { st->st_blocks = (size + BLOCKSIZE - 1) / BLOCKSIZE; st->st_size = size; st->st_nlink = 1; st->st_mode = S_IFREG; if (psiattr & PSI_A_READ) st->st_mode |= 0400; /* File readable */ if (!(psiattr & PSI_A_RDONLY)) st->st_mode |= 0200; /* File writeable */ } st->st_mtime = st->st_ctime = st->st_atime = ftime; pattr2xattr(psiattr, xattr); } static device *devices; static int query_devices(void) { device *dp, *np; int link_count = 2; /* set the root link count */ for (dp = devices; dp; dp = np) { np = dp->next; free(dp->name); free(dp); } devices = NULL; if (rfsv_drivelist(&link_count, &devices)) return 1; return 0; } static char * dirname(const char *dir) { static char *namebuf = NULL; if (namebuf) free(namebuf); if (asprintf(&namebuf, "%s\\", dir) == -1) return NULL; return namebuf; } static const char * filname(const char *dir) { char *p; if ((p = (char *) rindex(dir, '\\'))) return p + 1; else return dir; } static int dircount(const char *path, long *count) { dentry *e = NULL; long ret = 0; *count = 0; debuglog("dircount: %s", path); debuglog("RFSV dir %s", path); if ((ret = rfsv_dir(dirname(path), &e)) != 0) return ret; while (e) { struct stat st; dentry *o = e; char xattr[XATTR_MAXLEN + 1]; pattr2attr(e->attr, e->size, e->time, &st, xattr); free(e->name); e = e->next; free(o); if (st.st_nlink > 1) (*count)++; } debuglog("count %d", *count); return ret; } static int getlinks(const char *path, struct stat *st) { long dcount; int ret = dircount(path, &dcount); if (ret == 0) st->st_nlink = dcount + 2; return ret; } static int plp_getattr(const char *path, struct stat *st) { char xattr[XATTR_MAXLEN + 1]; int ret = 0; debuglog("plp_getattr `%s'", ++path); if (strcmp(path, "") == 0) { pattr2attr(PSI_A_DIR, 0, 0, st, xattr); if (!query_devices()) { device *dp; for (dp = devices; dp; dp = dp->next) st->st_nlink++; debuglog("root has %d links", st->st_nlink); } else return rfsv_isalive() ? -ENOENT : -ENOMEDIUM; } else { long pattr, psize, ptime; if (strlen(path) == 2 && path[1] == ':') { debuglog("getattr: device"); if (!query_devices()) { device *dp; for (dp = devices; dp; dp = dp->next) { debuglog("cmp '%c', '%c'", dp->letter, path[0]); if (dp->letter == path[0]) break; } debuglog("device: %s", dp ? "exists" : "does not exist"); pattr2attr(PSI_A_DIR, 0, 0, st, xattr); return getlinks(path, st); } else return rfsv_isalive() ? -ENOENT : -ENOMEDIUM; } debuglog("getattr: fileordir"); if ((ret = rfsv_getattr(path, &pattr, &psize, &ptime)) == 0) { pattr2attr(pattr, psize, ptime, st, xattr); debuglog(" attrs Psion: %x %d %d, UNIX modes: %o, xattrs: %s", pattr, psize, ptime, st->st_mode, xattr); if (st->st_nlink > 1) ret = getlinks(path, st); } } debuglog("getattr: returned %d", ret); return ret; } static int plp_access(const char *path, int mask) { (void)mask; debuglog("plp_access `%s'", ++path); return 0; } static int plp_readlink(const char *path, char *buf, size_t size) { (void)buf; (void)size; debuglog("plp_readlink `%s'", ++path); return -EINVAL; } static int plp_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) { device *dp; dentry *e = NULL; char xattr[XATTR_MAXLEN + 1]; debuglog("plp_readdir `%s'", ++path); (void)offset; (void)fi; if (strcmp(path, "") == 0) { debuglog("readdir root"); if (query_devices() == 0) { for (dp = devices; dp; dp = dp->next) { struct stat st; unsigned char name[3]; name[0] = dp->letter; name[1] = ':'; name[2] = '\0'; pattr2attr(dp->attrib, 1, 0, &st, xattr); if (filler(buf, (char *)name, &st, 0)) break; } } } else { int ret; debuglog("RFSV dir `%s'", dirname(path)); if ((ret = rfsv_dir(dirname(path), &e)) != 0) return ret; debuglog("scanning contents"); while (e) { dentry *o; struct stat st; const char *name = filname(e->name); pattr2attr(e->attr, e->size, e->time, &st, xattr); debuglog(" %s %o %d %d", name, st.st_mode, st.st_size, st.st_mtime); if (filler(buf, name, &st, 0)) break; free(e->name); o = e; e = e->next; free(o); } } debuglog("readdir OK"); return 0; } static int plp_mknod(const char *path, mode_t mode, dev_t dev) { int ret = -EINVAL; debuglog("plp_mknod `%s' %o", ++path, mode); if (S_ISREG(mode) && dev == 0) { uint32_t phandle; if ((ret = rfsv_fcreate(0x200, path, &phandle)) == 0) rfsv_fclose(phandle); } return ret; } static int plp_mkdir(const char *path, mode_t mode) { debuglog("plp_mkdir `%s' %o", ++path, mode); return rfsv_mkdir(path); } static int plp_unlink(const char *path) { debuglog("plp_unlink `%s'", ++path); return rfsv_remove(path); } static int plp_rmdir(const char *path) { debuglog("plp_rmdir `%s'", ++path); return rfsv_rmdir(path); } static int plp_symlink(const char *from, const char *to) { debuglog("plp_symlink `%s' -> `'%s'", ++from, ++to); return -EPERM; } static int plp_rename(const char *from, const char *to) { debuglog("plp_rename `%s' -> `%s'", ++from, ++to); rfsv_remove(to); return rfsv_rename(from, to); } static int plp_link(const char *from, const char *to) { debuglog("plp_link `%s' -> `%s'", ++from, ++to); return -EPERM; } static int plp_chmod(const char *path, mode_t mode) { int ret; long psisattr, psidattr, pattr, psize, ptime; struct stat st; char xattr[XATTR_MAXLEN + 1]; debuglog("plp_chmod `%s'", ++path); if ((ret = rfsv_getattr(path, &pattr, &psize, &ptime)) == 0) { pattr2attr(pattr, psize, ptime, &st, xattr); attr2pattr(st.st_mode, mode, "", "", &psisattr, &psidattr); debuglog(" UNIX old, new: %o, %o; Psion set, clear: %x, %x", st.st_mode, mode, psisattr, psidattr); if ((ret = rfsv_setattr(path, psisattr, psidattr)) == 0) debuglog("chmod succeeded"); } return ret; } static int plp_getxattr(const char *path, const char *name, char *value, size_t size #ifdef __APPLE__ , _GL_UNUSED uint32_t position #endif ) { debuglog("plp_getxattr `%s' %s", ++path, name); if (strcmp(name, XATTR_NAME) == 0) { if (size >= XATTR_MAXLEN) { long pattr, psize, ptime; int ret; if ((ret = rfsv_getattr(path, &pattr, &psize, &ptime)) == 0) { pattr2xattr(pattr, value); debuglog("getxattr succeeded: %s", value); return strlen(value); } else return ret; } else { debuglog("only gave %d bytes, need %d", size, XATTR_MAXLEN); return XATTR_MAXLEN; } } return 0; } static int plp_setxattr(const char *path, const char *name, const char *value, size_t size, int flags #ifdef __APPLE__ , _GL_UNUSED uint32_t position #endif ) { debuglog("plp_setxattr `%s'", ++path); if (strcmp(name, XATTR_NAME) == 0) { int ret; long psisattr, psidattr; char oxattr[XATTR_MAXLEN + 1], nxattr[XATTR_MAXLEN + 1]; #ifdef XATTR_CREATE if (flags & XATTR_CREATE) return -EEXIST; #endif strncpy(nxattr, value, size < XATTR_MAXLEN ? size : XATTR_MAXLEN); nxattr[XATTR_MAXLEN] = '\0'; /* Need to undo earlier increment of path when calling plp_getxattr */ plp_getxattr(path - 1, name, oxattr, XATTR_MAXLEN #ifdef __APPLE__ , 0 #endif ); psisattr = psidattr = 0; xattr2pattr(&psisattr, &psidattr, oxattr, value); debuglog("attrs set %x delete %x; %s, %s", psisattr, psidattr, oxattr, value); if ((ret = rfsv_setattr(path, psisattr, psidattr)) != 0) return ret; debuglog("setxattr succeeded"); return 0; } else { #ifdef XATTR_REPLACE if (flags & XATTR_REPLACE) return -ENOATTR; else #endif return -ENOTSUP; } } static int plp_listxattr(const char *path, char *list, size_t size) { debuglog("plp_listxattr `%s'", ++path); if (size > sizeof(XATTR_NAME)) strcpy(list, XATTR_NAME); return sizeof(XATTR_NAME); } static int plp_removexattr(const char *path, const char *name) { debuglog("plp_removexattr `%s'", ++path); (void)name; return -ENOTSUP; } static int plp_chown(const char *path, uid_t uid, gid_t gid) { (void)uid; (void)gid; debuglog("plp_chown `%s'", ++path); return -EPERM; } static int plp_truncate(const char *path, off_t size) { debuglog("plp_truncate `%s'", ++path); return rfsv_setsize(path, size); } static int plp_utimens(const char *path, const struct timespec ts[2]) { debuglog("plp_utimens `%s'", ++path); return rfsv_setmtime(path, ts[1].tv_sec); } static int plp_open(const char *path, struct fuse_file_info *fi) { debuglog("plp_open `%s'", ++path); (void)fi; return 0; } static int plp_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { long read; (void)fi; debuglog("plp_read `%s' offset %lld size %ld", ++path, offset, size); read = rfsv_read(buf, (long)offset, size, path); debuglog("read returned %ld", read); return read; } static int plp_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { long written; (void)fi; debuglog("plp_write `%s' offset %lld size %ld", ++path, offset, size); written = rfsv_write(buf, offset, size, path); debuglog("write returned %ld", written); return written; } static int plp_statfs(const char *path, struct statvfs *stbuf) { device *dp; (void)path; debuglog("plp_statfs"); stbuf->f_bsize = BLOCKSIZE; stbuf->f_frsize = BLOCKSIZE; if (query_devices() == 0) { for (dp = devices; dp; dp = dp->next) { stbuf->f_blocks += (dp->total + BLOCKSIZE - 1) / BLOCKSIZE; stbuf->f_bfree += (dp->free + BLOCKSIZE - 1) / BLOCKSIZE; } } stbuf->f_bavail = stbuf->f_bfree; /* Don't have numbers for these */ stbuf->f_files = 0; stbuf->f_ffree = stbuf->f_favail = 0; stbuf->f_fsid = FID; stbuf->f_flag = 0; /* don't have mount flags */ stbuf->f_namemax = 255; /* KDMaxFileNameLen% */ return 0; } struct fuse_operations plp_oper = { .getattr = plp_getattr, .access = plp_access, .readlink = plp_readlink, .readdir = plp_readdir, .mknod = plp_mknod, .mkdir = plp_mkdir, .symlink = plp_symlink, .unlink = plp_unlink, .rmdir = plp_rmdir, .rename = plp_rename, .link = plp_link, .chmod = plp_chmod, .setxattr = plp_setxattr, .getxattr = plp_getxattr, .listxattr = plp_listxattr, .removexattr = plp_removexattr, .chown = plp_chown, .truncate = plp_truncate, .utimens = plp_utimens, .open = plp_open, .read = plp_read, .write = plp_write, .statfs = plp_statfs, }; plptools-1.0.26/plpfuse/main.cc000066400000000000000000000273501504470754400164060ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999-2001 Fritz Elfert * Copyright (C) 2007 Reuben Thomas * * 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 * along with this program; if not, see . * */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rfsv_api.h" #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include using namespace std; static rfsv *a; static rfsvfactory *rf; static rpcs *r; static rpcsfactory *rp; static bufferStore owner; /* Translate EPOC/SIBO error to UNIX error code, leaving positive numbers alone */ int epocerr_to_errno(long epocerr) { int unixerr = (int)epocerr; if (epocerr < 0) { switch (epocerr) { case rfsv::E_PSI_GEN_NONE: unixerr = 0; break; case rfsv::E_PSI_FILE_EXIST: unixerr = -EEXIST; break; case rfsv::E_PSI_FILE_NXIST: case rfsv::E_PSI_FILE_DIR: unixerr = -ENOENT; break; case rfsv::E_PSI_FILE_WRITE: case rfsv::E_PSI_FILE_READ: case rfsv::E_PSI_FILE_EOF: // Can't err = EOF as it's not an error code case rfsv::E_PSI_FILE_ALLOC: // FIXME: No idea what this is case rfsv::E_PSI_FILE_UNKNOWN: case rfsv::E_PSI_FILE_DIRFULL: unixerr = -EPERM; break; case rfsv::E_PSI_FILE_FULL: unixerr = -ENOSPC; break; case rfsv::E_PSI_FILE_NAME: case rfsv::E_PSI_FILE_RECORD: case rfsv::E_PSI_FILE_VOLUME: unixerr = -EINVAL; break; case rfsv::E_PSI_FILE_ACCESS: case rfsv::E_PSI_FILE_LOCKED: case rfsv::E_PSI_FILE_RDONLY: case rfsv::E_PSI_FILE_PROTECT: unixerr = -EACCES; break; case rfsv::E_PSI_GEN_INUSE: case rfsv::E_PSI_FILE_DEVICE: case rfsv::E_PSI_FILE_PENDING: case rfsv::E_PSI_FILE_NOTREADY: unixerr = -EBUSY; break; case rfsv::E_PSI_FILE_INV: case rfsv::E_PSI_FILE_RETRAN: case rfsv::E_PSI_FILE_LINE: case rfsv::E_PSI_FILE_INACT: case rfsv::E_PSI_FILE_PARITY: case rfsv::E_PSI_FILE_FRAME: case rfsv::E_PSI_FILE_OVERRUN: case rfsv::E_PSI_FILE_CORRUPT: case rfsv::E_PSI_FILE_INVALID: case rfsv::E_PSI_FILE_ABORT: case rfsv::E_PSI_FILE_ERASE: case rfsv::E_PSI_FILE_NDISC: case rfsv::E_PSI_FILE_DRIVER: case rfsv::E_PSI_FILE_COMPLETION: default: unixerr = -EIO; break; case rfsv::E_PSI_FILE_CANCEL: unixerr = -EINTR; break; case rfsv::E_PSI_FILE_DISC: case rfsv::E_PSI_FILE_CONNECT: unixerr = -ENODEV; break; case rfsv::E_PSI_FILE_TOOBIG: unixerr = -EFBIG; break; case rfsv::E_PSI_FILE_HANDLE: unixerr = -EBADF; break; } } debuglog("EPOC error %ld became UNIX code %d", epocerr, unixerr); return unixerr; } int rfsv_isalive(void) { if (!a) { if (!(a = rf->create(true))) return 0; } return a->getStatus() == rfsv::E_PSI_GEN_NONE; } int rfsv_dir(const char *file, dentry **e) { PlpDir entries; dentry *tmp; long ret; if (!a) return -ENODEV; ret = a->dir(file, entries); for (int i = 0; i < entries.size(); i++) { PlpDirent pe = entries[i]; tmp = *e; *e = (dentry *)calloc(1, sizeof(dentry)); if (!*e) return -ENODEV; (*e)->time = pe.getPsiTime().getTime(); (*e)->size = pe.getSize(); (*e)->attr = pe.getAttr(); (*e)->name = strdup(pe.getName()); (*e)->next = tmp; } return epocerr_to_errno(ret); } int rfsv_dircount(const char *file, uint32_t *count) { if (!a) return -ENODEV; return epocerr_to_errno(a->dircount(file, *count)); } int rfsv_rmdir(const char *name) { if (!a) return -ENODEV; return epocerr_to_errno(a->rmdir(name)); } int rfsv_mkdir(const char *file) { if (!a) return -ENODEV; return epocerr_to_errno(a->mkdir(file)); } int rfsv_remove(const char *file) { if (!a) return -ENODEV; return epocerr_to_errno(a->remove(file)); } int rfsv_fclose(long handle) { if (!a) return -ENODEV; return epocerr_to_errno(a->fclose(handle)); } int rfsv_fcreate(long attr, const char *file, uint32_t *handle) { uint32_t ph; long ret; if (!a) return -ENODEV; ret = a->fcreatefile(attr, file, ph); *handle = ph; return epocerr_to_errno(ret); } int rfsv_open(const char *name, long mode, uint32_t *handle) { long ret, retry; if (!a) return -ENODEV; if (mode == O_RDONLY) mode = rfsv::PSI_O_RDONLY; else mode = rfsv::PSI_O_RDWR; for (retry = 100; retry > 0 && (ret = a->fopen(a->opMode(mode), name, *handle)) != rfsv::E_PSI_GEN_NONE; retry--) usleep(20000); return epocerr_to_errno(ret); } int rfsv_read(char *buf, long offset, long len, const char *name) { uint32_t ret = 0, r_offset, handle; if (!a) return -ENODEV; if ((ret = rfsv_open(name, O_RDONLY, &handle))) return ret; if (a->fseek(handle, offset, rfsv::PSI_SEEK_SET, r_offset) != rfsv::E_PSI_GEN_NONE || offset != r_offset || a->fread(handle, (unsigned char *)buf, len, ret) != rfsv::E_PSI_GEN_NONE) ret = -1; rfsv_fclose(handle); return epocerr_to_errno(ret); } int rfsv_write(const char *buf, long offset, long len, const char *name) { uint32_t ret = 0, r_offset, handle; if (!a) return -ENODEV; if ((ret = rfsv_open(name, O_RDWR, &handle))) return ret; if (a->fseek(handle, offset, rfsv::PSI_SEEK_SET, r_offset) != rfsv::E_PSI_GEN_NONE || offset != r_offset || a->fwrite(handle, (unsigned char *)buf, len, ret) != rfsv::E_PSI_GEN_NONE) ret = -1; rfsv_fclose(handle); return epocerr_to_errno(ret); } int rfsv_setmtime(const char *name, long time) { if (!a) return -ENODEV; return epocerr_to_errno(a->fsetmtime(name, PsiTime(time))); } int rfsv_setsize(const char *name, long size) { uint32_t ph; long ret; if (!a) return -ENODEV; ret = a->fopen(a->opMode(rfsv::PSI_O_RDWR), name, ph); if (!ret) { ret = a->fsetsize(ph, size); a->fclose(ph); } return epocerr_to_errno(ret); } int rfsv_setattr(const char *name, long sattr, long dattr) { if (!a) return -ENODEV; return epocerr_to_errno(a->fsetattr(name, sattr, dattr)); } int rfsv_getattr(const char *name, long *attr, long *size, long *time) { long res; PlpDirent e; if (!a) return -ENODEV; res = a->fgeteattr(name, e); *attr = e.getAttr(); *size = e.getSize(); *time = e.getPsiTime().getTime(); return epocerr_to_errno(res); } int rfsv_rename(const char *oldname, const char *newname) { if (!a) return -ENODEV; return epocerr_to_errno(a->rename(oldname, newname)); } int rfsv_drivelist(int *cnt, device **dlist) { *dlist = NULL; uint32_t devbits; long ret; int i; if (!a) return -ENODEV; ret = a->devlist(devbits); if (ret == 0) for (i = 0; i < 26; i++) { PlpDrive drive; if ((devbits & 1) && ((a->devinfo(i + 'A', drive) == rfsv::E_PSI_GEN_NONE))) { device *next = *dlist; *dlist = (device *)malloc(sizeof(device)); (*dlist)->next = next; (*dlist)->name = strdup(drive.getName().c_str()); (*dlist)->total = drive.getSize(); (*dlist)->free = drive.getSpace(); (*dlist)->letter = 'A' + i; (*dlist)->attrib = drive.getMediaType(); (*cnt)++; } devbits >>= 1; } return epocerr_to_errno(ret); } static void help() { cerr << _( "Usage: plpfuse [OPTION...] MOUNTPOINT\n" "\n" "plpfuse options:\n" "\n" " -d, --debug Increase debugging level\n" " -h, --help Display this text\n" " -V, --version Print version and exit\n" " -p, --port=[HOST:]PORT Connect to port PORT on host HOST\n" " Default for HOST is 127.0.0.1\n" " Default for PORT is " ) << DPORT << "\n\n"; } static struct option opts[] = { {"help", no_argument, nullptr, 'h'}, {"debug", no_argument, nullptr, 'd'}, {"version", no_argument, nullptr, 'V'}, {"port", required_argument, nullptr, 'p'}, {nullptr, 0, nullptr, 0 } }; static void parse_destination(const char *arg, const char **host, int *port) { if (!arg) return; // We don't want to modify argv, therefore copy it first ... char *argcpy = strdup(arg); char *pp = strchr(argcpy, ':'); if (pp) { // host.domain:400 // 10.0.0.1:400 *pp ++= '\0'; *host = argcpy; } else { // 400 // host.domain // host // 10.0.0.1 if (strchr(argcpy, '.') || !isdigit(argcpy[0])) { *host = argcpy; pp = nullptr; } else pp = argcpy; } if (pp) *port = atoi(pp); } int fuse(int argc, char *argv[]) { struct fuse_args args = FUSE_ARGS_INIT(argc, argv); struct fuse_chan *ch; char *mountpoint; int err = -1, foreground; if (fuse_parse_cmdline(&args, &mountpoint, NULL, &foreground) != -1 && (ch = fuse_mount(mountpoint, &args)) != NULL) { if (fuse_daemonize(foreground) != -1) { struct fuse *fp = fuse_new(ch, &args, &plp_oper, sizeof(plp_oper), NULL); if (fp != NULL) err = fuse_loop(fp); } fuse_unmount(mountpoint, ch); } fuse_opt_free_args(&args); return err ? 1 : 0; } int main(int argc, char**argv) { ppsocket *skt, *skt2; const char *host = "127.0.0.1"; int sockNum = DPORT, i, c, oldoptind = 1; struct servent *se = getservbyname("psion", "tcp"); endservent(); if (se != nullptr) sockNum = ntohs(se->s_port); /* N.B. Option handling is kludged. Most of the options are shared with FUSE, except for -p/--port, which has to be removed from argv so that FUSE doesn't see it. Hence, we don't complain about unknown options, but leave that to FUSE, and similarly we don't quit after issuing a version or help message. */ opterr = 0; // Suppress errors from unknown options while ((c = getopt_long(argc, argv, "hVp:d", opts, NULL)) != -1) { switch (c) { case 'V': cerr << _("plpfuse version ") << VERSION << endl; break; case 'h': help(); break; case 'd': debug++; break; case 'p': parse_destination(optarg, &host, &sockNum); argc -= optind - oldoptind; for (i = oldoptind; i < argc; i++) argv[i] = argv[i + (optind - oldoptind)]; break; } if (optind >= argc) break; } skt = new ppsocket(); if (!skt->connect(host, sockNum)) { cerr << _("plpfuse: could not connect to ncpd") << endl; return 1; } skt2 = new ppsocket(); if (!skt2->connect(host, sockNum)) { cerr << _("plpfuse: could not connect to ncpd") << endl; return 1; } rf = new rfsvfactory(skt); rp = new rpcsfactory(skt2); a = rf->create(true); r = rp->create(true); if (a != NULL && r != NULL) debuglog("plpfuse: connected"); else debuglog("plpfuse: could not create rfsv or rpcs object, connect delayed"); return fuse(argc, argv); } plptools-1.0.26/plpfuse/plpfuse.h000066400000000000000000000030501504470754400167710ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 2007-2024 Reuben Thomas * * 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 * along with this program; if not, see . * */ #ifndef _plpfuse_h_ #define _plpfuse_h_ #include typedef struct p_inode { int inode; char *name; struct p_inode *nextnam, *nextnum; } p_inode; /** * Description of a Psion-Device */ typedef struct p_device { char *name; /* Volume-Name */ char letter; /* Drive-Letter */ long attrib; /* Device-Attribs */ long total; /* Total capacity in bytes */ long free; /* Free space in bytes */ struct p_device *next; } device; /* * Description of a Psion-File/Dir */ typedef struct p_dentry { char *name; long time; long attr; long size; long links; struct p_dentry *next; } dentry; extern int debug; extern void debuglog(const char *fmt, ...); #define BLOCKSIZE 512 #define FID 7 /* File system id */ #endif extern struct fuse_operations plp_oper; plptools-1.0.26/plpfuse/rfsv_api.h000066400000000000000000000044271504470754400171350ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999-2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #ifndef _rfsv_api_h_ #define _rfsv_api_h_ #ifdef __cplusplus extern "C" { #endif #include "plpfuse.h" extern int psierr_to_errno(long psierr); extern int rfsv_dir(const char *name, dentry **e); extern int rfsv_mkdir(const char *name); extern int rfsv_rmdir(const char *name); extern int rfsv_remove(const char *name); extern int rfsv_rename(const char *oldname, const char *newname); extern int rfsv_open(const char *name, long mode, uint32_t *handle); extern int rfsv_fclose(long handle); extern int rfsv_fcreate(long attr, const char *name, uint32_t *handle); extern int rfsv_read(char *buf, long offset, long len, const char *name); extern int rfsv_write(const char *buf, long offset, long len, const char *name); extern int rfsv_getattr(const char *name, long *attr, long *size, long *time); extern int rfsv_setattr(const char *name, long sattr, long dattr); extern int rfsv_setsize(const char *name, long size); extern int rfsv_setmtime(const char *name, long time); extern int rfsv_drivelist(int *cnt, device **devlist); extern int rfsv_dircount(const char *name, long *count); extern int rfsv_isalive(void); /* File attributes, C-style */ #define PSI_A_RDONLY 0x0001 #define PSI_A_HIDDEN 0x0002 #define PSI_A_SYSTEM 0x0004 #define PSI_A_DIR 0x0008 #define PSI_A_ARCHIVE 0x0010 #define PSI_A_VOLUME 0x0020 #define PSI_A_NORMAL 0x0040 #define PSI_A_TEMP 0x0080 #define PSI_A_COMPRESSED 0x0100 #define PSI_A_READ 0x0200 #define PSI_A_EXEC 0x0400 #define PSI_A_STREAM 0x0800 #define PSI_A_TEXT 0x1000 #ifdef __cplusplus } #endif #endif plptools-1.0.26/plpprint/000077500000000000000000000000001504470754400153365ustar00rootroot00000000000000plptools-1.0.26/plpprint/.gitignore000066400000000000000000000000261504470754400173240ustar00rootroot00000000000000/plpprintd /prolog.ps plptools-1.0.26/plpprint/Makefile.am000066400000000000000000000022411504470754400173710ustar00rootroot00000000000000# plpprint/Makefile.am # # This file is part of plptools. # # Copyright (C) 2002 Fritz Elfert # Copyright (C) 2007-2025 Reuben Thomas # # 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 # along with this program; if not, see . sbin_PROGRAMS = plpprintd plpprintd_CPPFLAGS = -DPKGDATADIR="\"$(pkgdatadir)\"" -I$(top_srcdir)/lib -I$(top_srcdir)/libgnu -I$(top_builddir)/libgnu plpprintd_CXXFLAGS = $(WARN_CXXFLAGS) plpprintd_LDADD = $(LIB_PLP) $(INTLLIBS) $(SERVENT_LIB) $(top_builddir)/libgnu/libgnu.a plpprintd_SOURCES = plpprintd.cc EXTRA_DIST = prolog.ps.in fontmap pkgdata_DATA = prolog.ps fontmap plptools-1.0.26/plpprint/fontmap000066400000000000000000000014471504470754400167330ustar00rootroot00000000000000# This file specifies the font-mapping from Psion fonts # to PostScript fonts for plpprintd. # Format: # # FN:B:I:PSFN # # Where # FN is the Psion fontname # B is a flag for Bold (0 or 1) # I is a flag for Italic (0 or 1) # PSFN is the corresponding PostScript font to use # # Lines starting with a hash and empty lines are ignored. # Times New Roman:0:0:Times-Roman Times New Roman:1:0:Times-Bold Times New Roman:0:1:Times-Italic Times New Roman:1:1:Times-BoldItalic Arial:0:0:Helvetica Arial:1:0:Helvetica-Bold Arial:0:1:Helvetica-Oblique Arial:1:1:Helvetica-BoldOblique Courier New:0:0:Courier Courier New:1:0:Courier-Bold Courier New:0:1:Courier-Oblique Courier New:1:1:Courier-BoldOblique Swiss:0:0:Courier Swiss:1:0:Courier-Bold Swiss:0:1:Courier-Oblique Swiss:1:1:Courier-BoldOblique plptools-1.0.26/plpprint/plpprintd.cc000066400000000000000000001053671504470754400176750ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 1999-2001 Fritz Elfert * * 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 * along with this program; if not, see . * */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ignore-value.h" #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #define TEMPLATE "plpprint_XXXXXX" #define PRINTCMD "lpr -Ppsion" #define SPOOLDIR "/var/spool/plpprint" #define PSDICT "/prolog.ps" using namespace std; const char *spooldir = SPOOLDIR; const char *printcmd = PRINTCMD; wprt *wPrt; bool serviceLoop; bool debug = false; int verbose = 0; static void _log(int priority, const char *fmt, va_list ap) { char *buf; if (vasprintf(&buf, fmt, ap) == -1) return; if (debug) cout << buf << endl; else syslog(priority, "%s", buf); free(buf); } void debuglog(const char *fmt, ...) { va_list ap; va_start(ap, fmt); _log(LOG_DEBUG, fmt, ap); va_end(ap); } void errorlog(const char *fmt, ...) { va_list ap; va_start(ap, fmt); _log(LOG_ERR, fmt, ap); va_end(ap); } void infolog(const char *fmt, ...) { va_list ap; va_start(ap, fmt); _log(LOG_INFO, fmt, ap); va_end(ap); } static int minx, maxx, miny, maxy; string usedfonts; typedef struct fontmap_entry_s { struct fontmap_entry_s *next; char *psifont; bool bold; bool italic; char *psfont; } fontmap_entry; typedef struct const_fontmap_entry_s { const char *psifont; bool bold; bool italic; const char *psfont; } const_fontmap_entry; #define FALLBACK_FONT "Courier" static const_fontmap_entry default_fontmap[] = { // PsionFontname, bold, italic, PSFontName { "Times New Roman", false, false, "Times-Roman"}, { "Times New Roman", true, false, "Times-Bold"}, { "Times New Roman", false, true, "Times-Italic"}, { "Times New Roman", true, true, "Times-BoldItalic"}, { "Arial", false, false, "Helvetica"}, { "Arial", true, false, "Helvetica-Bold"}, { "Arial", false, true, "Helvetica-Oblique"}, { "Arial", true, true, "Helvetica-BoldOblique"}, { "Courier New", false, false, "Courier"}, { "Courier New", true, false, "Courier-Bold"}, { "Courier New", false, true, "Courier-Oblique"}, { "Courier New", true, true, "Courier-BoldOblique"}, { "Swiss", false, false, "Courier"}, { "Swiss", true, false, "Courier-Bold"}, { "Swiss", false, true, "Courier-Oblique"}, { "Swiss", true, true, "Courier-BoldOblique"}, { NULL, false, false, NULL} }; static fontmap_entry *fontmap = NULL; static void init_fontmap() { FILE *f; fontmap_entry *new_fontmap = NULL; fontmap_entry *fe; if ((f = fopen(PKGDATADIR "/fontmap", "r"))) { char *p; int bold; int italic; char *psifont; char *psfont; char *tmp; char buf[1024]; while (fgets(buf, sizeof(buf), f)) { char *bp = buf; int ne; if ((p = strchr(buf, '#'))) *p = '\0'; if ((p = strchr(buf, '\n'))) *p = '\0'; psifont = strsep(&bp, ":"); if (!psifont || !(*psifont)) continue; tmp = strsep(&bp, ":"); if (!tmp || !(*tmp) || (sscanf(tmp, "%d", &bold) != 1)) continue; tmp = strsep(&bp, ":"); if (!tmp || !(*tmp) || (sscanf(tmp, "%d", &italic) != 1)) continue; psfont = strsep(&bp, ":"); if (!psfont || !(*psfont)) continue; fe = (fontmap_entry *)malloc(sizeof(fontmap_entry)); if (!fe) break; if (!(fe->psifont = strdup(psifont))) { free(fe); break; } if (!(fe->psfont = strdup(psfont))) { free(fe->psifont); free(fe); break; } fe->bold = bold ? true : false; fe->italic = italic ? true : false; fe->next = new_fontmap; new_fontmap = fe; } fclose(f); } if (new_fontmap && (new_fontmap->psifont)) fontmap = new_fontmap; else { errorlog("No fontmap found in %s/fontmap, using builtin mapping", PKGDATADIR); for (const_fontmap_entry *cfe = default_fontmap; cfe->psifont; cfe++) { fontmap_entry *nfe = (fontmap_entry *)malloc(sizeof(fontmap_entry)); if (!nfe) break; nfe->next = fontmap; nfe->psifont = strdup(cfe->psifont); nfe->bold = cfe->bold; nfe->italic = cfe->italic; nfe->psfont = strdup(cfe->psfont); fontmap = nfe; } } if (debug) { debuglog("Active Font-Mapping:"); debuglog("%-20s%-7s%-7s%-20s", "Psion", "Bold", "Italic", "PS-Font"); fe = fontmap; while (fe) { debuglog("%-20s%-7s%-7s%-20s", fe->psifont, fe->bold ? "true" : "false", fe->italic ? "true" : "false", fe->psfont); fe = fe->next; } } } static void ps_setfont(FILE *f, const char *fname, bool bold, bool italic, unsigned long fsize) { fontmap_entry *fe = fontmap; const char *psf = NULL; while (fe) { if ((!strcmp(fe->psifont, fname)) && (fe->bold == bold) && (fe->italic == italic)) { psf = fe->psfont; break; } fe = fe->next; } if (!psf) { psf = FALLBACK_FONT; errorlog("No font mapping for '%s' (%s%s%s); fallback to %s", fname, (bold) ? "Bold" : "", (italic) ? "Italic" : "", (bold || italic) ? "" : "Regular", psf); } if (usedfonts.find(psf) == usedfonts.npos) { usedfonts += "%%+ font "; usedfonts += psf; usedfonts += "\n"; } fprintf(f, "%ld /%s F\n", fsize, psf); } static void ps_escape(string &text) { int pos = 0; while ((pos = text.find_first_of("()", pos)) != text.npos) { text.insert(pos, "\\"); pos += 2; } } static void ps_bitmap(FILE *f, unsigned long llx, unsigned long lly, unsigned long urx, unsigned long ury, const char *buf) { bufferStore out; int width, height; if (decodeBitmap((const unsigned char *)buf, width, height, out)) { fprintf(f, "%lu %lu %lu %lu %d %d I\n", llx, lly, urx, ury, width, height); const unsigned char *p = (const unsigned char *)out.getString(0); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) fprintf(f, "%02x", *p++); fprintf(f, "\n"); } } else errorlog("Corrupted bitmap data"); } static void convertPage(FILE *f, int page, bool last, bufferStore buf) { int len = buf.getLen(); int i = 0; long boffset = 0; unsigned long left = 0; unsigned long top = 0; unsigned long right = 0; unsigned long bottom = 0; if (debug) { char dumpname[128]; sprintf(dumpname, "/tmp/pdump_%d", page); FILE *df = fopen(dumpname, "w"); fwrite(buf.getString(0), 1, len, df); fclose(df); debuglog("Saved page input to %s", dumpname); } if (page == 0) { time_t now = time(NULL); fputs( "%!PS-Adobe-3.0\n" "%%Creator: plpprintd " VERSION "\n" "%%CreationDate: ", f); fputs(ctime(&now), f); fputs( "%%Pages: (atend)\n" "%%BoundingBox: (atend)\n" "%%DocumentNeededResources: (atend)\n" "%%LanguageLevel: 2\n" "%%EndComments\n" "%%BeginProlog\n", f); char pbuf[1024]; FILE *pf = fopen(PKGDATADIR PSDICT, "r"); while (fgets(pbuf, sizeof(pbuf), pf)) fputs(pbuf, f); fclose(pf); fputs( "%%EndProlog\n" "%%BeginSetup\n" "currentpagedevice /PageSize get 1 get /top exch def\n" "ip 1 1 TH 32 DM RC 1 PS\n" "%%EndSetup\n", f); minx = miny = 9999; maxx = maxy = 0; usedfonts = ""; } fprintf(f, "%%%%Page: %d %d\n", page+1, page+1); while (i < len) { unsigned char opcode = buf.getByte(i); switch (opcode) { case 0x00: { // Start of section unsigned long section = buf.getDWord(i+1) & 3; unsigned long pagenr = buf.getDWord(i+1) >> 2; fprintf(f, "%% @%d: Section %ld, Page %ld\n", i, section, pagenr); fprintf(f, "1 1 TH 32 DM RC 1 PS CC\n"); // (section & 3) = // 0 = Header, 1 = Body, 2 = Footer, 3 = Footer i += 5; } break; case 0x01: { // End of page i = len + 1; } break; case 0x03: { // Set drawing mode unsigned char drwmode = buf.getByte(i+1); fprintf(f, "%% @%d: Drawing mode %02x\n", i, drwmode); switch (drwmode) { case 0x01: // ~screen break; case 0x02: // colour ^ screen break; case 0x03: // ~colour ^ screen break; case 0x04: // colour | screen break; case 0x05: // colour | ~screen break; case 0x08: // colour & screen break; case 0x09: // colour & ~screen break; case 0x14: // ~colour | screen break; case 0x15: // ~colour | ~screen break; case 0x18: // ~colour & screen break; case 0x19: // ~colour & ~screen break; case 0x20: // colour break; case 0x30: // ~colour break; } fprintf(f, "%d DM\n", drwmode); i += 2; } break; case 0x04: { // Bounding box (clipping rectangle) left = buf.getDWord(i+1); top = buf.getDWord(i+5); right = buf.getDWord(i+9); bottom = buf.getDWord(i+13); if (left < minx) minx = left; if (right > maxx) maxx = right; if (top < miny) miny = top; if (bottom > maxy) maxy = bottom; i += 17; fprintf(f, "%% @%d: bbox %ld %ld %ld %ld\n", i, left, top, right, bottom); fprintf(f, "%ld %ld %ld %ld CB\n", left, top, right, bottom); } break; case 0x05: { // Cancel clipping rect left = top = right = bottom = 0; fprintf(f, "%% @%d: Cancel Cliprect\n", i); fprintf(f, "CC\n"); i++; } break; case 0x06: { // ??? fprintf(f, "%% @%d: U06 %d 0x%08x\n", i, buf.getByte(i+1), buf.getDWord(i+2)); i += 6; } break; case 0x07: { // Font int namelen; int ofs; if (buf.getByte(i+1) & 1) { namelen = buf.getWord(i+1) >> 3; ofs = i + 3; } else { namelen = buf.getByte(i+1) >> 2; ofs = i + 2; } string fname(buf.getString(ofs), namelen); ofs += namelen; int screenfont = buf.getByte(ofs); int basesize = buf.getWord(ofs+1); unsigned long style = buf.getDWord(ofs+3); bool italic = ((style & 1) != 0); bool bold = ((style & 2) != 0); unsigned long fontsize = buf.getDWord(ofs+7); boffset = (long)buf.getDWord(ofs+11); fprintf(f, "%% @%d: Font '%s' %ld %s%s%s\n", i, fname.c_str(), fontsize, bold ? "Bold" : "", italic ? "Italic" : "", (bold || italic) ? "" : "Regular"); ps_setfont(f, fname.c_str(), bold, italic, fontsize); i = ofs + 15; } break; case 0x08: { // End Font fprintf(f, "%% @%d: End Font\n", i); i++; } break; case 0x09: { // underline fprintf(f, "%% @%d: Underline %d\n", i, buf.getByte(i+1)); fprintf(f, "%d UL\n", buf.getByte(i+1)); i += 2; } break; case 0x0a: { // strikethru fprintf(f, "%% @%d: Strikethru %d\n", i, buf.getByte(i+1)); fprintf(f, "%d ST\n", buf.getByte(i+1)); i += 2; } break; case 0x0b: { // newline fprintf(f, "%% @%d: Newline %d %d\n", i, buf.getDWord(i+1), buf.getDWord(i+5)); i += 9; } break; case 0x0c: { // cr fprintf(f, "%% @%d: CR %d %d\n", i, buf.getDWord(i+1), buf.getDWord(i+5)); i += 9; } break; case 0x0d: { // foreground color fprintf(f, "%% @%d: Foreground %d %d %d\n", i, buf.getByte(i+1), buf.getByte(i+2), buf.getByte(i+3)); fprintf(f, "%d %d %d FG\n", buf.getByte(i+1), buf.getByte(i+2), buf.getByte(i+3)); i += 4; } break; case 0x0e: { // Set pen style unsigned char pstyle = buf.getByte(i+1); switch (pstyle) { case 0x00: // Don't draw break; case 0x01: // Solid break; case 0x02: // Dotted line break; case 0x03: // Dashed line break; case 0x04: // Dash Dot break; case 0x05: // Dash Dot Dot break; } fprintf(f, "%% @%d: Pen Style %d\n", i, pstyle); fprintf(f, "%d PS\n", pstyle); i += 2; } break; case 0x0f: { // Pen thickness x, y fprintf(f, "%% @%d: Pen thickness %u %u\n", i, buf.getDWord(i+1), buf.getDWord(i+5)); fprintf(f, "%u %u TH\n", buf.getDWord(i+1), buf.getDWord(i+5)); i += 9; } break; case 0x10: { // background color fprintf(f, "%% @%d: Background %d %d %d\n", i, buf.getByte(i+1), buf.getByte(i+2), buf.getByte(i+3)); fprintf(f, "%d %d %d BG\n", buf.getByte(i+1), buf.getByte(i+2), buf.getByte(i+3)); i += 4; } break; case 0x11: { // Brush style unsigned char bstyle = buf.getByte(i+1); switch (bstyle) { case 0x00: // No brush break; case 0x01: // Solid brush break; case 0x02: // Patterned brush break; case 0x03: // Vertical hatch brush break; case 0x04: // Diagonal hatch brush (bottom left to top right) break; case 0x05: // Horizontal hatch brush break; case 0x06: // Rev. diagonal hatch brush (top left to bottom right) break; case 0x07: // Square cross hatch (horizontal and vertical) break; case 0x08: // Diamond cross hatch (both diagonals) break; } fprintf(f, "%% @%d: Brush style %d\n", i, bstyle); fprintf(f, "%d BS\n", bstyle); i += 2; } break; case 0x17: { // ??? fprintf(f, "%% @%d: U17 %u %u\n", i, buf.getDWord(i+1), buf.getDWord(i+5)); i += 9; } break; case 0x19: { // Draw line fprintf(f, "%% @%d: Line %u %u %u %u\n", i, buf.getDWord(i+1), buf.getDWord(i+5), buf.getDWord(i+9), buf.getDWord(i+13)); fprintf(f, "%u %u %u %u L\n", buf.getDWord(i+1), buf.getDWord(i+5), buf.getDWord(i+9), buf.getDWord(i+13)); i += 17; } break; case 0x1b: { // ??? fprintf(f, "%% @%d: U1b %u %u\n", i, buf.getDWord(i+1), buf.getDWord(i+5)); i += 9; } break; case 0x1f: { // Draw ellipse fprintf(f, "%% @%d: Ellipse %u %u %u %u\n", i, buf.getDWord(i+1), buf.getDWord(i+5), buf.getDWord(i+9), buf.getDWord(i+13)); fprintf(f, "%u %u %u %u E\n", buf.getDWord(i+1), buf.getDWord(i+5), buf.getDWord(i+9), buf.getDWord(i+13)); i += 17; } break; case 0x20: { // Draw rectangle fprintf(f, "%% @%d: Rectangle %u %u %u %u\n", i, buf.getDWord(i+1), buf.getDWord(i+5), buf.getDWord(i+9), buf.getDWord(i+13)); fprintf(f, "%u %u %u %u R\n", buf.getDWord(i+1), buf.getDWord(i+5), buf.getDWord(i+9), buf.getDWord(i+13)); i += 17; } break; case 0x23: { // Draw polygon unsigned long count = buf.getDWord(i+1); int o = i + 5; fprintf(f, "%% @%d: Polygon (%ld segments)\n", i, count); fprintf(f, "[\n"); for (int j = 0; j < count; j++) { fprintf(f, "%d %d\n", buf.getDWord(o), buf.getDWord(o+4)); o += 8; } unsigned char frule = buf.getByte(o); fprintf(f, "] %s P\n", frule ? "false" : "true"); i = o + 1; } break; case 0x25: { // Draw bitmap unsigned long llx = buf.getDWord(i+1); unsigned long lly = buf.getDWord(i+13); unsigned long urx = buf.getDWord(i+9); unsigned long ury = buf.getDWord(i+5); unsigned long blen = buf.getDWord(i+17); fprintf(f, "%% @%d: Bitmap\n", i); ps_bitmap(f, llx, lly, urx, ury, buf.getString(i+17)); i += (17 + blen); } break; case 0x26: { // Draw bitmap unsigned long llx = buf.getDWord(i+1); unsigned long lly = buf.getDWord(i+13); unsigned long urx = buf.getDWord(i+9); unsigned long ury = buf.getDWord(i+5); unsigned long blen = buf.getDWord(i+17); unsigned long u1 = buf.getDWord(i+17+blen); unsigned long u2 = buf.getDWord(i+17+blen+4); unsigned long u3 = buf.getDWord(i+17+blen+8); unsigned long u4 = buf.getDWord(i+17+blen+12); fprintf(f, "%% @%d: Bitmap %ld %ld %ld %ld\n", i, u1, u2, u3, u4); ps_bitmap(f, llx, lly, urx, ury, buf.getString(i+17)); i += (17 + blen + 16); } break; case 0x27: { // Draw label int tlen; int ofs; if (buf.getByte(i+1) & 1) { tlen = buf.getWord(i+1) >> 3; ofs = i + 3; } else { tlen = buf.getByte(i+1) >> 2; ofs = i + 2; } string text(buf.getString(ofs), tlen); ofs += tlen; ps_escape(text); fprintf(f, "%% @%d: Text '%s' %d %d\n", i, text.c_str(), buf.getDWord(ofs), buf.getDWord(ofs+4)); fprintf(f, "(%s) %d %ld 0 0 -1 T\n", text.c_str(), buf.getDWord(ofs), buf.getDWord(ofs+4) + boffset); i = ofs + 8; } break; case 0x28: { // Draw justified text int tlen; int ofs; if (buf.getByte(i+1) & 1) { tlen = buf.getWord(i+1) >> 3; ofs = i + 3; } else { tlen = buf.getByte(i+1) >> 2; ofs = i + 2; } string text(buf.getString(ofs), tlen); ofs += tlen; int left = buf.getDWord(ofs); int top = buf.getDWord(ofs+4); int right = buf.getDWord(ofs+8); int bottom = buf.getDWord(ofs+12); int baseline = buf.getDWord(ofs+16); unsigned char align = buf.getByte(ofs+20); int amargin = buf.getDWord(ofs+21); fprintf(f, "%% @%d: JText '%s' %d %ld %d %d %d %d %d\n", i, text.c_str(), left, bottom + boffset, top, right, baseline, align, amargin); ps_escape(text); if (align == 2) right -= amargin; else left += amargin; bottom -= ((bottom - top) / 4); fprintf(f, "(%s) %d %ld %d %d %d T\n", text.c_str(), left, bottom + boffset, top, right, align); i = ofs + 25; } break; default: fprintf(f, "@%d: UNHANDLED OPCODE %02x\n", i, opcode); debuglog("@%d: UNHANDLED OPCODE %02x", i, opcode); i++; break; } } fprintf(f, "showpage\n"); if (last) { fputs( "%%Trailer\n" "%%DocumentNeededResources: ", f); if (usedfonts.empty()) fputs("none\n", f); else { usedfonts.erase(0, 4); fputs(usedfonts.c_str(), f); } fprintf(f, "%%%%Pages: %d\n", page + 1); fprintf(f, "%%%%BoundingBox: %d %d %d %d\n", minx / 20, miny / 20, maxx / 20, maxy / 20); fputs("%%EOF\n", f); } } static unsigned char fakePage[15] = { 0x2a, 0x2a, 0x09, 0x00, 0x00, 0x00, 0x82, 0x2e, 0x00, 0x00, 0xc6, 0x41, 0x00, 0x00, 0x00, }; const static void service_loop() { bool jobLoop = true; while (jobLoop) { bool spoolOpen = false; bool pageStart = true; bool cancelled = false; bool jobEnd; unsigned long plen; int pageCount = 0; bufferStore buf; bufferStore pageBuf; int fd; FILE *f = nullptr; unsigned char b; char *jname = (char *)malloc(strlen(spooldir) + strlen(TEMPLATE) + 2); while (jobLoop) { /* Job loop */ buf.init(); switch (wPrt->getData(buf)) { case rfsv::E_PSI_FILE_DISC: jobLoop = false; break; case rfsv::E_PSI_GEN_NONE: if ((buf.getLen() == 15) && (!memcmp(buf.getString(0), fakePage, 15))) { cancelled = false; if (spoolOpen) { fclose(f); infolog("Cancelled job %s", jname); unlink(jname); break; } continue; } if (!spoolOpen && !cancelled) { sprintf(jname, "%s/%s", spooldir, TEMPLATE); if ((fd = mkstemp(jname)) != -1) { infolog("Receiving new job %s", jname); spoolOpen = true; pageStart = true; pageCount = 0; } else { errorlog("Could not create spool file."); cancelled = true; wPrt->cancelJob(); } f = fdopen(fd, "w"); plen = 0; } b = buf.getByte(0); if ((b != 0x2a) && (b != 0xff)) { errorlog("Invalid packet type 0x%02x.", b); cancelled = true; wPrt->cancelJob(); } jobEnd = (b == 0xff); if (!cancelled) { buf.discardFirstBytes(1); if (pageStart) { b = buf.getByte(0); plen = buf.getDWord(1) - 8; buf.discardFirstBytes(5+8); pageStart = false; pageBuf.init(); } pageBuf.addBuff(buf); plen -= buf.getLen(); if (plen <= 0) { convertPage(f, pageCount++, jobEnd, pageBuf); pageBuf.init(); pageStart = true; } } if (jobEnd) { if (spoolOpen) fclose(f); if (!cancelled) { if (pageCount > 0) { if (!printcmd) infolog("Output stored in %s", jname); else { int r; char cbuf[4096]; infolog("Spooling %d pages", pageCount); FILE *pipe = popen(printcmd, "w"); if (!pipe) { errorlog("Could not execute %s: %m", printcmd); unlink(jname); } f = fopen(jname, "r"); if (!f) { errorlog("Could not read %s: %m", jname); pclose(pipe); unlink(jname); } while ((r = fread(cbuf, 1, sizeof(cbuf), f)) > 0) fwrite(cbuf, 1, r, pipe); pclose(pipe); fclose(f); unlink(jname); } } } else unlink(jname); spoolOpen = false; } break; } } free(jname); } } static void help() { cout << "Options of plpprintd:\n" "\n" " -d, --debug Debugging, do not fork.\n" " -h, --help Display this text.\n" " -v, --verbose Increase verbosity.\n" " -V, --version Print version and exit.\n" " -p, --port=[HOST:]PORT Connect to port PORT on host HOST.\n" " -s, --spooldir=DIR Specify spooldir DIR.\n" " Default: " SPOOLDIR "\n" " -c, --printcmd=CMD Specify print command.\n" " Default: " PRINTCMD "\n"; } static void usage() { cerr << "Usage: plpprintd [OPTIONS]" << endl << "Use --help for more information" << endl; } static struct option opts[] = { {"debug", no_argument, nullptr, 'd'}, {"help", no_argument, nullptr, 'h'}, {"version", no_argument, nullptr, 'V'}, {"verbose", no_argument, nullptr, 'v'}, {"port", required_argument, nullptr, 'p'}, {"spooldir", required_argument, nullptr, 's'}, {"printcmd", required_argument, nullptr, 'c'}, {nullptr, 0, nullptr, 0 } }; static void parse_destination(const char *arg, const char **host, int *port) { if (!arg) return; // We don't want to modify argv, therefore copy it first ... char *argcpy = strdup(arg); char *pp = strchr(argcpy, ':'); if (pp) { // host.domain:400 // 10.0.0.1:400 *pp ++= '\0'; *host = argcpy; } else { // 400 // host.domain // host // 10.0.0.1 if (strchr(argcpy, '.') || !isdigit(argcpy[0])) { *host = argcpy; pp = nullptr; } else pp = argcpy; } if (pp) *port = atoi(pp); } int main(int argc, char **argv) { ppsocket *skt; const char *host = "127.0.0.1"; int sockNum = DPORT; int ret = 0; int c; struct servent *se = getservbyname("psion", "tcp"); endservent(); if (se != nullptr) sockNum = ntohs(se->s_port); while (1) { c = getopt_long(argc, argv, "dhVvp:s:c:", opts, NULL); if (c == -1) break; switch (c) { case '?': usage(); return -1; case 'd': debug = true; break; case 'v': verbose++; break; case 'V': cout << "plpprintd Version " << VERSION << endl; return 0; case 'h': help(); return 0; case 'p': parse_destination(optarg, &host, &sockNum); break; case 's': spooldir = strdup(optarg); break; case 'c': if (!strcmp(optarg, "-")) printcmd = NULL; else printcmd = strdup(optarg); break; } } if (optind < argc) { usage(); return -1; } skt = new ppsocket(); if (!skt->connect(host, sockNum)) { cout << _("plpprintd: could not connect to ncpd") << endl; return 1; } if (!debug) ret = fork(); switch (ret) { case 0: /* child */ setsid(); ignore_value(chdir("/")); if (!debug) { openlog("plpprintd", LOG_PID|LOG_CONS, LOG_DAEMON); int devnull = open("/dev/null", O_RDWR, 0); if (devnull != -1) { dup2(devnull, STDIN_FILENO); dup2(devnull, STDOUT_FILENO); dup2(devnull, STDERR_FILENO); if (devnull > 2) close(devnull); } } init_fontmap(); infolog("started, waiting for requests."); serviceLoop = true; while (serviceLoop) { wPrt = new wprt(skt); if (wPrt) { Enum ret; ret = wPrt->initPrinter(); if (ret == rfsv::E_PSI_GEN_NONE) service_loop(); else { if (debug) debuglog("plpprintd: could not connect: %s", ret.toString().c_str()); } delete wPrt; sleep(1); } else { errorlog("plpprintd: Could not create wprt object"); exit(1); } } break; case -1: cerr << "plpprintd: fork failed" << endl; return 1; default: /* parent */ break; } return 0; } plptools-1.0.26/plpprint/prolog.ps.in000066400000000000000000000131041504470754400176100ustar00rootroot00000000000000%%BeginResource: plpprint_prolog @VERSION@ 0 /bd{bind def}bind def /ed{exch def}bd /d{def}bd /e{exch}bd /twips{1440 div 72 mul}bd /pixel{10 twips mul}bd /ul false d /st false d /bg[0 0 0]d /fg[0 0 0]d /pen_st 0 d /pen_th 0 d /brush_st 0 d /dofill false d /parray 6 array d /EpocEncoding ISOLatin1Encoding 256 array copy dup 128 [/Euro/.notdef/quotesinglbase/florin/quotedblbase/ellipsis/dagger/daggerdbl /circumflex/perthousand/Scaron/guilsinglleft/OE/.notdef/Zcaron/.notdef /.notdef/quoteleft/quoteright/quotedblleft/quotedblright/bullet/endash /emdash/tilde/trademark/scaron/guilsinglright/oe/.notdef/zcaron/ydieresis] putinterval d /dashes[[][1 2][3][1 2 3 2][1 2 1 2 3 2]]d /s{ pen_st 0 ne{ dashes pen_st 1 sub get 0 setdash stroke }if }bd /bp1{begin 0 setgray}bd /bp2{lineto s end}bd /bps[ {bp1 0 0 moveto 0 8 bp2}bind {bp1 0 0 moveto 8 8 bp2}bind {bp1 0 0 moveto 8 0 bp2}bind {bp1 0 8 moveto 8 0 bp2}bind {bp1 0 8 moveto 0 0 lineto 8 0 bp2}bind {bp1 0 0 moveto 8 8 lineto s 0 8 moveto 8 0 bp2}bind ]d /ip{ % init patterns 0 1 5{ /i ed parray i << /PatternType 1 /PaintType 1 /TilingType 2 /BBox [0 0 8 8] /XStep 8 /YStep 8 >> dup/PaintProc bps i get put matrix makepattern put }for }bd /fs{ % fontvalue fs pointvalue 1000 div 24 mul }bd /m{ top e sub moveto }bd /l{ top e sub lineto }bd /f{ dofill{ gsave fill grestore brush_st 2 gt{ gsave parray brush_st 3 sub get setpattern fill grestore /DeviceRGB setcolorspace fg SC }if }if }bd /ef{ dofill{ gsave eofill grestore brush_st 2 gt{ gsave parray brush_st 3 sub get setpattern eofill grestore /DeviceRGB setcolorspace fg SC }if }if }bd /UL{ 1 eq/ul ed }bd /ST{ 1 eq/st ed }bd /BG{ % r g b BG - (store background color) mark 4 1 roll ]/bg ed }bd /FG{ % r g b FG - (store foreground color) mark 4 1 roll ]/fg ed }bd /RC{ % - RC - (reset colors) 0 BS 0 0 0 FG }bd /SC{ % colorarray SC - (set stored color) dup 0 get 255 div e dup 1 get 255 div e 2 get 255 div setrgbcolor }bd /F{ findfont % reencode for ISOLatin1. (From redbook sec. 5.6.1) and add some % special symbols, resulting in an EPOC encoding dup length dict begin { 1 index dup/FID ne{ /CharStrings ne{ d }{ % must copy CharStrings dict to make it writeable dup length dict begin{def}forall % copy /Euro charstring from Symbol font /Euro /Symbol findfont /CharStrings get /Euro get def currentdict end d }ifelse }{ pop pop pop }ifelse }forall % Replace encoding /Encoding EpocEncoding d currentdict end dup/FontName get 80 string cvs (-EPOCLatin15) concatstrings cvn e definefont % end of reencoding e twips scalefont setfont }bd /L{ % x1 y1 x2 y2 L - (draw line from x1,y1 to x2,y2) 4 -1 roll twips 4 -1 roll twips m twips e twips e l fg SC s }bd /R{ % left top right bottom R - (draw rectangle) 4 dict begin twips/y2 ed twips/x2 ed twips/y1 ed twips/x1 ed newpath x1 y1 m x2 y1 l x2 y2 l x1 y2 l closepath gsave bg SC f grestore fg SC s end }bd /E{ % ulx uly llx lly E - (draw ellipse) 6 dict begin twips/lly ed twips/llx ed twips/uly ed twips/ulx ed /wx llx ulx sub d /wy lly uly sub d gsave newpath ulx wx 2 div add top uly sub wy 2 div sub translate 1 wy wx div scale newpath wx 2 div 0 moveto 0 0 wx 2 div 0 360 arc closepath gsave bg SC f grestore fg SC s grestore end }bd /P{ % pointarray eofill P - (draw polygon) 5 dict begin /efmode ed /points ed 0 2 points length 1 sub{ /idx ed points idx get twips points idx 1 add get twips idx 0 eq{m}{l}ifelse }for gsave bg SC efmode{ef}{f}ifelse grestore fg SC s end }bd /T{ % string left bottom top right justify T - (draw text) 6 dict begin /just ed twips/x2 ed twips/y2 ed twips/y1 ed twips/x1 ed dup stringwidth pop/sw ed just 0 gt{ just 1 gt{x2 sw sub}{x2 x1 sub sw sub 2 div x1 add}ifelse }{x1}ifelse y1 m gsave ul{ gsave currentfont/FontInfo known{ currentfont/FontInfo get begin 0 UnderlinePosition fs rmoveto UnderlineThickness fs setlinewidth end }{ 0 -10 rmoveto 0.5 setlinewidth }ifelse sw 0 rlineto s pen_th setlinewidth grestore }if st{ gsave newpath 0 0 moveto (I) false charpath pathbbox e pop e sub e pop 2 div 0 e grestore gsave rmoveto sw 0 rlineto s grestore }if show grestore end }bd /I{ 7 dict begin /rows ed /cols ed twips/ury ed twips/urx ed twips/lly ed twips/llx ed /ibuf cols string d gsave llx top lly sub translate urx llx sub lly ury sub scale cols rows 8 [cols 0 0 rows neg 0 rows] {currentfile ibuf readhexstring pop}image grestore end }bd /TH{ % xwid ywid TH - (set pen thickness) pop pixel/pen_th ed pen_th setlinewidth }bd /DM{ % mode DM - (set drawing mode) pop }bd /PS{ % style PS - (set pen style) /pen_st ed }bd /BS{ % style BS - (set brush style) dup/brush_st ed 0 ne/dofill ed }bd /CB{ % left top right bottom CB - (clipping bbox) 4 dict begin twips/y2 ed twips/x2 ed twips/y1 ed twips/x1 ed newpath x1 y1 m x2 y1 l x2 y2 l x1 y2 l closepath clip newpath end }bd /CC{ % - CC - (restore clipping) initclip }bd %%EndResource plptools-1.0.26/po/000077500000000000000000000000001504470754400141045ustar00rootroot00000000000000plptools-1.0.26/po/.gitignore000066400000000000000000000003251504470754400160740ustar00rootroot00000000000000/POTFILES /*.gmo /*.pot /stamp-po /remove-potcdate.sed /Makefile.in.in /Makevars.template /Rules-quot /boldquot.sed /en@boldquot.header /en@quot.header /insert-header.sin /quot.sed /remove-potcdate.sin /ChangeLog plptools-1.0.26/po/Makevars000066400000000000000000000067171504470754400156130ustar00rootroot00000000000000# Makefile variables for PO directory in any package using GNU gettext. # Usually the message domain is the same as the package name. DOMAIN = $(PACKAGE) # These two variables depend on the location of this directory. subdir = po top_builddir = .. # These options get passed to xgettext. XGETTEXT_OPTIONS = --keyword=_ --keyword=N_ \ --flag=_:1:pass-c-format \ --flag=N_:1:pass-c-format \ $${end_of_xgettext_options+} # This is the copyright holder that gets inserted into the header of the # $(DOMAIN).pot file. Set this to the copyright holder of the surrounding # package. (Note that the msgstr strings, extracted from the package's # sources, belong to the copyright holder of the package.) Translators are # expected to transfer the copyright for their translations to this person # or entity, or to disclaim their copyright. The empty string stands for # the public domain; in this case the translators are expected to disclaim # their copyright. COPYRIGHT_HOLDER = plptools developers # This tells whether or not to prepend "GNU " prefix to the package # name that gets inserted into the header of the $(DOMAIN).pot file. # Possible values are "yes", "no", or empty. If it is empty, try to # detect it automatically by scanning the files in $(top_srcdir) for # "GNU packagename" string. PACKAGE_GNU = # This is the email address or URL to which the translators shall report # bugs in the untranslated strings: # - Strings which are not entire sentences, see the maintainer guidelines # in the GNU gettext documentation, section 'Preparing Strings'. # - Strings which use unclear terms or require additional context to be # understood. # - Strings which make invalid assumptions about notation of date, time or # money. # - Pluralisation problems. # - Incorrect English spelling. # - Incorrect formatting. # It can be your email address, or a mailing list address where translators # can write to without being subscribed, or the URL of a web page through # which the translators can contact you. MSGID_BUGS_ADDRESS = plptools-developers@lists.sourceforge.net # This is the list of locale categories, beyond LC_MESSAGES, for which the # message catalogs shall be used. It is usually empty. EXTRA_LOCALE_CATEGORIES = # This tells whether the $(DOMAIN).pot file contains messages with an 'msgctxt' # context. Possible values are "yes" and "no". Set this to yes if the # package uses functions taking also a message context, like pgettext(), or # if in $(XGETTEXT_OPTIONS) you define keywords with a context argument. USE_MSGCTXT = no # These options get passed to msgmerge. # Useful options are in particular: # --previous to keep previous msgids of translated messages, # --quiet to reduce the verbosity. MSGMERGE_OPTIONS = # These options get passed to msginit. # If you want to disable line wrapping when writing PO files, add # --no-wrap to MSGMERGE_OPTIONS, XGETTEXT_OPTIONS, and # MSGINIT_OPTIONS. MSGINIT_OPTIONS = # This tells whether or not to regenerate a PO file when $(DOMAIN).pot # has changed. Possible values are "yes" and "no". Set this to no if # the POT file is checked in the repository and the version control # program ignores timestamps. PO_DEPENDS_ON_POT = yes # This tells whether or not to forcibly update $(DOMAIN).pot and # regenerate PO files on "make dist". Possible values are "yes" and # "no". Set this to no if the POT file and PO files are maintained # externally. DIST_DEPENDS_ON_UPDATE_PO = yes plptools-1.0.26/po/POTFILES.in000066400000000000000000000005251504470754400156630ustar00rootroot00000000000000lib/rfsv.cc lib/rpcs.cc lib/psitime.h lib/rpcsfactory.cc lib/plpdirent.cc lib/rfsvfactory.cc lib/siscomponentrecord.cpp lib/sisfile.cpp lib/sisfileheader.cpp lib/sisfilerecord.cpp lib/sislangrecord.cpp lib/sisreqrecord.cpp plpftp/main.cc plpftp/ftp.cc sisinstall/sismain.cpp ncpd/main.cc ncpd/link.cc plpfuse/main.cc plpprint/plpprintd.cc plptools-1.0.26/po/de.po000066400000000000000000001063201504470754400150360ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR Free Software Foundation, Inc. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: plptools 0.11\n" "Report-Msgid-Bugs-To: plptools-developers@lists.sourceforge.net\n" "POT-Creation-Date: 2025-05-30 21:15+0100\n" "PO-Revision-Date: 2014-07-06 00:20+0100\n" "Last-Translator: Fritz Elfert \n" "Language-Team: Deutsch \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: KBabel 0.9.6\n" #: lib/rfsv.cc:32 lib/rpcsfactory.cc:33 lib/rfsvfactory.cc:36 msgid "no error" msgstr "kein Fehler" #: lib/rfsv.cc:33 msgid "general" msgstr "allgemein" #: lib/rfsv.cc:34 msgid "bad argument" msgstr "fehlerhaftes Argument" #: lib/rfsv.cc:35 msgid "OS error" msgstr "BS Fehler" #: lib/rfsv.cc:36 msgid "not supported" msgstr "nicht unterstützt" #: lib/rfsv.cc:37 msgid "numeric underflow" msgstr "numerischer Unterlauf" #: lib/rfsv.cc:38 msgid "numeric overflow" msgstr "numerischer Überlauf" #: lib/rfsv.cc:39 msgid "numeric exception" msgstr "numerische Ausnahme" #: lib/rfsv.cc:40 msgid "in use" msgstr "in Verwendung" #: lib/rfsv.cc:41 msgid "out of memory" msgstr "kein Speicher" #: lib/rfsv.cc:42 msgid "out of segments" msgstr "keine Segmente" #: lib/rfsv.cc:43 msgid "out of semaphores" msgstr "keine Semaphoren" #: lib/rfsv.cc:44 msgid "out of processes" msgstr "keine Prozesse" #: lib/rfsv.cc:45 msgid "already open" msgstr "bereits geöffnet" #: lib/rfsv.cc:46 msgid "not open" msgstr "nicht geöffnet" #: lib/rfsv.cc:47 msgid "bad image" msgstr "fehlerhaftes Abbild" #: lib/rfsv.cc:48 msgid "receiver error" msgstr "Empfangsfehler" #: lib/rfsv.cc:49 msgid "device error" msgstr "Gerätefehler" #: lib/rfsv.cc:50 msgid "no filesystem" msgstr "kein Dateisystem" #: lib/rfsv.cc:51 msgid "not ready" msgstr "nicht bereit" #: lib/rfsv.cc:52 msgid "no font" msgstr "kein Zeichensatz" #: lib/rfsv.cc:53 msgid "too wide" msgstr "zu breit" #: lib/rfsv.cc:54 msgid "too many" msgstr "zu viele" #: lib/rfsv.cc:55 msgid "file already exists" msgstr "Datei existiert bereits" #: lib/rfsv.cc:56 msgid "no such file" msgstr "keine solche Datei" #: lib/rfsv.cc:57 msgid "write error" msgstr "Schreibfehler" #: lib/rfsv.cc:58 msgid "read error" msgstr "Lesefehler" #: lib/rfsv.cc:59 msgid "end of file" msgstr "Ende der Datei" #: lib/rfsv.cc:60 msgid "disk/serial read buffer full" msgstr "Platte/serieller Lesepuffer voll" #: lib/rfsv.cc:61 msgid "invalid name" msgstr "ungültiger Name" #: lib/rfsv.cc:62 msgid "access denied" msgstr "Zugriff verweigert" #: lib/rfsv.cc:63 msgid "resource locked" msgstr "Ressource gesperrt" #: lib/rfsv.cc:64 msgid "no such device" msgstr "Kein solches Gerät" #: lib/rfsv.cc:65 msgid "no such directory" msgstr "Kein solcher Ordner" #: lib/rfsv.cc:66 msgid "no such record" msgstr "Kein solcher Datensatz" #: lib/rfsv.cc:67 msgid "file is read-only" msgstr "Datei ist nur lesbar" #: lib/rfsv.cc:68 msgid "invalid I/O operation" msgstr "ungültige E/A-Operation" #: lib/rfsv.cc:69 msgid "I/O pending (not yet completed)" msgstr "E/A anstehend (noch nicht beendet)" #: lib/rfsv.cc:70 msgid "invalid volume name" msgstr "ungültiger Einheitenname" #: lib/rfsv.cc:71 msgid "cancelled" msgstr "abgebrochen" #: lib/rfsv.cc:72 msgid "no memory for control block" msgstr "kein Speicher für Kontrollblock" #: lib/rfsv.cc:73 msgid "unit disconnected" msgstr "Einheit nicht verbunden" #: lib/rfsv.cc:74 msgid "already connected" msgstr "bereits verbunden" #: lib/rfsv.cc:75 msgid "retransmission threshold exceeded" msgstr "Anzahl der Übertragungsversuche überschritten" #: lib/rfsv.cc:76 msgid "physical link failure" msgstr "physikalischer Verbindungsfehler" #: lib/rfsv.cc:77 msgid "inactivity timer expired" msgstr "Zeitlimit für Inaktivität abgelaufen" #: lib/rfsv.cc:78 msgid "serial parity error" msgstr "Serieller Paritätsfehler" #: lib/rfsv.cc:79 msgid "serial framing error" msgstr "Serieller Rahmenfehler" #: lib/rfsv.cc:80 msgid "serial overrun error" msgstr "Serieller Überlauf" #: lib/rfsv.cc:81 msgid "modem cannot connect to remote modem" msgstr "Modem kann nicht zu entferntem Modem verbinden" #: lib/rfsv.cc:82 msgid "remote modem busy" msgstr "Entferntes Modem besetzt" #: lib/rfsv.cc:83 msgid "remote modem did not answer" msgstr "Entferntes Modem antwortet nicht" #: lib/rfsv.cc:84 msgid "number blacklisted by the modem" msgstr "Rufnummer durch Modem gesperrt" #: lib/rfsv.cc:85 msgid "drive not ready" msgstr "Laufwerk nicht bereit" #: lib/rfsv.cc:86 msgid "unknown media" msgstr "Unbekanntes Medium" #: lib/rfsv.cc:87 msgid "directory full" msgstr "Ordner voll" #: lib/rfsv.cc:88 msgid "write-protected" msgstr "Schreibgeschützt" #: lib/rfsv.cc:89 msgid "media corrupt" msgstr "Medium beschädigt" #: lib/rfsv.cc:90 msgid "aborted operation" msgstr "abgebrochene Operation" #: lib/rfsv.cc:91 msgid "failed to erase flash media" msgstr "Löschen des Flash-Mediums gescheitert" #: lib/rfsv.cc:92 msgid "invalid file for DBF system" msgstr "Ungültige Datei für DBF-System" #: lib/rfsv.cc:93 msgid "power failure" msgstr "Stromversorgung ausgefallen" #: lib/rfsv.cc:94 msgid "too big" msgstr "zu groß" #: lib/rfsv.cc:95 msgid "bad descriptor" msgstr "fehlerhafter Deskriptor" #: lib/rfsv.cc:96 msgid "bad entry point" msgstr "fehlerhafter Einstiegspunkt" #: lib/rfsv.cc:97 msgid "could not diconnect" msgstr "Konnte Verbindung nicht beenden" #: lib/rfsv.cc:98 msgid "bad driver" msgstr "fehlerhafter Treiber" #: lib/rfsv.cc:99 msgid "operation not completed" msgstr "Operation nicht abgeschlossen" #: lib/rfsv.cc:100 msgid "server busy" msgstr "Server beschäftigt" #: lib/rfsv.cc:101 msgid "terminated" msgstr "beendet" #: lib/rfsv.cc:102 msgid "died" msgstr "abgestürzt" #: lib/rfsv.cc:103 msgid "bad handle" msgstr "fehlerhafte Referenz" #: lib/rfsv.cc:104 msgid "invalid operation for RFSV16" msgstr "Ungültige Operation für RFSV16" #: lib/rfsv.cc:105 msgid "libplp internal error" msgstr "Interner libplp Fehler" #: lib/rpcs.cc:36 msgid "Unknown device" msgstr "Unbekanntes Gerät" #: lib/rpcs.cc:37 msgid "PC" msgstr "PC" #: lib/rpcs.cc:38 msgid "MC" msgstr "MC" #: lib/rpcs.cc:39 msgid "HC" msgstr "HC" #: lib/rpcs.cc:40 msgid "Series 3" msgstr "Serie 3" #: lib/rpcs.cc:41 msgid "Series 3a, 3c or 3mx" msgstr "Serie 3a; 3c oder 3mx" #: lib/rpcs.cc:42 msgid "Workabout" msgstr "Workabout" #: lib/rpcs.cc:43 msgid "Sienna" msgstr "Sienna" #: lib/rpcs.cc:44 msgid "Series 3c" msgstr "Serie 3c" #: lib/rpcs.cc:45 msgid "Series 5" msgstr "Serie 5" #: lib/rpcs.cc:46 msgid "WinC" msgstr "WinC" #: lib/rpcs.cc:50 msgid "Empty" msgstr "Leer" #: lib/rpcs.cc:51 msgid "Very Low" msgstr "Sehr verbraucht" #: lib/rpcs.cc:52 msgid "Low" msgstr "Verbraucht" #: lib/rpcs.cc:53 msgid "Good" msgstr "Gut" #: lib/rpcs.cc:58 msgid "Test" msgstr "Test" #: lib/rpcs.cc:59 msgid "English" msgstr "Englisch" #: lib/rpcs.cc:60 msgid "German" msgstr "Deutsch" #: lib/rpcs.cc:61 msgid "French" msgstr "Französisch" #: lib/rpcs.cc:62 msgid "Spanish" msgstr "Spanisch" #: lib/rpcs.cc:63 msgid "Italian" msgstr "Italienisch" #: lib/rpcs.cc:64 msgid "Swedish" msgstr "Schwedisch" #: lib/rpcs.cc:65 msgid "Danish" msgstr "Dänisch" #: lib/rpcs.cc:66 msgid "Norwegian" msgstr "Norwegisch" #: lib/rpcs.cc:67 msgid "Finnish" msgstr "Finnisch" #: lib/rpcs.cc:68 msgid "American" msgstr "Amerikanisch" #: lib/rpcs.cc:69 msgid "Swiss French" msgstr "Schweizer Französisch" #: lib/rpcs.cc:70 msgid "Swiss German" msgstr "Schweizer Deutsch" #: lib/rpcs.cc:71 msgid "Portugese" msgstr "Portugiesisch" #: lib/rpcs.cc:72 msgid "Turkish" msgstr "Türkisch" #: lib/rpcs.cc:73 msgid "Icelandic" msgstr "Isländisch" #: lib/rpcs.cc:74 msgid "Russian" msgstr "Russisch" #: lib/rpcs.cc:75 msgid "Hungarian" msgstr "Ungarisch" #: lib/rpcs.cc:76 msgid "Dutch" msgstr "Holländisch" #: lib/rpcs.cc:77 msgid "Belgian Flemish" msgstr "Belgisch Flämisch" #: lib/rpcs.cc:78 msgid "Australian" msgstr "Australisch" #: lib/rpcs.cc:79 msgid "Belgish French" msgstr "Belgisch Französisch" #: lib/rpcs.cc:80 msgid "Austrian" msgstr "Österreichisch" #: lib/rpcs.cc:81 msgid "New Zealand English" msgstr "Neuseeländisch" # FIXME: not shure about ISO code #: lib/rpcs.cc:82 msgid "Canadian French" msgstr "Internationales Französisch" # FIXME: not shure about ISO code #: lib/rpcs.cc:83 msgid "Czech" msgstr "Tschechisch" #: lib/rpcs.cc:84 msgid "Slovak" msgstr "Slowakisch" #: lib/rpcs.cc:85 msgid "Polish" msgstr "Polnisch" #: lib/rpcs.cc:86 msgid "Slovenian" msgstr "Slowenisch" #: lib/psitime.h:61 msgid " years " msgstr " Jahre " #: lib/psitime.h:61 msgid " year " msgstr " Jahr " #: lib/psitime.h:63 msgid " days " msgstr " Tage " #: lib/psitime.h:63 msgid " day " msgstr " Tag " #: lib/psitime.h:65 msgid " hours " msgstr " Stunden " #: lib/psitime.h:65 msgid " hour " msgstr " Stunde " #: lib/psitime.h:67 msgid " minutes " msgstr " Minuten " #: lib/psitime.h:67 msgid " minute " msgstr " Minute " #: lib/psitime.h:68 plpftp/ftp.cc:1203 plpftp/ftp.cc:1205 plpftp/ftp.cc:1281 msgid " seconds" msgstr " Sekunden" #: lib/psitime.h:68 msgid " second" msgstr " Sekunde" #: lib/rpcsfactory.cc:34 lib/rfsvfactory.cc:37 msgid "could not send version request" msgstr "Konnte Versions-Anforderung nicht versenden" #: lib/rpcsfactory.cc:35 lib/rfsvfactory.cc:38 msgid "try again" msgstr "Erneut versuchen" #: lib/rpcsfactory.cc:36 lib/rfsvfactory.cc:39 msgid "no EPOC device connected" msgstr "Kein Psion angeschlossen" #: lib/rpcsfactory.cc:37 lib/rfsvfactory.cc:40 msgid "wrong protocol version" msgstr "Falsche Protokoll-Version" #: lib/rpcsfactory.cc:38 lib/rfsvfactory.cc:41 msgid "no response from ncpd" msgstr "Keine Antwort vom ncpd" #: lib/plpdirent.cc:174 msgid "Not present" msgstr "Nicht vorhanden" #: lib/plpdirent.cc:175 ncpd/link.cc:54 msgid "Unknown" msgstr "Unbekannt" #: lib/plpdirent.cc:176 msgid "Floppy" msgstr "Diskette" #: lib/plpdirent.cc:177 msgid "Disk" msgstr "Platte" #: lib/plpdirent.cc:178 msgid "CD-ROM" msgstr "CD-ROM" #: lib/plpdirent.cc:179 msgid "RAM" msgstr "RAM" #: lib/plpdirent.cc:180 msgid "Flash Disk" msgstr "Flash Disk" #: lib/plpdirent.cc:181 lib/plpdirent.cc:208 msgid "ROM" msgstr "ROM" #: lib/plpdirent.cc:182 msgid "Remote" msgstr "Netzlaufwerk" #: lib/plpdirent.cc:206 msgid "local" msgstr "Lokal" #: lib/plpdirent.cc:210 msgid "redirected" msgstr "Umgeleitet" #: lib/plpdirent.cc:212 msgid "substituted" msgstr "Ersetzt" #: lib/plpdirent.cc:214 msgid "internal" msgstr "Intern" #: lib/plpdirent.cc:216 msgid "removable" msgstr "Wechselmedium" #: lib/plpdirent.cc:229 msgid "variable size" msgstr "Variable Größe" #: lib/plpdirent.cc:231 msgid "dual density" msgstr "Doppelte Dichte" #: lib/plpdirent.cc:233 msgid "formattable" msgstr "Formatierbar" #: lib/plpdirent.cc:235 msgid "write protected" msgstr "Schreibgeschützt" #: lib/siscomponentrecord.cpp:67 #, c-format msgid "Length too large for name record %d.\n" msgstr "Länge %d zu groß für einen Komponenten-Eintrag.\n" #: lib/siscomponentrecord.cpp:82 #, c-format msgid "Position/length too large for name record %d.\n" msgstr "Position/Länge %d zu groß für einen Namens-Eintrag.\n" #: lib/siscomponentrecord.cpp:87 #, c-format msgid "Name %d (for %s) is %.*s\n" msgstr "Name %d (für %s) ist %.*s\n" #: lib/siscomponentrecord.cpp:98 #, c-format msgid "%d .. %d (%d bytes): Name records\n" msgstr "%d .. %d (%d Bytes): Namens-Einträge\n" #: lib/sisfile.cpp:56 #, c-format msgid "Could not read header, rc = %d\n" msgstr "Konnte Vorspann nicht lesen, rc = %d\n" #: lib/sisfile.cpp:60 #, c-format msgid "Ate header, got ix = %d\n" msgstr "Vorspann konsumiert, ergab ix = %d\n" #: lib/sisfile.cpp:75 #, c-format msgid "Problem reading language record %d, rc = %d.\n" msgstr "Problem beim Lesen des Sprach-Eintrags %d, rc = %d.\n" #: lib/sisfile.cpp:93 #, c-format msgid "Problem reading requisite record %d, rc = %d.\n" msgstr "Problem beim Lesen des Requisiten-Eintrags %d, rc = %d.\n" #: lib/sisfile.cpp:107 #, c-format msgid "Problem reading the name record, rc = %d.\n" msgstr "Problem beim Lesen des Namens-Eintrags, rc = %d.\n" #: lib/sisfile.cpp:124 #, c-format msgid "Problem reading file record %d, rc = %d.\n" msgstr "Problem beim Lesen des Datei-Eintrags %d, rc = %d.\n" #: lib/sisfileheader.cpp:55 #, c-format msgid "Got uid1 = %08x\n" msgstr "Uid1 (%08x) ermittelt\n" #: lib/sisfileheader.cpp:59 msgid "Got bad uid2.\n" msgstr "Ungültige Uid2 entdeckt.\n" #: lib/sisfileheader.cpp:63 #, c-format msgid "Got uid2 = %08x\n" msgstr "Uid2 (%08x) ermittelt\n" #: lib/sisfileheader.cpp:67 msgid "Got bad uid3.\n" msgstr "Ungültige Uid3 entdeckt.\n" #: lib/sisfileheader.cpp:71 #, c-format msgid "Got uid3 = %08x\n" msgstr "Uid3 (%08x) ermittelt\n" #: lib/sisfileheader.cpp:74 #, fuzzy, c-format msgid "Got uid4 = %08x\n" msgstr "Uid1 (%08x) ermittelt\n" #: lib/sisfileheader.cpp:82 #, c-format msgid "Got first crc = %08x, wanted %08x\n" msgstr "Erste CRC = %08x, erwartet wurde: %08x\n" #: lib/sisfileheader.cpp:86 msgid "Got bad crc.\n" msgstr "Ungültige CRC erhalten.\n" #: lib/sisfileheader.cpp:92 #, c-format msgid "Got %d languages\n" msgstr "%d Sprachen ermittelt.\n" #: lib/sisfileheader.cpp:95 #, c-format msgid "Got %d files\n" msgstr "%d Dateien ermittelt\n" #: lib/sisfileheader.cpp:98 #, c-format msgid "Got %d reqs\n" msgstr "%d Abhängigkeiten ermittelt\n" #: lib/sisfileheader.cpp:101 #, c-format msgid "Selected language is %d\n" msgstr "Gewählte Sprache ist %d\n" #: lib/sisfileheader.cpp:104 #, c-format msgid "Installed files: %d / %d\n" msgstr "Installierte Dateien: %d / %d\n" #: lib/sisfileheader.cpp:107 #, c-format msgid "Installed on drive %c\n" msgstr "Installiert auf Laufwerk %c\n" #: lib/sisfileheader.cpp:110 #, c-format msgid "Got installer version: %08x\n" msgstr "Installationsprogramm, Version %08x ermittelt\n" #: lib/sisfileheader.cpp:113 #, c-format msgid "Got options: %04x\n" msgstr "Optionen %04x ermittelt\n" #: lib/sisfileheader.cpp:116 #, c-format msgid "Got type: %04x\n" msgstr "Typ %04x ermittelt\n" #: lib/sisfileheader.cpp:119 #, c-format msgid "Got major: %d\n" msgstr "Hauptversion %d ermittelt\n" #: lib/sisfileheader.cpp:122 #, c-format msgid "Got minor: %d\n" msgstr "Unterversion %d ermittelt\n" #: lib/sisfileheader.cpp:125 #, c-format msgid "Got variant: %d\n" msgstr "Variante %d ermittelt\n" #: lib/sisfileheader.cpp:128 #, c-format msgid "Languages begin at %d\n" msgstr "Sprachen beginnen bei %d\n" #: lib/sisfileheader.cpp:133 #, c-format msgid "Files begin at %d\n" msgstr "Dateien beginnen bei %d\n" #: lib/sisfileheader.cpp:138 #, c-format msgid "Requisites begin at %d\n" msgstr "Abhängigkeiten beginnen bei %d\n" #: lib/sisfileheader.cpp:144 #, c-format msgid "Components begin at %d\n" msgstr "Komponenten beginnen bei %d\n" #: lib/sisfilerecord.cpp:40 #, c-format msgid "Got flags %d\n" msgstr "Flags %d ermittelt\n" #: lib/sisfilerecord.cpp:43 #, c-format msgid "Got file type %d\n" msgstr "Dateityp %d ermittelt\n" #: lib/sisfilerecord.cpp:46 #, c-format msgid "Got file details %d\n" msgstr "Dateidetails %d ermittelt\n" #: lib/sisfilerecord.cpp:53 #, c-format msgid "Got source name %.*s\n" msgstr "Quell-Dateiname %.*s ermittelt\n" #: lib/sisfilerecord.cpp:59 #, c-format msgid "Got destination name %.*s\n" msgstr "Ziel-Dateiname %.*s ermittelt\n" #: lib/sisfilerecord.cpp:70 #, c-format msgid "File is %d bytes long (at %d) (to %d)\n" msgstr "Datei ist %d Bytes groß (bei %d) (bis %d)\n" #: lib/sisfilerecord.cpp:74 #, c-format msgid "%d .. %d (%d bytes): Single file record type %d, %.*s\n" msgstr "%d .. %d (%d Bytes): Einzelner Dateieintragtyp %d, %.*s\n" #: lib/sisfilerecord.cpp:104 #, c-format msgid "File %d (for %s) is %d bytes long (at %d)\n" msgstr "Datei %d (für %s) ist %d Bytes groß (bei %d)\n" #: lib/sisfilerecord.cpp:110 #, c-format msgid "%d .. %d (%d bytes): File record (%s) for %.*s\n" msgstr "%d .. %d (%d Bytes): Dateieintrag (%s) für %.*s\n" #: lib/sisfilerecord.cpp:122 #, c-format msgid "Unknown file flags %d\n" msgstr "Unbekannte Datei-Flags: %d\n" #: lib/sislangrecord.cpp:35 #, c-format msgid "Got language %d (%s)\n" msgstr "Sprache %d (%s) ermittelt\n" #: lib/sislangrecord.cpp:37 #, c-format msgid "%d .. %d (%d bytes): Language record for %s\n" msgstr "%d .. %d (%d Bytes): Sprach-Eintrag für %s\n" #: lib/sisreqrecord.cpp:44 #, c-format msgid "Requisite: uid=%08x, version=%d.%d-%d.\n" msgstr "Requisite: uid=%08x, version=%d,%d-%d.\n" #: lib/sisreqrecord.cpp:54 #, c-format msgid "Got namelength %d\n" msgstr "Ermittelte Namenslänge %d\n" #: lib/sisreqrecord.cpp:64 #, c-format msgid "Got namepos %d\n" msgstr "Ermittelte Namens-Position %d\n" #: lib/sisreqrecord.cpp:67 #, c-format msgid "Position/length too large for req record %d.\n" msgstr "Position/Länge %d zu groß für einen Abhängigkeits-Eintrag.\n" #: lib/sisreqrecord.cpp:72 #, c-format msgid "Name of requisite for %s is %.*s\n" msgstr "Name der Prärequisite für %s ist %.*s\n" #: lib/sisreqrecord.cpp:78 #, c-format msgid "%d .. %d (%d bytes): Req record for uid %08x\n" msgstr "%d .. %d (%d Bytes): Req eintrag für UID %08x\n" #: plpftp/main.cc:52 msgid "" "Usage: plpftp [OPTIONS]... [FTPCOMMAND]\n" "\n" "If FTPCOMMAND is given, connect; run FTPCOMMAND and\n" "terminate afterwards. If no FTPCOMMAND is given, start up\n" "in interactive mode. For help on supported FTPCOMMANDs,\n" "use `?' or `help' as FTPCOMMAND.\n" "\n" "Supported options:\n" "\n" " -h, --help Display this text.\n" " -V, --version Print version and exit.\n" " -p, --port=[HOST:]PORT Connect to port PORT on host HOST.\n" " Default for HOST is 127.0.0.1\n" " Default for PORT is " msgstr "" "Anwendung: plpftp [OPTIONEN]... [FTPKOMMANDO]\n" "\n" "Wenn FTPKOMMANDO angegeben ist; führe es aus und beende\n" "hinterher. Wenn kein FTPKOMMANDO angegeben ist, starte im\n" "interaktiven Modus. Für Hilfe zu den unterstützten FTP KOMMANDOS,\n" "benutzen Sie `?' oder `help' als FTP KOMMANDO.\n" "\n" " -h, --help Zeige diesen Text.\n" " -V, --version Zeige Version und beende dann.\n" " -p, --port=[HOST:]PORT Verbinde mit port PORT auf Rechner HOST.\n" " Vorgabe für HOST ist 127.0.0.1\n" " Vorgabe für PORT ist " #: plpftp/main.cc:71 msgid "Try `plpftp --help' for more information" msgstr "Versuchen Sie `plpftp --help' für weitere Informationen." #: plpftp/main.cc:84 msgid "PLPFTP Version " msgstr "PLPFTP Version " #: plpftp/main.cc:85 #, fuzzy msgid " Copyright (C) 1999 Philip Proudman" msgstr " Copyright (C) 1999 Philip Proudman" #: plpftp/main.cc:86 msgid " Additions Copyright (C) 1999-2002 Fritz Elfert " msgstr " Erweiterungen Copyright (C) 1999-2002 Fritz Elfert " #: plpftp/main.cc:87 msgid " & (C) 1999 Matt Gumbley " msgstr " & (C) 1999 Matt Gumbley " #: plpftp/main.cc:88 msgid " & (C) 2006-2008 Reuben Thomas " msgstr "" #: plpftp/main.cc:89 msgid "PLPFTP comes with ABSOLUTELY NO WARRANTY." msgstr "PLPFTP kommt mit ABSOLUT KEINER GARANTIE" #: plpftp/main.cc:90 msgid "This is free software, and you are welcome to redistribute it" msgstr "Dies ist freie Software. Sie können Sie unter den Bedingungen" #: plpftp/main.cc:91 msgid "under GPL conditions; see the COPYING file in the distribution." msgstr "der GPL weitergeben. Siehe die Datei COPYING im Paket." #: plpftp/main.cc:93 msgid "FTP like interface started. Type \"?\" for help." msgstr "FTP Oberfläche gestartet. Geben Sie \"?\" ein, um Hilfe zu bekommen." #: plpftp/main.cc:156 msgid "plpftp Version " msgstr "plpftp Version " #: plpftp/main.cc:171 plpftp/main.cc:176 msgid "plpftp: could not connect to ncpd" msgstr "plpftp: Konnte ncpd nicht kontaktieren." #: plpftp/ftp.cc:106 msgid "Known FTP commands:" msgstr "Bekannte FTP Kommandos:" #: plpftp/ftp.cc:132 msgid "Known RPC commands:" msgstr "Bekannte RPC Kommandos:" #: plpftp/ftp.cc:194 plpftp/ftp.cc:271 #, fuzzy msgid "Could not open command list file " msgstr "Konnte SIS-Datei nicht parsen.\n" #: plpftp/ftp.cc:199 plpftp/ftp.cc:221 msgid "Could not get process list, Error: " msgstr "Konnte Prozessliste nicht lesen " #: plpftp/ftp.cc:215 msgid "" "Could not stop all processes. Please stop running\n" "programs manually on the Psion, then hit return." msgstr "" "Konnte nicht alle Prozesse stoppen. Bitte beenden Sie\n" "laufende Programme manuell auf dem Psion. Drücken Sie dann RETURN." #: plpftp/ftp.cc:277 plpftp/ftp.cc:320 plpftp/ftp.cc:767 plpftp/ftp.cc:775 #: plpftp/ftp.cc:784 plpftp/ftp.cc:793 plpftp/ftp.cc:801 plpftp/ftp.cc:811 #: plpftp/ftp.cc:823 plpftp/ftp.cc:873 plpftp/ftp.cc:880 plpftp/ftp.cc:908 #: plpftp/ftp.cc:915 plpftp/ftp.cc:944 plpftp/ftp.cc:966 plpftp/ftp.cc:994 #: plpftp/ftp.cc:1022 plpftp/ftp.cc:1047 plpftp/ftp.cc:1101 plpftp/ftp.cc:1125 #: plpftp/ftp.cc:1132 plpftp/ftp.cc:1139 plpftp/ftp.cc:1161 plpftp/ftp.cc:1169 #: plpftp/ftp.cc:1245 plpftp/ftp.cc:1255 plpftp/ftp.cc:1327 plpftp/ftp.cc:1353 #: plpftp/ftp.cc:1405 msgid "Error: " msgstr "Fehler: " #: plpftp/ftp.cc:278 msgid " is not a process list saved with killsave" msgstr " ist keine mit killsave gespeicherte Prozessliste" #: plpftp/ftp.cc:319 msgid "Could not start " msgstr "Konnte nicht starten: " #: plpftp/ftp.cc:334 msgid "Clipboard protocol not supported by Psion Series 3." msgstr "" #: plpftp/ftp.cc:342 msgid "" "Your Psion does not support the clipboard protocol.\n" " The reason for that is usually a missing server library.\n" " Make sure that C:\\System\\Libs\\clipsvr.rsy exists.\n" " This file is part of PsiWin and usually gets copied to\n" " your Psion when you enable CopyAnywhere in PsiWin.\n" " You can also get it from a PsiWin installation directory\n" " and copy it to your Psion manually." msgstr "" #: plpftp/ftp.cc:695 msgid "Connected to a " msgstr "Verbunden mit einem " #: plpftp/ftp.cc:695 msgid " at " msgstr " bei " #: plpftp/ftp.cc:696 msgid " baud, OwnerInfo:" msgstr " baud, Benutzerinfo:" #: plpftp/ftp.cc:702 msgid "OwnerInfo returned error " msgstr "OwnerInfo lieferte Fehler " #: plpftp/ftp.cc:722 msgid "FATAL: Couldn't find default Drive" msgstr "FATAL: Konnte Standard-Laufwerk nicht ermitteln" #: plpftp/ftp.cc:731 msgid "Psion dir is: \"" msgstr "Psion Ordner ist: \"" #: plpftp/ftp.cc:751 msgid "Prompting now " msgstr "Prompt ist nun " #: plpftp/ftp.cc:751 plpftp/ftp.cc:756 msgid "on" msgstr "an" #: plpftp/ftp.cc:751 plpftp/ftp.cc:756 plpftp/ftp.cc:1181 plpftp/ftp.cc:1184 #: plpftp/ftp.cc:1187 msgid "off" msgstr "aus" #: plpftp/ftp.cc:756 msgid "Hash printing now " msgstr "Fortschrittsanzeige ist nun " #: plpftp/ftp.cc:761 msgid "Local dir: \"" msgstr "Lokaler Ordner: \"" #: plpftp/ftp.cc:762 msgid "Psion dir: \"" msgstr "Psion Ordner: \"" #: plpftp/ftp.cc:882 msgid " Entries" msgstr " Einträge" #: plpftp/ftp.cc:888 msgid "Drive Type Volname Total Free UniqueID" msgstr "LW Typ Vol.Name Total Frei Eind.ID" #: plpftp/ftp.cc:931 msgid "No such directory" msgstr "Kein solcher Ordner" #: plpftp/ftp.cc:932 plpftp/ftp.cc:945 msgid "Keeping original directory \"" msgstr "Behalte ursprünglichen Ordner \"" #: plpftp/ftp.cc:983 plpftp/ftp.cc:1064 msgid "Transfer complete, (" msgstr "Übertragung abgeschlossen, (" #: plpftp/ftp.cc:984 plpftp/ftp.cc:1065 msgid " bytes in " msgstr " Bytes in " #: plpftp/ftp.cc:985 plpftp/ftp.cc:1066 msgid " secs = " msgstr " sek. = " #: plpftp/ftp.cc:1005 msgid "Get \"" msgstr "Hole \"" #: plpftp/ftp.cc:1027 plpftp/ftp.cc:1109 msgid "Transfer complete\n" msgstr "Übertragung abgeschlossen\n" #: plpftp/ftp.cc:1086 msgid "Put \"" msgstr "Sende \"" #: plpftp/ftp.cc:1118 msgid "Error in directory name \"" msgstr "Fehler in Ordner-Namen \"" #: plpftp/ftp.cc:1149 msgid "Starting subshell ...\n" msgstr "Starte Sub-Shell ...\n" #: plpftp/ftp.cc:1173 msgid "Unknown setup info received" msgstr "Unbekannte Einstellungen empfangen" #: plpftp/ftp.cc:1176 msgid "Setup information:" msgstr "Einstellungen:" #: plpftp/ftp.cc:1177 msgid " Screen contrast: " msgstr " Anzeige Kontrast: " #: plpftp/ftp.cc:1179 msgid " Keyboard click: " msgstr " Tastatur Klick: " #: plpftp/ftp.cc:1181 plpftp/ftp.cc:1184 plpftp/ftp.cc:1187 msgid "high" msgstr "laut" #: plpftp/ftp.cc:1181 plpftp/ftp.cc:1184 plpftp/ftp.cc:1187 msgid "low" msgstr "leise" #: plpftp/ftp.cc:1182 msgid " Screen click: " msgstr " Bildschirm Klick: " #: plpftp/ftp.cc:1185 msgid " Error sound: " msgstr " Fehlerton: " #: plpftp/ftp.cc:1188 msgid " Auto-switch off: " msgstr " Automatisches Ausschalten: " #: plpftp/ftp.cc:1191 plpftp/ftp.cc:1217 msgid "never" msgstr "nie" #: plpftp/ftp.cc:1194 msgid "if running on battery power" msgstr "bei Batteriebetrieb" #: plpftp/ftp.cc:1197 plpftp/ftp.cc:1223 msgid "always" msgstr "immer" #: plpftp/ftp.cc:1202 msgid " Switch off after: " msgstr " Ausschalten nach: " #: plpftp/ftp.cc:1204 msgid " Backlight off after: " msgstr " Hintergrundbeleuchtung ausschalten nach: " #: plpftp/ftp.cc:1206 msgid " Switch on when tapping on screen: " msgstr " Einschalten beim Tippen auf die Anzeige: " #: plpftp/ftp.cc:1207 plpftp/ftp.cc:1209 plpftp/ftp.cc:1211 plpftp/ftp.cc:1213 #: plpftp/ftp.cc:1269 plpftp/ftp.cc:1283 plpftp/ftp.cc:1301 msgid "yes" msgstr "ja" #: plpftp/ftp.cc:1207 plpftp/ftp.cc:1209 plpftp/ftp.cc:1211 plpftp/ftp.cc:1213 #: plpftp/ftp.cc:1269 plpftp/ftp.cc:1283 plpftp/ftp.cc:1301 msgid "no" msgstr "nein" #: plpftp/ftp.cc:1208 msgid " Switch on when opening: " msgstr " Einschalten beim Öffnen: " #: plpftp/ftp.cc:1210 msgid " Switch off when closing: " msgstr " Ausschalten beim Schließen: " #: plpftp/ftp.cc:1212 msgid " Ask for password on startup: " msgstr " Beim Einschalten nach Passwort fragen: " #: plpftp/ftp.cc:1214 msgid " Show Owner info on startup: " msgstr " Beim Einschalten Eigentümer anzeigen: " #: plpftp/ftp.cc:1220 msgid "once a day" msgstr "einmal täglich" #: plpftp/ftp.cc:1259 msgid "General:" msgstr "Allgemein:" #: plpftp/ftp.cc:1260 msgid " Machine Type: " msgstr " Geräte Typ: " #: plpftp/ftp.cc:1261 msgid " Machine Name: " msgstr " Modell Name: " #: plpftp/ftp.cc:1262 msgid " Machine UID: " msgstr " Geräte-ID: " #: plpftp/ftp.cc:1263 msgid " UI Language: " msgstr " UI-Sprache: " #: plpftp/ftp.cc:1264 msgid "ROM:" msgstr "ROM:" #: plpftp/ftp.cc:1265 msgid " Version: " msgstr " Version: " #: plpftp/ftp.cc:1267 plpftp/ftp.cc:1271 msgid " Size: " msgstr " Größe: " #: plpftp/ftp.cc:1268 msgid " Programmable: " msgstr " Programmierbar: " #: plpftp/ftp.cc:1270 msgid "RAM:" msgstr "RAM:" #: plpftp/ftp.cc:1272 msgid " Free: " msgstr " Frei: " #: plpftp/ftp.cc:1273 msgid " Free max: " msgstr " Max frei: " #: plpftp/ftp.cc:1274 msgid "RAM disk size: " msgstr "RAM Disk Größe: " #: plpftp/ftp.cc:1275 msgid "Registry size: " msgstr "Registry-Größe: " #: plpftp/ftp.cc:1276 msgid "Display size: " msgstr "Anzeige-Größe: " #: plpftp/ftp.cc:1278 msgid "Time:" msgstr "Zeit:" #: plpftp/ftp.cc:1280 msgid " Current time: " msgstr " Aktuelle Zeit: " #: plpftp/ftp.cc:1281 msgid " UTC offset: " msgstr " UTC-Abweichung: " #: plpftp/ftp.cc:1282 msgid " DST: " msgstr " Sommerzeit: " #: plpftp/ftp.cc:1284 msgid " Timezone: " msgstr " Zeitzone: " #: plpftp/ftp.cc:1285 msgid " Country Code: " msgstr " Länder-Code: " #: plpftp/ftp.cc:1286 msgid "Main battery:" msgstr "Hauptbatterie:" #: plpftp/ftp.cc:1288 msgid " Changed at: " msgstr " Gewechselt am: " #: plpftp/ftp.cc:1289 plpftp/ftp.cc:1302 msgid " Used for: " msgstr " Benutzt: " #: plpftp/ftp.cc:1290 plpftp/ftp.cc:1296 msgid " Status: " msgstr " Status: " #: plpftp/ftp.cc:1291 msgid " Current: " msgstr " Strom: " #: plpftp/ftp.cc:1292 msgid " UsedPower: " msgstr " Verbrauch: " #: plpftp/ftp.cc:1293 plpftp/ftp.cc:1297 msgid " Voltage: " msgstr " Spannung: " #: plpftp/ftp.cc:1294 plpftp/ftp.cc:1298 msgid " Max. voltage: " msgstr " Max. Spannung: " #: plpftp/ftp.cc:1295 msgid "Backup battery:" msgstr "Sicherungs-Batterie:" #: plpftp/ftp.cc:1299 msgid "External power:" msgstr "Externe Stromversorgung:" #: plpftp/ftp.cc:1300 msgid " Supplied: " msgstr " Angeschlossen: " #: plpftp/ftp.cc:1315 msgid "Error setting clipboard" msgstr "" #: plpftp/ftp.cc:1320 msgid "Error getting clipboard" msgstr "" #: plpftp/ftp.cc:1346 msgid "no such process" msgstr "Kein solcher Prozess" #: plpftp/ftp.cc:1362 msgid "syntax error. Try \"help\"" msgstr "Syntaxfehler. Probieren Sie \"help\"" #: sisinstall/sismain.cpp:45 #, c-format msgid "Error %d on line %d: %s\n" msgstr "Fehler %d in Zeile %d: %s\n" #: sisinstall/sismain.cpp:61 msgid "" "Usage: sisinstall [OPTIONS]... SISFILE\n" "\n" "Supported options:\n" "\n" " -h, --help Display this text.\n" " -V, --version Print version and exit.\n" " -v, --verbose=LEVEL Set the verbosity level, by default 0.\n" " -n, --dry-run Just parse the file.\n" msgstr "" "Anwendung: sisinstall [OPTIONEN]... SISDATEI\n" "\n" "Unterstütze Optionen:\n" "\n" " -h, --help Zeige diesen Text.\n" " -V, --version Version anzeigen und beenden.\n" " -v, --verbose=LEVEL Setze Log-stufe, Vorgabe ist 0.\n" " -n, --dry-run Nur parsen; keine Installation.\n" #: sisinstall/sismain.cpp:103 msgid "sisinstall version 0.1\n" msgstr "Installationsprogramm, Version 0.1\n" #: sisinstall/sismain.cpp:110 #, c-format msgid "Installing sis file %s%s.\n" msgstr "Installiere SIS-Datei %s%s.\n" #: sisinstall/sismain.cpp:111 msgid ", not really" msgstr ", nicht wirklich" #: sisinstall/sismain.cpp:115 msgid "Missing SIS filename\n" msgstr "Fehlender SIS Dateiname\n" #: sisinstall/sismain.cpp:123 #, fuzzy, c-format msgid "File is %jd bytes long\n" msgstr "Datei ist %d Bytes lang\n" #: sisinstall/sismain.cpp:138 msgid "Couldn't connect with the Psion\n" msgstr "Konnte nicht mit dem Psion verbinden\n" #: sisinstall/sismain.cpp:152 msgid "Could not parse the sis file.\n" msgstr "Konnte SIS-Datei nicht parsen.\n" #: ncpd/main.cc:79 msgid "Got SIGTERM" msgstr "TERM signal erhalten" #: ncpd/main.cc:87 msgid "Got SIGINT" msgstr "INT Signal erhalten" #: ncpd/main.cc:147 msgid "" "Usage: ncpd [OPTIONS]...\n" "\n" "Supported options:\n" "\n" " -d, --dontfork Run in foreground, don't fork\n" " -h, --help Display this text.\n" " -V, --version Print version and exit.\n" " -e, --autoexit Exit after device is disconnected.\n" " -v, --verbose=LOGCLASS Enable logging of LOGCLASS events\n" " Valid log classes are:\n" " m - main program\n" " nl - NCP protocol log\n" " nd - NCP protocol data dump\n" " ll - PLP protocol log\n" " ld - PLP protocol data dump\n" " pl - physical I/O log\n" " ph - physical I/O handshake\n" " pd - physical I/O data dump\n" " all - All of the above\n" " -s, --serial=DEV Use serial device DEV.\n" " -b, --baudrate=RATE Set serial speed to BAUD.\n" msgstr "" "Anwendung: ncpd [OPTIONEN]...\n" "\n" "Unterstützte Optionen:\n" "\n" " -d, --dontfork Starte im Vordergrund.\n" " -h, --help Zeige diesen Text.\n" " -V, --version Version ausgeben und beenden.\n" " -e, --autoexit Beenden, wenn Verbindung unterbrochen wurde.\n" " -v, --verbose=KLASSE Aktiviere protokoll für Ereignisse in KLASSE.\n" " Gültige Werte für KLASSE sind:\n" " m - Hauptprogramm\n" " nl - NCP Protokoll log\n" " nd - NCP Protokoll daten\n" " ll - PLP Protokoll log\n" " ld - PLP Protokoll daten\n" " pl - physikalische E/A log\n" " ph - physikalische E/A handshake\n" " pd - physikalische E/A daten\n" " all - Alles obige\n" " -s, --serial=DEV Benutze serielle Schnittstelle DEV.\n" " -b, --baudrate=RATE Setze Schnittstellengeschwindigkeit auf BAUD.\n" #: ncpd/main.cc:172 msgid " Default: " msgstr " Vorgabe: " #: ncpd/main.cc:174 msgid "" " Default: Autocycle 115.2k, 57.6k 38.4k, 19.2k\n" msgstr "" " Vorgabe: Automatisch 115.2k, 57.6k 38.4k, 19.2k\n" #: ncpd/main.cc:177 msgid "" " -p, --port=[HOST:]PORT Listen on host HOST, port PORT.\n" " Default for HOST: 127.0.0.1\n" " Default for PORT: " msgstr "" " -p, --port=[HOST:]PORT Warte auf Verbindung über host HOST, port PORT.\n" " Vorgabe für HOST: 127.0.0.1\n" " Vorgabe für PORT: " #: ncpd/main.cc:185 msgid "Try `ncpd --help' for more information" msgstr "Versuchen Sie `ncpd --help' für weitere Informationen." #: ncpd/main.cc:277 msgid "ncpd Version " msgstr "ncpd Version " #: ncpd/main.cc:358 msgid "daemon started. Listening at " msgstr "Daemon gestartet. Lausche auf " #: ncpd/main.cc:359 msgid " using device " msgstr " unter Verwendung von Gerät " #: ncpd/main.cc:391 msgid "terminating" msgstr "am Beenden" #: ncpd/main.cc:394 msgid "joined Link thread" msgstr "" #: ncpd/main.cc:396 msgid "joined Socket thread" msgstr "" #: ncpd/main.cc:398 msgid "shut down NCP" msgstr "" #: ncpd/main.cc:401 #, fuzzy msgid "socket closed" msgstr "Ressource gesperrt" #: ncpd/main.cc:409 msgid "normal exit" msgstr "" #: ncpd/link.cc:55 msgid "SIBO" msgstr "SIBO" #: ncpd/link.cc:56 msgid "EPOC" msgstr "EPOC" #: plpfuse/main.cc:338 #, fuzzy msgid "" "Usage: plpfuse [OPTION...] MOUNTPOINT\n" "\n" "plpfuse options:\n" "\n" " -d, --debug Increase debugging level\n" " -h, --help Display this text\n" " -V, --version Print version and exit\n" " -p, --port=[HOST:]PORT Connect to port PORT on host HOST\n" " Default for HOST is 127.0.0.1\n" " Default for PORT is " msgstr "" "Anwendung: plpfuse [OPTIONEN]...\n" "\n" "Unterstützte Optionen:\n" "\n" " -d, --debug Erhöhe Debug-Ebene\n" " -h, --help Zeige diesen Text.\n" " -V, --version Version ausgeben und beenden.\n" " -p, --port=[HOST:]PORT Verbinde mit port PORT auf host HOST.\n" " Vorgabe für HOST ist 127.0.0.1\n" " Vorgabe für PORT ist " #: plpfuse/main.cc:428 #, fuzzy msgid "plpfuse version " msgstr "plpfuse Version " #: plpfuse/main.cc:449 plpfuse/main.cc:454 msgid "plpfuse: could not connect to ncpd" msgstr "plpfuse: Konnte ncpd nicht kontaktieren." #: plpprint/plpprintd.cc:1005 msgid "plpprintd: could not connect to ncpd" msgstr "plpprintd: Konnte ncpd nicht kontaktieren." plptools-1.0.26/po/sv.po000066400000000000000000000773031504470754400151060ustar00rootroot00000000000000# Swedish texts for PLPTools. # Copyright (C) 2002 Free Software Foundation, Inc. # Daniel Brahneborg , 2002. # msgid "" msgstr "" "Project-Id-Version: plptools 0.11\n" "Report-Msgid-Bugs-To: plptools-developers@lists.sourceforge.net\n" "POT-Creation-Date: 2025-05-30 21:15+0100\n" "PO-Revision-Date: 2014-07-06 00:22+0100\n" "Last-Translator: Daniel Brahneborg \n" "Language-Team: SWEDISH \n" "Language: sv\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=ISO-8859-1\n" "Content-Transfer-Encoding: 8bit\n" #: lib/rfsv.cc:32 lib/rpcsfactory.cc:33 lib/rfsvfactory.cc:36 msgid "no error" msgstr "inget fel" #: lib/rfsv.cc:33 msgid "general" msgstr "allmän(t)" #: lib/rfsv.cc:34 msgid "bad argument" msgstr "felaktig parameter" #: lib/rfsv.cc:35 msgid "OS error" msgstr "OS fel" #: lib/rfsv.cc:36 msgid "not supported" msgstr "stöds ej" #: lib/rfsv.cc:37 msgid "numeric underflow" msgstr "för litet tal" #: lib/rfsv.cc:38 msgid "numeric overflow" msgstr "för stort tal" #: lib/rfsv.cc:39 msgid "numeric exception" msgstr "numeriskt fel" #: lib/rfsv.cc:40 msgid "in use" msgstr "används" #: lib/rfsv.cc:41 msgid "out of memory" msgstr "minnet slut" #: lib/rfsv.cc:42 msgid "out of segments" msgstr "slut på segment" #: lib/rfsv.cc:43 msgid "out of semaphores" msgstr "slut på semaforer" #: lib/rfsv.cc:44 msgid "out of processes" msgstr "slut på processer" #: lib/rfsv.cc:45 msgid "already open" msgstr "redan öppen" #: lib/rfsv.cc:46 msgid "not open" msgstr "inte öppen" #: lib/rfsv.cc:47 msgid "bad image" msgstr "trasig fil" #: lib/rfsv.cc:48 msgid "receiver error" msgstr "fel hos mottagaren" #: lib/rfsv.cc:49 msgid "device error" msgstr "hårdvarufel" #: lib/rfsv.cc:50 msgid "no filesystem" msgstr "inget filsystem" #: lib/rfsv.cc:51 msgid "not ready" msgstr "inte klar" #: lib/rfsv.cc:52 msgid "no font" msgstr "inget typsnitt" #: lib/rfsv.cc:53 msgid "too wide" msgstr "för bred" #: lib/rfsv.cc:54 msgid "too many" msgstr "för många" #: lib/rfsv.cc:55 msgid "file already exists" msgstr "filen finns redan" #: lib/rfsv.cc:56 msgid "no such file" msgstr "filen finns ej" #: lib/rfsv.cc:57 msgid "write error" msgstr "skrivfel" #: lib/rfsv.cc:58 msgid "read error" msgstr "läsfel" #: lib/rfsv.cc:59 msgid "end of file" msgstr "filen slut" #: lib/rfsv.cc:60 msgid "disk/serial read buffer full" msgstr "läsbuffer full" #: lib/rfsv.cc:61 msgid "invalid name" msgstr "ogiltigt namn" #: lib/rfsv.cc:62 msgid "access denied" msgstr "inget tillstånd" #: lib/rfsv.cc:63 msgid "resource locked" msgstr "resursen låst" #: lib/rfsv.cc:64 msgid "no such device" msgstr "enheten finns inte" #: lib/rfsv.cc:65 msgid "no such directory" msgstr "mappen finns inte" #: lib/rfsv.cc:66 msgid "no such record" msgstr "posten finns inte" #: lib/rfsv.cc:67 msgid "file is read-only" msgstr "filen är skrivskyddad" #: lib/rfsv.cc:68 msgid "invalid I/O operation" msgstr "ogiltig I/O-operation" #: lib/rfsv.cc:69 msgid "I/O pending (not yet completed)" msgstr "I/O pågår" #: lib/rfsv.cc:70 msgid "invalid volume name" msgstr "ogiltigt volymnamn" #: lib/rfsv.cc:71 msgid "cancelled" msgstr "avbruten" #: lib/rfsv.cc:72 msgid "no memory for control block" msgstr "inget minne till kontrollblocket" #: lib/rfsv.cc:73 msgid "unit disconnected" msgstr "enheten urkopplad" #: lib/rfsv.cc:74 msgid "already connected" msgstr "redan ikopplad" #: lib/rfsv.cc:75 msgid "retransmission threshold exceeded" msgstr "överföringsgräns nådd" #: lib/rfsv.cc:76 msgid "physical link failure" msgstr "fysiskt fel på länken" #: lib/rfsv.cc:77 msgid "inactivity timer expired" msgstr "för lång inaktivitet" #: lib/rfsv.cc:78 msgid "serial parity error" msgstr "paritetsfel" #: lib/rfsv.cc:79 msgid "serial framing error" msgstr "seriellt ramfel" #: lib/rfsv.cc:80 msgid "serial overrun error" msgstr "överfullt på serieporten" #: lib/rfsv.cc:81 msgid "modem cannot connect to remote modem" msgstr "modemet kunde inte få kontakt med det uppringda modemet" #: lib/rfsv.cc:82 msgid "remote modem busy" msgstr "det uppringa modemet är upptaget" #: lib/rfsv.cc:83 msgid "remote modem did not answer" msgstr "det uppringa modemet svarade inte" #: lib/rfsv.cc:84 msgid "number blacklisted by the modem" msgstr "numret svartlistat av modemet" #: lib/rfsv.cc:85 msgid "drive not ready" msgstr "enheten är inte klar" #: lib/rfsv.cc:86 msgid "unknown media" msgstr "okänt media" #: lib/rfsv.cc:87 msgid "directory full" msgstr "mappen är full" #: lib/rfsv.cc:88 msgid "write-protected" msgstr "skrivskyddad" #: lib/rfsv.cc:89 msgid "media corrupt" msgstr "media är trasigt" #: lib/rfsv.cc:90 msgid "aborted operation" msgstr "avbruten operation" #: lib/rfsv.cc:91 msgid "failed to erase flash media" msgstr "kunde inte radera flashkortet" #: lib/rfsv.cc:92 msgid "invalid file for DBF system" msgstr "ogiltig fil för DBF-systemet" #: lib/rfsv.cc:93 msgid "power failure" msgstr "spänningsfel" #: lib/rfsv.cc:94 msgid "too big" msgstr "för stor" #: lib/rfsv.cc:95 msgid "bad descriptor" msgstr "ogiltigt handtag" #: lib/rfsv.cc:96 msgid "bad entry point" msgstr "ogiltig ingångspunkt" #: lib/rfsv.cc:97 msgid "could not diconnect" msgstr "kunde inte koppla ifrån" #: lib/rfsv.cc:98 msgid "bad driver" msgstr "fel drivrutin" #: lib/rfsv.cc:99 msgid "operation not completed" msgstr "funktionen ej avslutad" #: lib/rfsv.cc:100 msgid "server busy" msgstr "servern är upptagen" #: lib/rfsv.cc:101 msgid "terminated" msgstr "avbruten" #: lib/rfsv.cc:102 msgid "died" msgstr "dog" #: lib/rfsv.cc:103 msgid "bad handle" msgstr "ogiltigt handtag" #: lib/rfsv.cc:104 msgid "invalid operation for RFSV16" msgstr "ogiltig funktion för RFSV16" #: lib/rfsv.cc:105 msgid "libplp internal error" msgstr "internt fel i libplp" #: lib/rpcs.cc:36 msgid "Unknown device" msgstr "Okänd enhet" #: lib/rpcs.cc:37 msgid "PC" msgstr "" #: lib/rpcs.cc:38 msgid "MC" msgstr "" #: lib/rpcs.cc:39 msgid "HC" msgstr "" #: lib/rpcs.cc:40 msgid "Series 3" msgstr "Serie 3" #: lib/rpcs.cc:41 msgid "Series 3a, 3c or 3mx" msgstr "Serie 3a, 3c eller 3mx" #: lib/rpcs.cc:42 msgid "Workabout" msgstr "" #: lib/rpcs.cc:43 msgid "Sienna" msgstr "" #: lib/rpcs.cc:44 msgid "Series 3c" msgstr "Serie 3c" #: lib/rpcs.cc:45 msgid "Series 5" msgstr "Serie 5" #: lib/rpcs.cc:46 msgid "WinC" msgstr "" #: lib/rpcs.cc:50 msgid "Empty" msgstr "Tom" #: lib/rpcs.cc:51 msgid "Very Low" msgstr "Mycket Låg" #: lib/rpcs.cc:52 msgid "Low" msgstr "Låg" #: lib/rpcs.cc:53 msgid "Good" msgstr "God" #: lib/rpcs.cc:58 msgid "Test" msgstr "" #: lib/rpcs.cc:59 msgid "English" msgstr "Engelska" #: lib/rpcs.cc:60 msgid "German" msgstr "Tyska" #: lib/rpcs.cc:61 msgid "French" msgstr "Franska" #: lib/rpcs.cc:62 msgid "Spanish" msgstr "Spanska" #: lib/rpcs.cc:63 msgid "Italian" msgstr "Italienska" #: lib/rpcs.cc:64 msgid "Swedish" msgstr "Svenska" #: lib/rpcs.cc:65 msgid "Danish" msgstr "Danska" #: lib/rpcs.cc:66 msgid "Norwegian" msgstr "Norska" #: lib/rpcs.cc:67 msgid "Finnish" msgstr "Finska" #: lib/rpcs.cc:68 msgid "American" msgstr "Amerikanska" #: lib/rpcs.cc:69 msgid "Swiss French" msgstr "Schweizisk Franska" #: lib/rpcs.cc:70 msgid "Swiss German" msgstr "Schweizisk Tyska" #: lib/rpcs.cc:71 msgid "Portugese" msgstr "Portugisiska" #: lib/rpcs.cc:72 msgid "Turkish" msgstr "Turkiska" #: lib/rpcs.cc:73 msgid "Icelandic" msgstr "Isländska" #: lib/rpcs.cc:74 msgid "Russian" msgstr "Ryska" #: lib/rpcs.cc:75 msgid "Hungarian" msgstr "Ungerska" #: lib/rpcs.cc:76 msgid "Dutch" msgstr "Holländska" #: lib/rpcs.cc:77 msgid "Belgian Flemish" msgstr "Belgisk Flamlndska" #: lib/rpcs.cc:78 msgid "Australian" msgstr "Australiensk Engelska" #: lib/rpcs.cc:79 msgid "Belgish French" msgstr "Belgisk Franska" #: lib/rpcs.cc:80 msgid "Austrian" msgstr "Österrikiska" #: lib/rpcs.cc:81 msgid "New Zealand English" msgstr "Nya Zeeländska" # FIXME: not shure about ISO code #: lib/rpcs.cc:82 msgid "Canadian French" msgstr "Internationell Franska" # FIXME: not shure about ISO code #: lib/rpcs.cc:83 msgid "Czech" msgstr "Tjeckoslovakiska" #: lib/rpcs.cc:84 msgid "Slovak" msgstr "Slovakiska" #: lib/rpcs.cc:85 msgid "Polish" msgstr "Polska" #: lib/rpcs.cc:86 msgid "Slovenian" msgstr "Slovenska" #: lib/psitime.h:61 msgid " years " msgstr " år " #: lib/psitime.h:61 msgid " year " msgstr " år " #: lib/psitime.h:63 msgid " days " msgstr " dagar " #: lib/psitime.h:63 msgid " day " msgstr " dag " #: lib/psitime.h:65 msgid " hours " msgstr " timmar " #: lib/psitime.h:65 msgid " hour " msgstr " timme " #: lib/psitime.h:67 msgid " minutes " msgstr " minuter " #: lib/psitime.h:67 msgid " minute " msgstr " minut " #: lib/psitime.h:68 plpftp/ftp.cc:1203 plpftp/ftp.cc:1205 plpftp/ftp.cc:1281 msgid " seconds" msgstr " sekunder" #: lib/psitime.h:68 msgid " second" msgstr " sekund" #: lib/rpcsfactory.cc:34 lib/rfsvfactory.cc:37 msgid "could not send version request" msgstr "kunde inte skicka versionsfråga" #: lib/rpcsfactory.cc:35 lib/rfsvfactory.cc:38 msgid "try again" msgstr "försök igen" #: lib/rpcsfactory.cc:36 lib/rfsvfactory.cc:39 msgid "no EPOC device connected" msgstr "ingen psion ikopplad" #: lib/rpcsfactory.cc:37 lib/rfsvfactory.cc:40 msgid "wrong protocol version" msgstr "fel protokollversion" #: lib/rpcsfactory.cc:38 lib/rfsvfactory.cc:41 msgid "no response from ncpd" msgstr "inget svar från ncpd" #: lib/plpdirent.cc:174 msgid "Not present" msgstr "Inte tillgänglig" #: lib/plpdirent.cc:175 ncpd/link.cc:54 msgid "Unknown" msgstr "Okänd" #: lib/plpdirent.cc:176 msgid "Floppy" msgstr "" #: lib/plpdirent.cc:177 msgid "Disk" msgstr "" #: lib/plpdirent.cc:178 msgid "CD-ROM" msgstr "" #: lib/plpdirent.cc:179 msgid "RAM" msgstr "" #: lib/plpdirent.cc:180 msgid "Flash Disk" msgstr "" #: lib/plpdirent.cc:181 lib/plpdirent.cc:208 msgid "ROM" msgstr "" #: lib/plpdirent.cc:182 msgid "Remote" msgstr "Extern" #: lib/plpdirent.cc:206 msgid "local" msgstr "lokal" #: lib/plpdirent.cc:210 msgid "redirected" msgstr "vidarekopplad" #: lib/plpdirent.cc:212 msgid "substituted" msgstr "ersatt" #: lib/plpdirent.cc:214 msgid "internal" msgstr "intern" #: lib/plpdirent.cc:216 msgid "removable" msgstr "uttagbar" #: lib/plpdirent.cc:229 msgid "variable size" msgstr "med variabel storlek" #: lib/plpdirent.cc:231 msgid "dual density" msgstr "dubbel densitet" #: lib/plpdirent.cc:233 msgid "formattable" msgstr "formatterbar" #: lib/plpdirent.cc:235 msgid "write protected" msgstr "skrivskyddad" #: lib/siscomponentrecord.cpp:67 #, c-format msgid "Length too large for name record %d.\n" msgstr "Längden för lång för namnpost nummer %d.\n" #: lib/siscomponentrecord.cpp:82 #, c-format msgid "Position/length too large for name record %d.\n" msgstr "Position/längd för stor för namnpost %d.\n" #: lib/siscomponentrecord.cpp:87 #, c-format msgid "Name %d (for %s) is %.*s\n" msgstr "Namn %d (för %s) r %.*s\n" #: lib/siscomponentrecord.cpp:98 #, c-format msgid "%d .. %d (%d bytes): Name records\n" msgstr "%d .. %d (%d bytes): Namnposter\n" #: lib/sisfile.cpp:56 #, c-format msgid "Could not read header, rc = %d\n" msgstr "Kunde inte läsa headern, rc = %d\n" #: lib/sisfile.cpp:60 #, c-format msgid "Ate header, got ix = %d\n" msgstr "" #: lib/sisfile.cpp:75 #, c-format msgid "Problem reading language record %d, rc = %d.\n" msgstr "Kunde inte läsa språkpost %d, rc = %d.\n" #: lib/sisfile.cpp:93 #, c-format msgid "Problem reading requisite record %d, rc = %d.\n" msgstr "Kunde inte läsa kravpost %d, rc = %d.\n" #: lib/sisfile.cpp:107 #, c-format msgid "Problem reading the name record, rc = %d.\n" msgstr "Kunde inte läsa namnpost, rc = %d.\n" #: lib/sisfile.cpp:124 #, c-format msgid "Problem reading file record %d, rc = %d.\n" msgstr "Kunde inte läsa filpost %d, rc = %d.\n" #: lib/sisfileheader.cpp:55 #, c-format msgid "Got uid1 = %08x\n" msgstr "" #: lib/sisfileheader.cpp:59 msgid "Got bad uid2.\n" msgstr "" #: lib/sisfileheader.cpp:63 #, c-format msgid "Got uid2 = %08x\n" msgstr "" #: lib/sisfileheader.cpp:67 msgid "Got bad uid3.\n" msgstr "" #: lib/sisfileheader.cpp:71 #, c-format msgid "Got uid3 = %08x\n" msgstr "" #: lib/sisfileheader.cpp:74 #, c-format msgid "Got uid4 = %08x\n" msgstr "" #: lib/sisfileheader.cpp:82 #, c-format msgid "Got first crc = %08x, wanted %08x\n" msgstr "" #: lib/sisfileheader.cpp:86 msgid "Got bad crc.\n" msgstr "" #: lib/sisfileheader.cpp:92 #, c-format msgid "Got %d languages\n" msgstr "Hittade %d språk\n" #: lib/sisfileheader.cpp:95 #, c-format msgid "Got %d files\n" msgstr "Hittade %d filer\n" #: lib/sisfileheader.cpp:98 #, c-format msgid "Got %d reqs\n" msgstr "Hittade %d krav\n" #: lib/sisfileheader.cpp:101 #, c-format msgid "Selected language is %d\n" msgstr "Valt språk är %d\n" #: lib/sisfileheader.cpp:104 #, c-format msgid "Installed files: %d / %d\n" msgstr "Installerade filer: %d / %d\n" #: lib/sisfileheader.cpp:107 #, c-format msgid "Installed on drive %c\n" msgstr "Installerad på enhet %c\n" #: lib/sisfileheader.cpp:110 #, c-format msgid "Got installer version: %08x\n" msgstr "" #: lib/sisfileheader.cpp:113 #, c-format msgid "Got options: %04x\n" msgstr "" #: lib/sisfileheader.cpp:116 #, c-format msgid "Got type: %04x\n" msgstr "" #: lib/sisfileheader.cpp:119 #, c-format msgid "Got major: %d\n" msgstr "" #: lib/sisfileheader.cpp:122 #, c-format msgid "Got minor: %d\n" msgstr "" #: lib/sisfileheader.cpp:125 #, c-format msgid "Got variant: %d\n" msgstr "" #: lib/sisfileheader.cpp:128 #, c-format msgid "Languages begin at %d\n" msgstr "" #: lib/sisfileheader.cpp:133 #, c-format msgid "Files begin at %d\n" msgstr "" #: lib/sisfileheader.cpp:138 #, c-format msgid "Requisites begin at %d\n" msgstr "" #: lib/sisfileheader.cpp:144 #, c-format msgid "Components begin at %d\n" msgstr "" #: lib/sisfilerecord.cpp:40 #, c-format msgid "Got flags %d\n" msgstr "" #: lib/sisfilerecord.cpp:43 #, c-format msgid "Got file type %d\n" msgstr "" #: lib/sisfilerecord.cpp:46 #, c-format msgid "Got file details %d\n" msgstr "" #: lib/sisfilerecord.cpp:53 #, c-format msgid "Got source name %.*s\n" msgstr "Hittade källnamn %.*s\n" #: lib/sisfilerecord.cpp:59 #, c-format msgid "Got destination name %.*s\n" msgstr "Hittade destinationsnamn %.*s\n" #: lib/sisfilerecord.cpp:70 #, c-format msgid "File is %d bytes long (at %d) (to %d)\n" msgstr "" #: lib/sisfilerecord.cpp:74 #, c-format msgid "%d .. %d (%d bytes): Single file record type %d, %.*s\n" msgstr "%d .. %d (%d bytes): Enstaka filpost typ %d, %.*s\n" #: lib/sisfilerecord.cpp:104 #, c-format msgid "File %d (for %s) is %d bytes long (at %d)\n" msgstr "" #: lib/sisfilerecord.cpp:110 #, c-format msgid "%d .. %d (%d bytes): File record (%s) for %.*s\n" msgstr "%d .. %d (%d bytes): Filpost (%s) för %.*s\n" #: lib/sisfilerecord.cpp:122 #, c-format msgid "Unknown file flags %d\n" msgstr "" #: lib/sislangrecord.cpp:35 #, c-format msgid "Got language %d (%s)\n" msgstr "Hittade språk %d (%s)\n" #: lib/sislangrecord.cpp:37 #, c-format msgid "%d .. %d (%d bytes): Language record for %s\n" msgstr "%d .. %d (%d bytes): Språkpost för %s\n" #: lib/sisreqrecord.cpp:44 #, c-format msgid "Requisite: uid=%08x, version=%d.%d-%d.\n" msgstr "" #: lib/sisreqrecord.cpp:54 #, c-format msgid "Got namelength %d\n" msgstr "" #: lib/sisreqrecord.cpp:64 #, c-format msgid "Got namepos %d\n" msgstr "Hittade namnposition %d\n" #: lib/sisreqrecord.cpp:67 #, c-format msgid "Position/length too large for req record %d.\n" msgstr "Position/längd för stor för kravpost %d.\n" #: lib/sisreqrecord.cpp:72 #, c-format msgid "Name of requisite for %s is %.*s\n" msgstr "Namn på krav för %s är %.*s\n" #: lib/sisreqrecord.cpp:78 #, c-format msgid "%d .. %d (%d bytes): Req record for uid %08x\n" msgstr "%d .. %d (%d bytes): Kravpost för uid %08x\n" #: plpftp/main.cc:52 msgid "" "Usage: plpftp [OPTIONS]... [FTPCOMMAND]\n" "\n" "If FTPCOMMAND is given, connect; run FTPCOMMAND and\n" "terminate afterwards. If no FTPCOMMAND is given, start up\n" "in interactive mode. For help on supported FTPCOMMANDs,\n" "use `?' or `help' as FTPCOMMAND.\n" "\n" "Supported options:\n" "\n" " -h, --help Display this text.\n" " -V, --version Print version and exit.\n" " -p, --port=[HOST:]PORT Connect to port PORT on host HOST.\n" " Default for HOST is 127.0.0.1\n" " Default for PORT is " msgstr "" "Användning: plpftp [OPTIONER]... [FTPKOMMANDO]\n" "\n" "Om FTPKOMMANDO är angivet, koppla upp; kör FTPKOMMANDO och\n" "avsluta efteråt. Om inget FTPKOMMANDO är angivet, starta\n" "i interaktivt läge. För mer information om tillgängliga\n" "FTPKOMMANDOn, använd `?' eller `help' som FTPKOMMANDO.\n" "\n" "Tillgängliga optioner:\n" "\n" " -h, --help Visa den här texten.\n" " -V, --version Visa versionsnummer och avsluta.\n" " -p, --port=[VRD:]PORT Ta kontakt med port PORT på maskin VÄRD.\n" " Om inget är angivet är VÄRD 127.0.0.1\n" " och PORT är " #: plpftp/main.cc:71 msgid "Try `plpftp --help' for more information" msgstr "Prova 'plpftp --help' för mer information" #: plpftp/main.cc:84 msgid "PLPFTP Version " msgstr "" #: plpftp/main.cc:85 msgid " Copyright (C) 1999 Philip Proudman" msgstr "" #: plpftp/main.cc:86 msgid " Additions Copyright (C) 1999-2002 Fritz Elfert " msgstr " Tillägg Copyright (C) 1999-2002 Fritz Elfert " #: plpftp/main.cc:87 msgid " & (C) 1999 Matt Gumbley " msgstr "" #: plpftp/main.cc:88 msgid " & (C) 2006-2008 Reuben Thomas " msgstr "" #: plpftp/main.cc:89 msgid "PLPFTP comes with ABSOLUTELY NO WARRANTY." msgstr "PLPFTP har INGEN SOM HELST GARANTI." #: plpftp/main.cc:90 msgid "This is free software, and you are welcome to redistribute it" msgstr "Det här är fri mjukvara, och du får gärna distribuera den" #: plpftp/main.cc:91 msgid "under GPL conditions; see the COPYING file in the distribution." msgstr "enligt villkoren i GPL; se filen COPYING i distributionen." #: plpftp/main.cc:93 msgid "FTP like interface started. Type \"?\" for help." msgstr "FTP-liknande interface startat. Skriv \"?\" för hjälp." #: plpftp/main.cc:156 msgid "plpftp Version " msgstr "" #: plpftp/main.cc:171 plpftp/main.cc:176 msgid "plpftp: could not connect to ncpd" msgstr "plpftp: kunde inte få kontakt med ncpd" #: plpftp/ftp.cc:106 msgid "Known FTP commands:" msgstr "FTP-kommandon:" #: plpftp/ftp.cc:132 msgid "Known RPC commands:" msgstr "RPC-kommandon:" #: plpftp/ftp.cc:194 plpftp/ftp.cc:271 #, fuzzy msgid "Could not open command list file " msgstr "Kunde inte parsa sisfilen.\n" #: plpftp/ftp.cc:199 plpftp/ftp.cc:221 msgid "Could not get process list, Error: " msgstr "Kunde inte läsa processlistan " #: plpftp/ftp.cc:215 msgid "" "Could not stop all processes. Please stop running\n" "programs manually on the Psion, then hit return." msgstr "" "Kunde inte avsluta alla processer. Var vänlig avsluta\n" "programmen manuellt på Psionen, tryck sedan på return." #: plpftp/ftp.cc:277 plpftp/ftp.cc:320 plpftp/ftp.cc:767 plpftp/ftp.cc:775 #: plpftp/ftp.cc:784 plpftp/ftp.cc:793 plpftp/ftp.cc:801 plpftp/ftp.cc:811 #: plpftp/ftp.cc:823 plpftp/ftp.cc:873 plpftp/ftp.cc:880 plpftp/ftp.cc:908 #: plpftp/ftp.cc:915 plpftp/ftp.cc:944 plpftp/ftp.cc:966 plpftp/ftp.cc:994 #: plpftp/ftp.cc:1022 plpftp/ftp.cc:1047 plpftp/ftp.cc:1101 plpftp/ftp.cc:1125 #: plpftp/ftp.cc:1132 plpftp/ftp.cc:1139 plpftp/ftp.cc:1161 plpftp/ftp.cc:1169 #: plpftp/ftp.cc:1245 plpftp/ftp.cc:1255 plpftp/ftp.cc:1327 plpftp/ftp.cc:1353 #: plpftp/ftp.cc:1405 msgid "Error: " msgstr "Fel: " #: plpftp/ftp.cc:278 msgid " is not a process list saved with killsave" msgstr " är inte en processlista som killsave har skapat" #: plpftp/ftp.cc:319 msgid "Could not start " msgstr "Kunde inte starta " #: plpftp/ftp.cc:334 msgid "Clipboard protocol not supported by Psion Series 3." msgstr "" #: plpftp/ftp.cc:342 msgid "" "Your Psion does not support the clipboard protocol.\n" " The reason for that is usually a missing server library.\n" " Make sure that C:\\System\\Libs\\clipsvr.rsy exists.\n" " This file is part of PsiWin and usually gets copied to\n" " your Psion when you enable CopyAnywhere in PsiWin.\n" " You can also get it from a PsiWin installation directory\n" " and copy it to your Psion manually." msgstr "" #: plpftp/ftp.cc:695 msgid "Connected to a " msgstr "Kopplad till en " #: plpftp/ftp.cc:695 msgid " at " msgstr " dag " #: plpftp/ftp.cc:696 msgid " baud, OwnerInfo:" msgstr " baud, ÄgarInformation:" #: plpftp/ftp.cc:702 msgid "OwnerInfo returned error " msgstr "ÄgarInformation returnerade fel " #: plpftp/ftp.cc:722 msgid "FATAL: Couldn't find default Drive" msgstr "FATALT: Kunde inte hitta någon enhet" #: plpftp/ftp.cc:731 msgid "Psion dir is: \"" msgstr "Psion-mappen är: \"" #: plpftp/ftp.cc:751 msgid "Prompting now " msgstr "Promptning är nu " #: plpftp/ftp.cc:751 plpftp/ftp.cc:756 msgid "on" msgstr "på" #: plpftp/ftp.cc:751 plpftp/ftp.cc:756 plpftp/ftp.cc:1181 plpftp/ftp.cc:1184 #: plpftp/ftp.cc:1187 msgid "off" msgstr "av" #: plpftp/ftp.cc:756 msgid "Hash printing now " msgstr "Brädgårdsmarkering är nu " #: plpftp/ftp.cc:761 msgid "Local dir: \"" msgstr "Lokal mapp: \"" #: plpftp/ftp.cc:762 msgid "Psion dir: \"" msgstr "Psion mapp: \"" #: plpftp/ftp.cc:882 msgid " Entries" msgstr " Element" #: plpftp/ftp.cc:888 msgid "Drive Type Volname Total Free UniqueID" msgstr "Enhet Typ Volymnamn Totalt Ledigt UniktID" #: plpftp/ftp.cc:931 msgid "No such directory" msgstr "Mappen finns inte" #: plpftp/ftp.cc:932 plpftp/ftp.cc:945 msgid "Keeping original directory \"" msgstr "Behåller ursprungsmappen \"" #: plpftp/ftp.cc:983 plpftp/ftp.cc:1064 msgid "Transfer complete, (" msgstr "Överfringen klar, (" #: plpftp/ftp.cc:984 plpftp/ftp.cc:1065 msgid " bytes in " msgstr " bytes på " #: plpftp/ftp.cc:985 plpftp/ftp.cc:1066 msgid " secs = " msgstr " sekunder = " #: plpftp/ftp.cc:1005 msgid "Get \"" msgstr "Hämta \"" #: plpftp/ftp.cc:1027 plpftp/ftp.cc:1109 msgid "Transfer complete\n" msgstr "Överfringen klar\n" #: plpftp/ftp.cc:1086 msgid "Put \"" msgstr "Skicka \"" #: plpftp/ftp.cc:1118 msgid "Error in directory name \"" msgstr "Fel i mappnamn \"" #: plpftp/ftp.cc:1149 msgid "Starting subshell ...\n" msgstr "Startar kommandotolk ...\n" #: plpftp/ftp.cc:1173 msgid "Unknown setup info received" msgstr "" #: plpftp/ftp.cc:1176 msgid "Setup information:" msgstr "" #: plpftp/ftp.cc:1177 msgid " Screen contrast: " msgstr " Skärmkontrast: " #: plpftp/ftp.cc:1179 msgid " Keyboard click: " msgstr " Tangentbordsklick: " #: plpftp/ftp.cc:1181 plpftp/ftp.cc:1184 plpftp/ftp.cc:1187 msgid "high" msgstr "hög" #: plpftp/ftp.cc:1181 plpftp/ftp.cc:1184 plpftp/ftp.cc:1187 msgid "low" msgstr "låg" #: plpftp/ftp.cc:1182 msgid " Screen click: " msgstr " Skärmklick: " #: plpftp/ftp.cc:1185 msgid " Error sound: " msgstr " Felljud: " #: plpftp/ftp.cc:1188 msgid " Auto-switch off: " msgstr " Automatisk avstängning: " #: plpftp/ftp.cc:1191 plpftp/ftp.cc:1217 msgid "never" msgstr "aldrig" #: plpftp/ftp.cc:1194 msgid "if running on battery power" msgstr "om maskinen går på batteri" #: plpftp/ftp.cc:1197 plpftp/ftp.cc:1223 msgid "always" msgstr "alltid" #: plpftp/ftp.cc:1202 msgid " Switch off after: " msgstr " Stäng av efter: " #: plpftp/ftp.cc:1204 msgid " Backlight off after: " msgstr " Släck bakgrundsbelysningen efter: " #: plpftp/ftp.cc:1206 msgid " Switch on when tapping on screen: " msgstr " Starta när man klickar på skärmen:" #: plpftp/ftp.cc:1207 plpftp/ftp.cc:1209 plpftp/ftp.cc:1211 plpftp/ftp.cc:1213 #: plpftp/ftp.cc:1269 plpftp/ftp.cc:1283 plpftp/ftp.cc:1301 msgid "yes" msgstr "ja" #: plpftp/ftp.cc:1207 plpftp/ftp.cc:1209 plpftp/ftp.cc:1211 plpftp/ftp.cc:1213 #: plpftp/ftp.cc:1269 plpftp/ftp.cc:1283 plpftp/ftp.cc:1301 msgid "no" msgstr "nej" #: plpftp/ftp.cc:1208 msgid " Switch on when opening: " msgstr " Sätt på när maskinen öppnas: " #: plpftp/ftp.cc:1210 msgid " Switch off when closing: " msgstr " Stäng av när maskinen stängs: " #: plpftp/ftp.cc:1212 msgid " Ask for password on startup: " msgstr " Fråga efter lösenord vid start: " #: plpftp/ftp.cc:1214 msgid " Show Owner info on startup: " msgstr " Visa ägarinformation vid start: " #: plpftp/ftp.cc:1220 msgid "once a day" msgstr "en gång om dagen" #: plpftp/ftp.cc:1259 msgid "General:" msgstr "Allmänt:" #: plpftp/ftp.cc:1260 msgid " Machine Type: " msgstr " Maskintyp: " #: plpftp/ftp.cc:1261 msgid " Machine Name: " msgstr " Maskinnamn: " #: plpftp/ftp.cc:1262 msgid " Machine UID: " msgstr " Maskin UID: " #: plpftp/ftp.cc:1263 msgid " UI Language: " msgstr " Användarspråk:" #: plpftp/ftp.cc:1264 msgid "ROM:" msgstr "" #: plpftp/ftp.cc:1265 msgid " Version: " msgstr "" #: plpftp/ftp.cc:1267 plpftp/ftp.cc:1271 msgid " Size: " msgstr " Storlek: " #: plpftp/ftp.cc:1268 msgid " Programmable: " msgstr " Programmerbar:" #: plpftp/ftp.cc:1270 msgid "RAM:" msgstr "" #: plpftp/ftp.cc:1272 msgid " Free: " msgstr " Ledigt: " #: plpftp/ftp.cc:1273 msgid " Free max: " msgstr " Max ledigt: " #: plpftp/ftp.cc:1274 msgid "RAM disk size: " msgstr "RAM-disk: " #: plpftp/ftp.cc:1275 msgid "Registry size: " msgstr "Inställningar: " #: plpftp/ftp.cc:1276 msgid "Display size: " msgstr "Bildskärm: " #: plpftp/ftp.cc:1278 msgid "Time:" msgstr "Tid: " #: plpftp/ftp.cc:1280 msgid " Current time: " msgstr " Aktuell tid: " #: plpftp/ftp.cc:1281 msgid " UTC offset: " msgstr "" #: plpftp/ftp.cc:1282 msgid " DST: " msgstr " Sommartid: " #: plpftp/ftp.cc:1284 msgid " Timezone: " msgstr " Tidszon: " #: plpftp/ftp.cc:1285 msgid " Country Code: " msgstr " Landskod: " #: plpftp/ftp.cc:1286 msgid "Main battery:" msgstr "Huvudbatteri:" #: plpftp/ftp.cc:1288 msgid " Changed at: " msgstr " Bytt: " #: plpftp/ftp.cc:1289 plpftp/ftp.cc:1302 msgid " Used for: " msgstr " Använt i: " #: plpftp/ftp.cc:1290 plpftp/ftp.cc:1296 msgid " Status: " msgstr " Tillstånd: " #: plpftp/ftp.cc:1291 msgid " Current: " msgstr " Aktuell åtgång:" #: plpftp/ftp.cc:1292 msgid " UsedPower: " msgstr " Använt: " #: plpftp/ftp.cc:1293 plpftp/ftp.cc:1297 msgid " Voltage: " msgstr " Spänning: " #: plpftp/ftp.cc:1294 plpftp/ftp.cc:1298 msgid " Max. voltage: " msgstr " Max spänning: " #: plpftp/ftp.cc:1295 msgid "Backup battery:" msgstr "Backupbatteri: " #: plpftp/ftp.cc:1299 msgid "External power:" msgstr "Extern ström: " #: plpftp/ftp.cc:1300 msgid " Supplied: " msgstr " Ikopplad: " #: plpftp/ftp.cc:1315 msgid "Error setting clipboard" msgstr "" #: plpftp/ftp.cc:1320 msgid "Error getting clipboard" msgstr "" #: plpftp/ftp.cc:1346 msgid "no such process" msgstr "ingen sådan process" #: plpftp/ftp.cc:1362 msgid "syntax error. Try \"help\"" msgstr "syntax error. Prova \"help\"" #: sisinstall/sismain.cpp:45 #, c-format msgid "Error %d on line %d: %s\n" msgstr "Fel %d på rad %d: %s\n" #: sisinstall/sismain.cpp:61 msgid "" "Usage: sisinstall [OPTIONS]... SISFILE\n" "\n" "Supported options:\n" "\n" " -h, --help Display this text.\n" " -V, --version Print version and exit.\n" " -v, --verbose=LEVEL Set the verbosity level, by default 0.\n" " -n, --dry-run Just parse the file.\n" msgstr "" "Användning: sisinstall [OPTIONS]... SISFILE\n" "\n" "Optioner:\n" "\n" " -h, --help Visa den här texten.\n" " -V, --version Skriv versionsnummer och avsluta.\n" " -v, --verbose=NIVÅ Sätt utskriftsnivån, 0 om inget annat anges.\n" " -n, --dry-run Läs bara igenom filen.\n" #: sisinstall/sismain.cpp:103 msgid "sisinstall version 0.1\n" msgstr "" #: sisinstall/sismain.cpp:110 #, c-format msgid "Installing sis file %s%s.\n" msgstr "Installerar sisfil %s%s.\n" #: sisinstall/sismain.cpp:111 msgid ", not really" msgstr ", inte egentligen" #: sisinstall/sismain.cpp:115 msgid "Missing SIS filename\n" msgstr "Saknar SIS-filnamn\n" #: sisinstall/sismain.cpp:123 #, fuzzy, c-format msgid "File is %jd bytes long\n" msgstr "Filen är %d bytes lång\n" #: sisinstall/sismain.cpp:138 msgid "Couldn't connect with the Psion\n" msgstr "Kunde inte få kontakt med Psionen\n" #: sisinstall/sismain.cpp:152 msgid "Could not parse the sis file.\n" msgstr "Kunde inte parsa sisfilen.\n" #: ncpd/main.cc:79 msgid "Got SIGTERM" msgstr "" #: ncpd/main.cc:87 msgid "Got SIGINT" msgstr "" #: ncpd/main.cc:147 msgid "" "Usage: ncpd [OPTIONS]...\n" "\n" "Supported options:\n" "\n" " -d, --dontfork Run in foreground, don't fork\n" " -h, --help Display this text.\n" " -V, --version Print version and exit.\n" " -e, --autoexit Exit after device is disconnected.\n" " -v, --verbose=LOGCLASS Enable logging of LOGCLASS events\n" " Valid log classes are:\n" " m - main program\n" " nl - NCP protocol log\n" " nd - NCP protocol data dump\n" " ll - PLP protocol log\n" " ld - PLP protocol data dump\n" " pl - physical I/O log\n" " ph - physical I/O handshake\n" " pd - physical I/O data dump\n" " all - All of the above\n" " -s, --serial=DEV Use serial device DEV.\n" " -b, --baudrate=RATE Set serial speed to BAUD.\n" msgstr "" #: ncpd/main.cc:172 msgid " Default: " msgstr "" #: ncpd/main.cc:174 msgid "" " Default: Autocycle 115.2k, 57.6k 38.4k, 19.2k\n" msgstr "" #: ncpd/main.cc:177 msgid "" " -p, --port=[HOST:]PORT Listen on host HOST, port PORT.\n" " Default for HOST: 127.0.0.1\n" " Default for PORT: " msgstr "" #: ncpd/main.cc:185 msgid "Try `ncpd --help' for more information" msgstr "Prova `ncpd --help' för mer information" #: ncpd/main.cc:277 msgid "ncpd Version " msgstr "" #: ncpd/main.cc:358 msgid "daemon started. Listening at " msgstr "daemon startad. Lyssnar på " #: ncpd/main.cc:359 msgid " using device " msgstr " använder enhet " #: ncpd/main.cc:391 msgid "terminating" msgstr "avbruten" #: ncpd/main.cc:394 msgid "joined Link thread" msgstr "" #: ncpd/main.cc:396 msgid "joined Socket thread" msgstr "" #: ncpd/main.cc:398 msgid "shut down NCP" msgstr "" #: ncpd/main.cc:401 #, fuzzy msgid "socket closed" msgstr "resursen låst" #: ncpd/main.cc:409 msgid "normal exit" msgstr "" #: ncpd/link.cc:55 msgid "SIBO" msgstr "" #: ncpd/link.cc:56 msgid "EPOC" msgstr "" #: plpfuse/main.cc:338 msgid "" "Usage: plpfuse [OPTION...] MOUNTPOINT\n" "\n" "plpfuse options:\n" "\n" " -d, --debug Increase debugging level\n" " -h, --help Display this text\n" " -V, --version Print version and exit\n" " -p, --port=[HOST:]PORT Connect to port PORT on host HOST\n" " Default for HOST is 127.0.0.1\n" " Default for PORT is " msgstr "" "Användning: plpfuse [OPTIONER]...\n" "\n" "Tillgängliga optioner:\n" "\n" " -d, --debug Increase debugging level\n" " -h, --help Visa den här texten.\n" " -V, --version Visa versionsnummer och avsluta.\n" " -p, --port=[VRD:]PORT Ta kontakt med port PORT på maskin VÄRD.\n" " Om inget är angivet är VÄRD 127.0.0.1\n" " och PORT är " #: plpfuse/main.cc:428 msgid "plpfuse version " msgstr "" #: plpfuse/main.cc:449 plpfuse/main.cc:454 msgid "plpfuse: could not connect to ncpd" msgstr "plpfuse: kunde inte få kontakt med ncpd" #: plpprint/plpprintd.cc:1005 msgid "plpprintd: could not connect to ncpd" msgstr "plpprintd: kunde inte få kontakt med ncpd" plptools-1.0.26/sisinstall/000077500000000000000000000000001504470754400156535ustar00rootroot00000000000000plptools-1.0.26/sisinstall/.gitignore000066400000000000000000000000131504470754400176350ustar00rootroot00000000000000sisinstall plptools-1.0.26/sisinstall/Makefile.am000066400000000000000000000022741504470754400177140ustar00rootroot00000000000000# sisinstall/Makefile.am # # This file is part of plptools. # # Copyright (C) 2002 Daniel Brahneborg # Copyright (C) 2007-2025 Reuben Thomas # # 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 # along with this program; if not, see . bin_PROGRAMS = sisinstall sisinstall_CPPFLAGS = -I$(top_srcdir)/lib -I$(top_srcdir)/libgnu -I$(top_builddir)/libgnu sisinstall_CXXFLAGS = $(WARN_CXXFLAGS) sisinstall_LDADD = ../lib/libplp.la $(INTLLIBS) $(SERVENT_LIB) $(top_builddir)/libgnu/libgnu.a sisinstall_SOURCES = psion.cpp sisinstaller.cpp sismain.cpp \ fakepsion.cpp sisfilelink.cpp sisfilelink.h \ psion.h sisinstaller.h fakepsion.h plptools-1.0.26/sisinstall/fakepsion.cpp000066400000000000000000000033441504470754400203420ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 2002 Daniel Brahneborg * * 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 * along with this program; if not, see . * */ #include "config.h" #include "fakepsion.h" #include "sistypes.h" #include FakePsion::~FakePsion() { } bool FakePsion::connect() { return true; } Enum FakePsion::copyToPsion(const char * const from, const char * const to, void *, cpCallback_t func) { if (logLevel >= 1) printf(" -- Not really copying %s to %s\n", from, to); return rfsv::E_PSI_GEN_NONE; } Enum FakePsion::devinfo(const char drive, PlpDrive& plpDrive) { return rfsv::E_PSI_GEN_NONE; } Enum FakePsion::devlist(uint32_t& devbits) { return rfsv::E_PSI_GEN_FAIL; } Enum FakePsion::dir(const char* dir, PlpDir& files) { return rfsv::E_PSI_GEN_NONE; } bool FakePsion::dirExists(const char* name) { return true; } void FakePsion::disconnect() { } Enum FakePsion::mkdir(const char* dir) { if (logLevel >= 1) printf(" -- Not really creating dir %s\n", dir); return rfsv::E_PSI_GEN_NONE; } void FakePsion::remove(const char* name) { } plptools-1.0.26/sisinstall/fakepsion.h000066400000000000000000000027551504470754400200140ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 2002 Daniel Brahneborg * * 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 * along with this program; if not, see . * */ #ifndef _FAKEPSION_H #define _FAKEPSION_H #include "psion.h" /** * A dummy version of the Psion proxy, mainly for testing the installer. */ class FakePsion : public Psion { public: virtual ~FakePsion(); virtual bool connect(); virtual Enum copyToPsion(const char * const from, const char * const to, void *, cpCallback_t func); virtual Enum devinfo(const char drive, PlpDrive& plpDrive); virtual Enum devlist(uint32_t& devbits); virtual Enum dir(const char* dir, PlpDir& files); virtual bool dirExists(const char* name); virtual void disconnect(); virtual Enum mkdir(const char* dir); virtual void remove(const char* name); }; #endif plptools-1.0.26/sisinstall/psion.cpp000066400000000000000000000062021504470754400175070ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 2002 Daniel Brahneborg * * 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 * along with this program; if not, see . * */ #include "config.h" #include "psion.h" #include #include #include #include #include #include #include #include #include #include #include Psion::~Psion() { disconnect(); } bool Psion::connect() { int sockNum = DPORT; #if 0 setlocale (LC_ALL, ""); textdomain(PACKAGE); #endif struct servent *se = getservbyname("psion", "tcp"); endservent(); if (se != 0L) sockNum = ntohs(se->s_port); #if 0 // Command line parameter processing if ((argc > 2) && !strcmp(argv[1], "-p")) { sockNum = atoi(argv[2]); argc -= 2; for (int i = 1; i < argc; i++) argv[i] = argv[i + 2]; } #endif m_skt = new ppsocket(); if (!m_skt->connect(NULL, sockNum)) { return false; } m_skt2 = new ppsocket(); if (!m_skt2->connect(NULL, sockNum)) { return false; } m_rfsvFactory = new rfsvfactory(m_skt); m_rpcsFactory = new rpcsfactory(m_skt2); m_rfsv = m_rfsvFactory->create(true); m_rpcs = m_rpcsFactory->create(true); if ((m_rfsv != NULL) && (m_rpcs != NULL)) return true; return false; } Enum Psion::copyFromPsion(const char * const from, int fd, cpCallback_t func) { return m_rfsv->copyFromPsion(from, fd, func); } Enum Psion::copyToPsion(const char * const from, const char * const to, void *, cpCallback_t func) { Enum res; res = m_rfsv->copyToPsion(from, to, NULL, func); // printf("Returned to Psion\n"); return res; } Enum Psion::devinfo(const char drive, PlpDrive& plpDrive) { return m_rfsv->devinfo(drive, plpDrive); } Enum Psion::devlist(uint32_t& devbits) { Enum res; res = m_rfsv->devlist(devbits); return res; } Enum Psion::dir(const char* dir, PlpDir& files) { return m_rfsv->dir(dir, files); } bool Psion::dirExists(const char* name) { rfsvDirhandle handle; Enum res; bool exists = false; res = m_rfsv->opendir(rfsv::PSI_A_ARCHIVE, name, handle); if (res == rfsv::E_PSI_GEN_NONE) exists = true; res = m_rfsv->closedir(handle); return exists; } void Psion::disconnect() { delete m_rfsv; delete m_rpcs; delete m_skt; delete m_skt2; delete m_rfsvFactory; delete m_rpcsFactory; } Enum Psion::mkdir(const char* dir) { return m_rfsv->mkdir(dir); } void Psion::remove(const char* name) { m_rfsv->remove(name); } plptools-1.0.26/sisinstall/psion.h000066400000000000000000000034011504470754400171520ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 2002 Daniel Brahneborg * * 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 * along with this program; if not, see . * */ #ifndef _PSION_H #define _PSION_H #include #include "rfsv.h" class ppsocket; class rfsvfactory; class rpcsfactory; class rpcs; /** * Semi smart proxy for communicating with a Psion. */ class Psion { public: virtual ~Psion(); virtual bool connect(); virtual Enum copyFromPsion(const char * const from, int fd, cpCallback_t func); virtual Enum copyToPsion(const char * const from, const char * const to, void *, cpCallback_t func); virtual Enum devinfo(const char drive, PlpDrive& plpDrive); virtual Enum devlist(uint32_t& devbits); virtual Enum dir(const char* dir, PlpDir& files); virtual bool dirExists(const char* name); virtual void disconnect(); virtual Enum mkdir(const char* dir); virtual void remove(const char* name); private: ppsocket* m_skt; ppsocket* m_skt2; rfsvfactory* m_rfsvFactory; rpcsfactory* m_rpcsFactory; rpcs* m_rpcs; rfsv* m_rfsv; }; #endif plptools-1.0.26/sisinstall/sisfilelink.cpp000066400000000000000000000015741504470754400207020ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 2002 Daniel Brahneborg * * 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 * along with this program; if not, see . * */ #include "config.h" #include "sisfilelink.h" SISFileLink::SISFileLink(SISFile* file) { m_file = file; m_next = 0; } plptools-1.0.26/sisinstall/sisfilelink.h000066400000000000000000000016611504470754400203440ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 2002 Daniel Brahneborg * * 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 * along with this program; if not, see . * */ #ifndef _SISFILELINK_H #define _SISFILELINK_H class SISFile; class SISFileLink { public: SISFileLink(SISFile* file); SISFileLink* m_next; SISFile* m_file; }; #endif plptools-1.0.26/sisinstall/sisinstaller.cpp000066400000000000000000000641271504470754400211050ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 2002 Daniel Brahneborg * * 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 * along with this program; if not, see . * */ #include "config.h" #include "sisinstaller.h" #include "sisfile.h" #include "sisfilelink.h" #include "sisfilerecord.h" #include "sisreqrecord.h" #include "psion.h" #include #include #include #include #include #include #include "ignore-value.h" static int continueRunning; static int checkAbortHash(void *, uint32_t) { if (continueRunning) { if (logLevel >= 1) { printf("#"); fflush(stdout); } } return continueRunning; } SISInstaller::SISInstaller() { m_installed = nullptr; m_ownInstalled = false; } SISInstaller::~SISInstaller() { if (m_ownInstalled) { SISFileLink* curr = m_installed; while (curr) { delete curr->m_file; SISFileLink* next = curr->m_next; delete curr; curr = next; } } } void SISInstaller::createDirs(char* filename) { char* end = filename + strlen(filename); while (--end > filename) { char ch = *end; if ((ch == '/') || (ch == '\\')) { *end = 0; if (logLevel >= 1) fprintf(stderr, "Checking for existance of %s\n", filename); // if (!m_psion->dirExists(filename)) { if (logLevel >= 1) fprintf(stderr, "Creating dir %s\n", filename); Enum res; res = m_psion->mkdir(filename); if ((res != rfsv::E_PSI_GEN_NONE) && (res != rfsv::E_PSI_FILE_EXIST)) { fprintf(stderr, " -> Failed: %s\n", (const char*)res); } } *end = ch; return; } } } void SISInstaller::copyFile(SISFileRecord* fileRecord) { uint8_t* destptr = fileRecord->getDestPtr(); if (destptr == nullptr) return; if (destptr[0] == '!') { if (m_drive == 0) selectDrive(); m_file->setDrive(m_drive); } int len = fileRecord->m_destLength; char dest[256]; memcpy(dest, destptr, len); dest[len] = 0; if (dest[0] == '!') { dest[0] = m_drive; fileRecord->setMainDrive(m_drive); if (logLevel >= 2) fprintf(stderr, "Setting main drive to %c\n", m_drive); } char msgbuf[1024]; sprintf(msgbuf, "Copying %u bytes to %s\n", fileRecord->m_fileLengths[m_fileNo], dest); { printf("%s\n", msgbuf); } copyBuf(fileRecord->getFilePtr(m_fileNo), fileRecord->m_fileLengths[m_fileNo], dest); } void SISInstaller::copyBuf(const uint8_t* buf, int len, char* name) { createDirs(name); char srcName[32]; strcpy(srcName, "/tmp/plptools-sis-XXXXXX"); int fd = mkstemp(srcName); if (-1 == fd) { fprintf(stderr, "Couldn't create temp file: %s\n", strerror(errno)); return; } Enum res; if (logLevel >= 2) fprintf(stderr, "Storing in %s\n", srcName); ssize_t written = write(fd, buf, len); int close_res = close(fd); bool write_ok = written == len && close_res == 0; if (write_ok) { continueRunning = 1; res = m_psion->copyToPsion(srcName, name, NULL, checkAbortHash); } if (write_ok && res == rfsv::E_PSI_GEN_NONE) { if (logLevel >= 1) fprintf(stderr, " -> Success.\n"); } else { fprintf(stderr, " -> Fail: %s\n", (const char*)res); } unlink(srcName); } int SISInstaller::installFile(SISFileRecord* fileRecord) { char readbuf[8]; switch (fileRecord->m_fileType) { case 0: copyFile(fileRecord); break; case 1: { printf("Info:\n%.*s\n", (int) fileRecord->m_fileLengths[m_fileNo], fileRecord->getFilePtr(m_fileNo)); switch (fileRecord->m_fileDetails) { case 0: printf("Continue\n"); ignore_value(fgets(readbuf, sizeof(readbuf), stdin)); break; case 1: { printf("(Install next file?) [Y]es/No\n"); char *res = fgets(readbuf, sizeof(readbuf), stdin); if (!res || strchr("Nn", readbuf[0])) { return FILE_SKIP; } break; } case 2: { printf("(Continue installation?) [Y]es/No\n"); char *res = fgets(readbuf, sizeof(readbuf), stdin); if (!res || strchr("Nn", readbuf[0])) { // Watch out if we have copied any files // already. // return FILE_ABORT; } break; } } } break; case 2: { if (logLevel >= 1) fprintf(stderr, "Recursive sis file...\n"); SISFile sisFile; uint8_t* buf2 = fileRecord->getFilePtr(m_fileNo); off_t len = fileRecord->m_fileLengths[m_fileNo]; // if (m_lastSisFile < fileptr + len) // m_lastSisFile = fileptr + len; SisRC rc = sisFile.fillFrom(buf2, len); if (rc != SIS_OK) { fprintf(stderr, "Could not read contained sis file, rc = %d\n", rc); break; } SISInstaller installer; installer.setPsion(m_psion); installer.setInstalled(m_installed); rc = installer.run(&sisFile, buf2, len, m_file); if (0 == m_drive) { m_drive = sisFile.m_header.m_installationDrive; m_file->setDrive(m_drive); if (logLevel >= 1) fprintf(stderr, "Updated drive to %c from recursive sis file\n", m_drive); } break; } case 3: if (logLevel >= 1) fprintf(stderr, "Run %.*s during installation/remove\n", (int) fileRecord->m_destLength, fileRecord->getDestPtr()); break; case 4: if (logLevel >= 2) fprintf(stderr, "Running the app will create %.*s\n", (int) fileRecord->m_destLength, fileRecord->getDestPtr()); break; } return FILE_OK; } #define SYSTEMINSTALL "c:\\system\\install\\" SisRC SISInstaller::loadInstalled() { PlpDir files; Enum res; if ((res = m_psion->dir(SYSTEMINSTALL, files)) != rfsv::E_PSI_GEN_NONE) { return SIS_FAILED; } else { while (!files.empty()) { PlpDirent file = files[0]; if (logLevel >= 1) fprintf(stderr, "Loading sis file `%s'\n", file.getName()); char sisname[256]; sprintf(sisname, "%s%s", SYSTEMINSTALL, file.getName()); loadPsionSis(sisname); files.pop_front(); } return SIS_OK; } } void SISInstaller::loadPsionSis(const char* name) { char srcName[32]; strcpy(srcName, "/tmp/plptools-sis-XXXXXX"); int fd = mkstemp(srcName); if (-1 == fd) { fprintf(stderr, "Couldn't create temp file: %s\n", strerror(errno)); return; } Enum res; continueRunning = 1; if (logLevel >= 2) fprintf(stderr, "Copying from %s to temp file %s\n", name, srcName); res = m_psion->copyFromPsion(name, fd, checkAbortHash); if (res == rfsv::E_PSI_GEN_NONE) { off_t fileLen = lseek(fd, 0, SEEK_END); if (logLevel >= 2) fprintf(stderr, "Read %d bytes from the Psion file %s\n", (int)fileLen, name); lseek(fd, SEEK_SET, 0); uint8_t* sisbuf = new uint8_t[fileLen]; int rc = read(fd, sisbuf, fileLen); if (rc == fileLen) { SISFile* sisFile = new SISFile(); SisRC rc2 = sisFile->fillFrom(sisbuf, fileLen); if (rc2 == SIS_OK) { if (logLevel >= 1) fprintf(stderr, " Ok.\n"); SISFileLink* link = new SISFileLink(sisFile); link->m_next = m_installed; m_ownInstalled = true; m_installed = link; sisFile->ownBuffer(); } else { delete sisFile; delete[] sisbuf; } } } close(fd); unlink(srcName); } void SISInstaller::removeFile(SISFileRecord* fileRecord) { int len = fileRecord->m_destLength; char dest[256]; memcpy(dest, fileRecord->getDestPtr(), len); dest[len] = 0; if (logLevel >= 1) fprintf(stderr, "Removing file component %s.\n", dest); m_psion->remove(dest); } SisRC SISInstaller::run(SISFile* file, uint8_t* buf, off_t len) { return run(file, buf, len, 0); } SisRC SISInstaller::run(SISFile* file, uint8_t* buf, off_t len, SISFile* parent) { int n; long lang; m_file = file; m_buf = buf; char msgbuf[1024]; if (parent == 0) { n = m_file->m_header.m_nlangs; if (n == 1) { sprintf(msgbuf, _("You have only one language: %s"), m_file->getLanguage(0)->m_name); printf("%s\n", msgbuf); lang = 0; } else { printf("Select a language (%d alternatives):\n", n); for (int i = 0; i < n; ++i) printf(" %d. %s\n", i, m_file->getLanguage(i)->m_name); lang = 0; } } else { // This needs to check the _name_ of the language, since the // recursive sis file might have a different language list. // For now, defalt to 0. // lang = parent->getLanguage(); lang = 0; if (logLevel >= 1) fprintf(stderr, "Forcing language to %ld\n", lang); } m_file->setLanguage(lang); uint8_t* compName = m_file->getName(); sprintf(msgbuf, _("Installing component: `%s'"), compName); printf("%s\n", msgbuf); // In order to check requisites and previous versions, we need to // load all sis files from the c:/system/install directory. // This is the only way to find out if a specific application or // library has been loaded, since the sis file names could be just // about anything. // if (m_installed == 0) loadInstalled(); // Check Requisites. // n = m_file->m_header.m_nreqs; if (logLevel >= 1) fprintf(stderr, "Found %d requisites, of some sort.\n", n); for (int i = 0; i < n; ++i) { SISReqRecord* reqRecord = &m_file->m_reqRecords[i]; if (logLevel >= 1) fprintf(stderr, " Check if app with uid %08x exists with version >= %d.%d\n", reqRecord->m_uid, reqRecord->m_major, reqRecord->m_minor); } // Check previous version. // if (logLevel >= 1) fprintf(stderr, "Checking if this app (uid %08x) exists with a version less than %d.%d.\n", m_file->m_header.m_uid1, m_file->m_header.m_major, m_file->m_header.m_minor); bool uninstallFirst = false; SISFileLink* curr = m_installed; SISFile* oldFile = 0; while (curr) { SISFile* sisFile = curr->m_file; switch (sisFile->compareApp(m_file)) { case SIS_VER_EARLIER: uninstallFirst = true; oldFile = sisFile; break; case SIS_SAME_OR_LATER: // Ask for confirmation. uninstallFirst = true; oldFile = sisFile; break; case SIS_OTHER_VARIANT: // Ask for confirmation. uninstallFirst = true; oldFile = sisFile; break; } curr = curr->m_next; } if (uninstallFirst) { // printf("You should uninstall the previous version first.\n"); // if (!m_forced) // return SIS_ABORTED; // printf("Forced mode... Installing anyway!\n"); if (oldFile == 0) fprintf(stderr, "Already installed, but 0?\n"); else { sprintf(msgbuf, "%s", _("Uninstalling the previous version first.")); printf("%s\n", msgbuf); uninstall(oldFile); } } // Install file components. // n = m_file->m_header.m_nfiles; if (logLevel >= 1) fprintf(stderr, "Found %d files.\n", n); m_drive = (parent == 0) ? 0 : parent->m_header.m_installationDrive; int nCopiedFiles = 0; m_lastSisFile = 0; bool skipnext = false; bool aborted = false; while (!aborted && (n-- > 0)) { SISFileRecord* fileRecord = &m_file->m_fileRecords[n]; m_fileNo = (fileRecord->m_flags & 1) ? lang : 0; if (skipnext) { skipnext = false; } else { switch (installFile(fileRecord)) { case FILE_OK: break; case FILE_SKIP: skipnext = true; break; case FILE_ABORT: aborted = true; break; } } if (!aborted) nCopiedFiles++; } m_file->setFiles(nCopiedFiles); if (logLevel >= 1) fprintf(stderr, "Installed %d files of %d, cutting at offset %u.\n", m_file->m_header.m_installationFiles, m_file->m_header.m_nfiles, m_file->getResidualEnd()); if (nCopiedFiles == 0) { // There is no need to copy any uninstall information to the // psion, unless we've actually copied anything there. // return SIS_ABORTED; } // Copy the updated sis file to the epoc machine. // char resname[256]; int namelen = 0; while (compName[namelen] != 0) { if (compName[namelen] == ' ') break; namelen++; } sprintf(resname, "C:\\System\\Install\\%.*s.sis", namelen, compName); if (logLevel >= 1) fprintf(stderr, "Creating residual sis file %s\n", resname); copyBuf(buf, m_file->getResidualEnd(), resname); if (aborted) return SIS_ABORTED; return SIS_OK; } void SISInstaller::selectDrive() { uint32_t devbits = 0; Enum res; char drivelist[26]; int ndrives = 0; if ((res = m_psion->devlist(devbits)) == rfsv::E_PSI_GEN_NONE) { for (int i = 0; i < 26; i++) { PlpDrive plpdrive; if (((devbits & 1) != 0) && (m_psion->devinfo(i + 'A', plpdrive) == rfsv::E_PSI_GEN_NONE)) { uint32_t mediaType = plpdrive.getMediaType(); if ((mediaType == 3) || (mediaType == 5)) { drivelist[ndrives] = 'A' + i; printf("%c: %lud bytes free, %lud bytes total\n", 'A' + i, plpdrive.getSpace(), plpdrive.getSize()); ++ndrives; } } devbits >>= 1; } } drivelist[ndrives] = 0; if (ndrives == 0) { m_drive = 'C'; if (logLevel >= 1) printf("Selecting the only drive %c\n", m_drive); } else if (ndrives == 1) { m_drive = drivelist[0]; if (logLevel >= 1) printf("Selecting the only drive %c\n", m_drive); } else { { printf("Please select a drive\n"); char ch; char readbuf[2]; while (m_drive == 0) { if (fgets(readbuf, 2, stdin) == NULL) { printf("please enter a drive"); continue; } ch = readbuf[0]; if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) { m_drive = toupper(ch); if (!strchr(drivelist, m_drive)) { m_drive = 0; printf("Please select a valid drive: %s\n", drivelist); } } } } } } void SISInstaller::setPsion(Psion* psion) { m_psion = psion; } void SISInstaller::uninstall(SISFile* file) { int n = file->m_header.m_nfiles; int fileix = n - file->m_header.m_installationFiles; if (logLevel >= 1) fprintf(stderr, "Uninstalling %d files, from a total of %d.\n", file->m_header.m_installationFiles, file->m_header.m_nfiles); int lang = file->getLanguage(); while (fileix < n) { SISFileRecord* fileRecord = &file->m_fileRecords[fileix]; m_fileNo = (fileRecord->m_flags & 1) ? lang : 0; char drive = file->m_header.m_installationDrive; fileRecord->setMainDrive(drive); uninstallFile(fileRecord); ++fileix; } } void SISInstaller::uninstallFile(SISFileRecord* fileRecord) { switch (fileRecord->m_fileType) { case 0: case 4: removeFile(fileRecord); break; case 2: { #if 0 // This is messy... We can't remove the sis component unless // we've stored the entire component in the residual sis // file on the target machine. if (logLevel >= 1) fprintf(stderr, "Recursive sis file...\n"); SISFile sisFile; int fileptr = fileRecord->m_filePtrs[m_fileNo]; uint8_t* buf2 = m_buf + fileptr; off_t len = fileRecord->m_fileLengths[m_fileNo]; if (m_lastSisFile < fileptr + len) m_lastSisFile = fileptr + len; SisRC rc = sisFile.fillFrom(buf2, len); if (rc != SIS_OK) { fprintf(stderr, "Could not read contained sis file, rc = %d\n", rc); break; } SISInstaller installer; installer.setPsion(m_psion); installer.setInstalled(m_installed); rc = installer.run(&sisFile, buf2, len, m_file); if (0 == m_drive) { m_drive = sisFile.m_header.m_installationDrive; m_file->setDrive(m_drive); if (logLevel >= 1) fprintf(stderr, "Updated drive to %c from recursive sis file\n", m_drive); } #endif break; } } } plptools-1.0.26/sisinstall/sisinstaller.h000066400000000000000000000043571504470754400205510ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 2002 Daniel Brahneborg * * 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 * along with this program; if not, see . * */ #ifndef _SISINSTALLER_H #define _SISINSTALLER_H #include "sistypes.h" #include class Psion; class SISFile; class SISFileLink; class SISFileRecord; /** * A minimal SIS installer. * Handles recursive sis files. */ class SISInstaller { public: SISInstaller(); virtual ~SISInstaller(); SisRC run(SISFile* file, uint8_t* buf, off_t len); SisRC run(SISFile* file, uint8_t* buf, off_t len, SISFile* parent); /** * Ask the user which drive to install to. */ void selectDrive(); /** * Set the base pointer to the list of already installed * applications, so we don't have to scan it for every sis * component. */ void setInstalled(SISFileLink* installed) { m_installed = installed; } /** * Set the Psion manager. */ void setPsion(Psion* psion); private: char m_drive; int m_fileNo; Psion* m_psion; uint8_t* m_buf; SISFile* m_file; SISFileLink* m_installed; int m_lastSisFile; bool m_ownInstalled; enum { FILE_OK, FILE_SKIP, FILE_ABORT, }; /** * Store the contents of a buffer in a file on the Psion. */ void copyBuf(const uint8_t* buf, int len, char* name); /** * Copy a file to the Psion. */ void copyFile(SISFileRecord* fileRecord); void createDirs(char* filename); int installFile(SISFileRecord* fileRecord); SisRC loadInstalled(); void loadPsionSis(const char* name); void removeFile(SISFileRecord* fileRecord); void uninstall(SISFile* sisFile); void uninstallFile(SISFileRecord* fileRecord); }; #endif plptools-1.0.26/sisinstall/sismain.cpp000066400000000000000000000112741504470754400200270ustar00rootroot00000000000000/* * This file is part of plptools. * * Copyright (C) 2002 Daniel Brahneborg * * 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 * along with this program; if not, see . * */ #include "config.h" #include "sisfile.h" #include "sisinstaller.h" #include "psion.h" #include "fakepsion.h" #include #include #include #include #include #include #include #include #include #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include static void error(int line) { fprintf(stderr, _("Error %d on line %d: %s\n"), errno, line, strerror(errno)); exit(1); } static struct option opts[] = { { "help", no_argument, 0, 'h' }, { "version", no_argument, 0, 'V' }, { "verbose", required_argument, 0, 'v' }, { "dry-run", no_argument, 0, 'n' }, { NULL, 0, 0, 0 }, }; void printHelp() { printf("%s", _("Usage: sisinstall [OPTIONS]... SISFILE\n" "\n" "Supported options:\n" "\n" " -h, --help Display this text.\n" " -V, --version Print version and exit.\n" " -v, --verbose=LEVEL Set the verbosity level, by default 0.\n" " -n, --dry-run Just parse the file.\n" )); } int main(int argc, char* argv[]) { char* filename = 0; char option; bool dryrun = false; #ifdef LC_ALL setlocale(LC_ALL, ""); #endif textdomain(PACKAGE); while (1) { option = getopt_long(argc, argv, "hnv:V" , opts, NULL); if (option == -1) break; switch (option) { case 'h': case '?': printHelp(); exit(0); case 'v': logLevel = atoi(optarg); break; case 'n': dryrun = true; break; case 'V': printf("%s", _("sisinstall version 0.1\n")); exit(0); } } if (optind < argc) { filename = argv[optind]; printf(_("Installing sis file %s%s.\n"), filename, dryrun ? _(", not really") : ""); } else { fprintf(stderr, "%s", _("Missing SIS filename\n")); exit(1); } struct stat st; if (-1 == stat(filename, &st)) error(__LINE__); off_t len = st.st_size; if (logLevel >= 2) printf(_("File is %jd bytes long\n"), (intmax_t)len); uint8_t* buf = new uint8_t[len]; int fd = open(filename, O_RDONLY); if (-1 == fd) error(__LINE__); if (-1 == read(fd, buf, len)) error(__LINE__); close(fd); Psion* psion; if (dryrun) psion = new FakePsion(); else psion = new Psion(); if (!psion->connect()) { printf("%s", _("Couldn't connect with the Psion\n")); } else { createCRCTable(); SISFile sisFile; SisRC rc = sisFile.fillFrom(buf, len); if (rc == SIS_OK) { SISInstaller installer; installer.setPsion(psion); installer.run(&sisFile, buf, len); } else printf("%s", _("Could not parse the sis file.\n")); psion->disconnect(); } return 0; }