From 8c52e5dba8046d3ead0910f6238e1953f1b177ab Mon Sep 17 00:00:00 2001 From: crt0mega Date: Tue, 15 Sep 2020 21:16:28 +0200 Subject: [PATCH] Initial commit --- .gitignore | 21 + AUTHORS | 5 + COPYING | 340 ++++ ChangeLog | 683 +++++++ INSTALL | 368 ++++ Makefile.am | 21 + NEWS | 248 +++ README | 417 +++++ THANKS | 62 + TODO | 741 ++++++++ autogen.sh | 230 +++ compile | 348 ++++ configure.in | 201 ++ debian/README.Debian | 6 + debian/README.source | 8 + debian/changelog | 5 + debian/control | 16 + debian/copyright | 1 + debian/files | 3 + debian/ic35link-docs.docs | 2 + debian/ic35link.doc-base.EX | 20 + debian/ic35link.substvars | 3 + debian/patches/fix-formats | 16 + debian/patches/fix-label | 11 + debian/patches/gcc-build-workaround | 14 + debian/patches/series | 3 + debian/rules | 25 + debian/source/format | 1 + depcomp | 791 ++++++++ doc/.cvsignore | 2 + doc/CVS/Entries | 5 + doc/CVS/Repository | 1 + doc/CVS/Root | 1 + doc/Makefile.am | 17 + doc/ic35mgr.txt | 783 ++++++++ doc/ic35sync.txt | 846 +++++++++ src/.cvsignore | 9 + src/CVS/Entries | 35 + src/CVS/Repository | 1 + src/CVS/Root | 1 + src/Makefile.am | 64 + src/comio.c | 484 +++++ src/comio.h | 37 + src/databin.c | 362 ++++ src/dataio.c | 428 +++++ src/dataio.h | 77 + src/datatxt.c | 113 ++ src/datavca.c | 1355 ++++++++++++++ src/genproto.c | 199 ++ src/genproto.h | 39 + src/ic35frec.c | 384 ++++ src/ic35frec.h | 138 ++ src/ic35log.sh | 807 ++++++++ src/ic35mgr.c | 991 ++++++++++ src/ic35sync.c | 980 ++++++++++ src/mgrproto.c | 951 ++++++++++ src/mgrproto.h | 95 + src/mgrtrans.c | 943 ++++++++++ src/mgrtrans.h | 52 + src/port.h | 94 + src/synproto.c | 649 +++++++ src/synproto.h | 44 + src/syntrans.c | 515 +++++ src/syntrans.h | 37 + src/util.c | 298 +++ src/util.h | 96 + src/vcaconv.c | 722 +++++++ src/vcc.c | 2710 +++++++++++++++++++++++++++ src/vcc.h | 83 + src/vcc.y | 1249 ++++++++++++ src/vcutil.c | 762 ++++++++ src/vcutil.h | 61 + src/vobject.c | 1511 +++++++++++++++ src/vobject.h | 380 ++++ stamp-h1 | 1 + ylwrap | 247 +++ 76 files changed, 24269 insertions(+) create mode 100644 .gitignore create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 INSTALL create mode 100644 Makefile.am create mode 100644 NEWS create mode 100644 README create mode 100644 THANKS create mode 100644 TODO create mode 100644 autogen.sh create mode 100755 compile create mode 100644 configure.in create mode 100644 debian/README.Debian create mode 100644 debian/README.source create mode 100644 debian/changelog create mode 100644 debian/control create mode 120000 debian/copyright create mode 100644 debian/files create mode 100644 debian/ic35link-docs.docs create mode 100644 debian/ic35link.doc-base.EX create mode 100644 debian/ic35link.substvars create mode 100644 debian/patches/fix-formats create mode 100644 debian/patches/fix-label create mode 100644 debian/patches/gcc-build-workaround create mode 100644 debian/patches/series create mode 100755 debian/rules create mode 100644 debian/source/format create mode 100755 depcomp create mode 100644 doc/.cvsignore create mode 100644 doc/CVS/Entries create mode 100644 doc/CVS/Repository create mode 100644 doc/CVS/Root create mode 100644 doc/Makefile.am create mode 100644 doc/ic35mgr.txt create mode 100644 doc/ic35sync.txt create mode 100644 src/.cvsignore create mode 100644 src/CVS/Entries create mode 100644 src/CVS/Repository create mode 100644 src/CVS/Root create mode 100644 src/Makefile.am create mode 100644 src/comio.c create mode 100644 src/comio.h create mode 100644 src/databin.c create mode 100644 src/dataio.c create mode 100644 src/dataio.h create mode 100644 src/datatxt.c create mode 100644 src/datavca.c create mode 100644 src/genproto.c create mode 100644 src/genproto.h create mode 100644 src/ic35frec.c create mode 100644 src/ic35frec.h create mode 100644 src/ic35log.sh create mode 100644 src/ic35mgr.c create mode 100644 src/ic35sync.c create mode 100644 src/mgrproto.c create mode 100644 src/mgrproto.h create mode 100644 src/mgrtrans.c create mode 100644 src/mgrtrans.h create mode 100644 src/port.h create mode 100644 src/synproto.c create mode 100644 src/synproto.h create mode 100644 src/syntrans.c create mode 100644 src/syntrans.h create mode 100644 src/util.c create mode 100644 src/util.h create mode 100644 src/vcaconv.c create mode 100644 src/vcc.c create mode 100644 src/vcc.h create mode 100644 src/vcc.y create mode 100644 src/vcutil.c create mode 100644 src/vcutil.h create mode 100644 src/vobject.c create mode 100644 src/vobject.h create mode 100644 stamp-h1 create mode 100755 ylwrap diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4c6d063 --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +Makefile.in +Makefile +aclocal.m4 +configure +config.h.in +config.h +config.log +config.cache +config.status +install-sh +missing +mkinstalldirs +stamp-h.in +stamp-h +autom4te.cache +debian/autoreconf.after +debian/autoreconf.before +debian/debhelper-build-stamp +debian/*.ex +debian/ic35link +debian/.debhelper diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..41a1e38 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,5 @@ +IC35Link was written by Thomas Schulz, +see the file THANKS about contributions. + +Thomas Schulz + diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..d60c31a --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..807ac1e --- /dev/null +++ b/ChangeLog @@ -0,0 +1,683 @@ + $Id: ChangeLog,v 1.26 2001/11/20 23:26:42 thosch Exp $ + ChangeLog for IC35 communication + ================================ + +2001-11-10 version 1.18.1 (CVS only) + * ic35mgr.c,mgrtrans.c,mgrtrans.h,mgrproto.h,mgrproto.c: new ic35mgr + commands "mmcgettree","mmcputtree" for transfer whole directory tree + contributed by Hans-Michael Stahl + +2001-08-12 version 1.18 IC35LINK-1-18 + long pending release with several improvements and corrections + according to suggestions and bug reports by users. + improvements: + - improved README: table of contents, restrictions, more explanations + - ic35mgr restore and MMC write 30% faster (proposal by Harald Becker) + - ic35sync, ic35mgr, vcaconv now support long options, e.g. "--help" + info enquiry with "--help" or "--version" now returns exitcode 0 + - new autogen.sh script for generate configure etc. after CVS checkout + (thanks Konrad Mader for report about missing configure in CVS) + - new configure option "--disable-logandcomsim" for conditional compile + without logging and com-simulation (proposal by Harald Becker) + - new configure options "--enable-ansi" and "--enable-pedantic" + bugfixes: + - fixed problem where ic35sync with many (ca. 860) addresses failed + (thanks Konrad Mader for bug report "get_mod_flen failed") + - ic35sync now accepts also empty PIMfile(s), e.g. empty ic35.memo + (thanks Malte Schmidt for bug report "ic35.memo: parse error") + - ic35mgr MMCard functions now check ic35path before accessing IC35 + e.g. warn if shell eat backslash (\) in unquoted MMCard1\test.txt + (thanks Thomas Lichtenberg for bug report) + - restricted ic35mgr to max.blocksize 16350 found by experiments + - ic35sync, ic35mgr now abort on open failure of logfile or simfile + - fixed com-simulation for adjacent "RD nn" lines and non-logged bytes + - fixed compile problem util.c with Linux Mandrake-8.0 and SuSE-7.2 + (thanks Christian Theile, Karl Stroetmann for bug reports) + - corrected warnings from "--enable-ansi" and "--missing-prototypes" + - fixed all Makefile.am: maintainer-clean keeps only sources in CVS, + install also README and COPYING, clean dependencies and versinfo.c +2001-08-12_05 + * Makefile.am: maintainer-clean delete also INSTALL, as not in CVS + * autogen.sh: clearified CONFIGURE_FLAGS and NOCONFIGURE + * README: didactic reordering, explain autogen.sh maintainer-mode, + initial test with 'ic35sync status', tested some more platforms +2001-08-02_22 + * autogen.sh: new script for generating configure and Makefile.in etc. + thanks Konrad Mader for problem report about + configure missing in CVS + * README: explain when and how to use autogen.sh +2001-06-18_01 version 1.17.6 (CVS only) + * synproto.c: timeout 2.0 sec for L1-ack1 response on L1-command + thanks Konrad Mader for bug report where + sync of ca. 860 addresses caused "get_mod_flen failed" +2001-06-09_11 + * util.c: explicitely #include , as using localtime() etc. + thanks Christian Theile for bug report with + Mandrake 8.0 gcc 2.96 +2001-03-03_04 + * ic35mgr.c,comio.c,comio.h: new option "--nice=NICEVAL" to lower + process priority with restore,mmcput, default "--nice=2", works + like builtin 'nice -n 2 ic35mgr mmcput ..", see nice(1). + (proposal from Harald Becker) +2001-03-02_02 version 1.17.5 (CVS only) + * configure.in,README: new configure option "--disable-logandcomsim" + (proposal from Harald Becker) + * ic35sync.c,dataio.c,databin.c,datavca.c,ic35frec.c,syntrans.c, + ic35mgr.c,mgrtrans.c,mgrproto.c,comio.{c,h},util.{c,h}: + conditional compile for logging and com-simulation (default enabled) + * ic35sync.c,ic35mgr.c,comio.c,util.c: + log_init(),com_siminit() abort program on failure opening file + * util.{c,h}: new fatal() reports error message and aborts program +2001-02-22_05 + * datavca.c bugfix: accept also empty PIMfile(s), e.g. empty ic35.memo + ic35sync creates empty ic35.memo if IC35 has no memo records, and + next call ic35sync complained "vCard/vCal "ic35.memo": parse error" + (thanks Malte Schmidt for bug report) + * README: explain 1..3 PIMfiles, more details about export,import,sync +2001-02-19_19 + * comio.c: com_sendw() delay with busy wait instead of interval timer + increased write MMCard file throughput to 1740 b/s (with 2kB blocks) + (thanks Harald Becker) + * ic35mgr.c: max.blocksize 16350 for MMC read/write found by experiments + * mgrproto.c: timeout 5.0 sec for response length needed with big blocks +2001-02-19_02 version 1.17.4 (CVS only) + * ic35mgr.c: MMCard functions now check ic35path before accessing IC35 + (thanks Thomas Lichtenberg for bug report) + * comio.c: fixed com_simrecv() adjacent "RD nn" lines, non-logged bytes + * README: remarks about install-strip and uninstall + * all Makefile.am: maintainer-clean keeps only sources under CVS + * .cvsignore,src/.cvsignore: added missing config.log, programs, .deps +2001-02-17_21 version 1.17.3 (CVS only) + * ic35sync.c,ic35mgr.c,vcaconv.c: support long options, e.g. "--help" + info enquiry with "--help" or "--version" now returns exitcode 0 + * ic35sync.c,ic35mgr.c,comio.h,comio.c,util.h,util.c: + error exit on open failure of logfile or simfile + * README: 'configure --enable-ansi' is ok, warning '--enable-pedantic' + successful tests on some more platforms (thanks Thomas Lichtenberg) + restrictions of ic35sync and ic35mgr + * configure.in: pass revision info to configure + * Makefile.am: install also README and COPYING + * src/Makefile.am: clean dependencies and versinfo.c +2001-02-10_04 version 1.17.1 (CVS only) + * configure.in: separate "ansi" and "pedantic" gcc compile options + * ic35sync.c,ic35mgr.c,datavca.c,port.h,vcc.y,comio.c: + corrected warnings from "-ansi" compile + * util.c,util.h,comio.c,comio.h: + local substitutes for functions missing with "-ansi" compile +2001-02-07_04 + * configure.in: more compile warnings with "--missing-prototypes" + * databin.c,mgrtrans.c,vcc.y,vobject.c: + corrected warnings from "--missing-prototypes" + * README: table of contents, warning about 'configure --enable-ansi' + +2001-02-05_03 IC35LINK-1-17 + package renamed to "ic35link" and automake/autoconf/CVS reorganization. + no changes in sources, programs are unchanged except for version (and + except for compile with -O2 instead of -O due to automake/autoconf). + new files: + NEWS release information + AUTHORS authors + THANKS contributors + configure.in input for autoconf to create configure + Makefile.am input for automake to create Makefile.in + .cvsignore topdir CVS ignore patterns + doc/ subdirectory with documentation + doc/Makefile.am input for automake to create doc/Makefile.in + doc/.cvsignore CVS ignore patterns + src/ subdirectory with sources + src/Makefile.am input for automake to create doc/Makefile.in + src/.cvsignore CVS ignore patterns + +2001-02-04_02 IC35SYNC-1-16 + this is a bugfix and redesign release and is the last named "ic35sync". + (the package will be renamed to "ic35link" in the next release, when + the reorganizations for automake/autoconf and public CVS are done.) + improvements and redesign: + - ic35sync,ic35mgr,vcaconv now report package version and build info + - distribution includes vcc.c to handle missing/bad 'bison' or 'yacc' + (e.g. SuSE-7.0's yacc produced bad vcc.o, thanks Michael Bruennert) + - reworked and clearified the IC35 manager protocol documentation and + implementation (see ic35mgr.txt, mgrtrans.*, mgrproto.*), refer to + the IC35 SoftwareDevelopmentKit API MMCard functions in mgrproto.c + bugfixes in ic35mgr and ic35sync: + - fixed compile error RedHat-7.0 gcc-2.96 (thanks Dieter Schultschik) + - corrections for 'ic35mgr status' (thanks Michael Bruennert): + mgrtrans.c:status() tries to get IC35 response instead of only wait + mgrproto.c:MMCsend() sends blocklength with initial wait + corrected output of MMCard2 label + - ic35mgr commands backup and restore show error messages on new line + - avoid failing 'ic35mgr restore ..' at "disconnect" by delay 3.25 sec + after writedatabase() done + - avoid failure of 'ic35mgr mmcput file' with occasional slow IC35 + MMCard write with 3.5 sec timeout for length of response block + - corrected MMC closedir,closefile response PDUs having no status-word + - corrected to make 'ic35sync status' work also with simulation file +2001-02-04_01 + * ic35sync.c,ic35mgr.c,vcaconv.c,Makefile: + import version info from auto-generated "versinfo.c" + all programs now report the common package version + * util.{c,h}: new log_proginfo(),log_argsinfo() used + by ic35sync.c,ic35mgr.c for initial info in logfile + * ic35sync.c: corrected com_siminit() before status command + * Makefile: include vcc.c in distribution to avoid bison/yacc problems +2001-02-03_20 + * mgrproto.h,mgrtrans.h: moved MMC file attributes and open modes + from mgrtrans.h to mgrproto.h as needed for MMCxxxx() functions + * mgrproto.c,mgrtrans.c: common conversions functions for FILE_INFO + import FIDENSZ for accurate size of FILE_IDEN from IC35 SDK Mmc.h + * ic35mgr.txt: incorporated protocol corrections due to experience +2001-01-30_01 + * mgrproto.c: need 3.5 sec timeout for length of response block +2001-01-29_13 + * fixed compile error gcc-2.96 RedHat-7.0 (thanks Dieter Schultschik): + synproto.c now passes 'int' instead of 'ushort to 'va_arg', because + `short unsigned int' is promoted to `int' when passed through `...' + * mgrproto.h,mgrproto.c,mgrtrans.c: + new MMCard command functions MMCxxxx() replace sendmcmd(),recvmcmd() + for better prototype check and avoid the gcc-2.96 RedHat-7.0 problem + refer to IC35 SoftwareDevelopmentKit API MMCard functions + * synproto.c,mgrproto.c,ic35frec.c offsetof,alenof macros now in util.h +2001-01-27_05 + * bugfix "ic35mgr status" (thanks to Michael Bruennert): + mgrtrans.c:status() tries to get IC35 response instead of only wait + mgrproto.c:MMCsend() sends blocklength with initial wait + ic35mgr.c: corrected MMCard2 label output + * ic35mgr.c,util.{c,h}: extracted _not_impl() to util.{c,h} +2001-01-25_05 + * mgrtrans.c,mgrproto.c,ic35mgr.txt: + corrected: closedir,closefile response PDUs do not have status-word + * mgrtrans.c: backup,restore show error messages on new line + writedatabase() delays 3.25 sec to avoid failure in mdisconnect() + * mgrtrans.c,mgrproto.c,mgrproto.h,genproto.h: + reorganized manager protocol, new Mcmdrsp(),Msendblk(),Mrecvblk() + * ic35mgr.txt: described and incorporated basic protocol operations +2001-01-21_21 + * genproto.c,genproto.h,Makefile,README: + new module genproto.{c,h} - general IC35 protocol support + * synproto.c,mgrproto.c: import PDU encode/decode with genproto.h + * syntrans.c,mgrtrans.c: import general welcome() with genproto.h + +2001-01-20_03 IC35SYNC-1-15 + new IC35 manager 'ic35mgr' supports access of IC35 MMCard(s) + - command "mmcdir" lists contents of MMCard(s) + - commands "mmcget","mmcput","mmcdel" read,write,delete MMCard file + - commands "backup","restore" IC35 organizer database + - ic35mgr.txt now really describes the IC35 manager protocol + (previously it was just extracts of Windows 'portmon' logfiles) + bugfixes in ic35sync and vcaconv: + - "ic35sync sync" now updates record if on IC35, else writes new record + - "vcaconv sortvca ic35.vcal" did produce empty output, fixed vobject.c + - fixed vcc.y compile warnings + - "vcaconv sortvca .." improved: shows properties in sort order sequence + todo: + - ic35mgr install application on IC35 + - ic35sync initial sync does not yet write all records to IC35 + workaround: do "ic35sync import .." before first "ic35sync sync .." + * ic35mgr.c: fixed compile warning with unused _not_impl() +2001-01-18_02 + * comio.{c,h},mgrproto.c,mgrtrans.c: + new com_sendw() - send datablock with waiting (for slow MMCard ops) + com_send() does send without delays at full speed + * mgrtrans.c: status() needs 10 msec delay to let IC35 get ready +2001-01-17_05 + * ic35mgr.c,mgrtrans.c: + implemented "restore" IC35 organizer database from file, tested OK +2001-01-16_05 + * ic35mgr.c,mgrtrans.h,mgrtrans.c: + implemented "backup" IC35 organizer database to file, tested OK + * ic35mgr.txt: corrected details IC35 backup: head retry, last ack resp +2001-01-14_23 + * README: IC35 manager modules and usage, short section "programs" +2001-01-14_22 + * comio.c: com_send() using interval timer for delay increased + write MMCard file throughput to 1350 b/s (with 2kB blocks) +2001-01-14_01 + * ic35log.sh: new option "-s n" - prepend output with 1:line 2:time + CAVEAT: "-s n" does not work for "-lsyn2", "-lsyn4" + * Makefile: added missing ic35log,ic35mgr to "all" target +2001-01-13_04 + * comio.c: made "mmcput" faster with 2 stopbits and delay every 29 bytes + improved write MMCard to 890 b/s default 2kB blocks (930 b/s with 5kB) + err.corr com_recv,comsend: handle un-opened com_fd to avoid segfault + RCS branch 1.4.1 with experiments for throughput + RCS branch 1.3.1 with com_simrecv() non-logged bytes (yet erroneous) +2001-01-12_22 + ic35mgr "mmcget", "mmcput", "mmcdel" commands tested OK with IC35 + * ic35mgr.c,mgrtrans.h,mgrtrans.c,mgrproto.h,mgrproto.c: + implemented "mmcput" - write file to IC35 MMCard, tested OK (but slow) + implemented "mmcdel" - delete file on IC35 MMCard, tested OK (fast ;-) + additional 'mode' parameter for mmc_openfile(),sendmcmd(MCMDopenfile,) + * ic35mgr.c: new option "-b size" - read/write blocksize (for tests) + corrected mmcget,mmcput show progress: space to separate errormessage + * mgrproto.c: log checksum error in MPDUsend(),MPDUrecv() for diagnostic + * ic35mgr.txt: MMCard operations for write file, delete file + table of contents, table of MMCard operation functions and codes + moved logfile reference to end, removed hacked log extracts + read MMCard file throughput 2250 b/s with default 5kB blocksize + write MMCard file throughput 590 b/s default 2kB (610 b/s with 5kB) +2001-01-10_02 + * ic35mgr.c: mmcget,mmcput without 'file' arg derive it from ic35path + mmcget converts ic35path lowercase to uppercase, translate '/' to '\' + mmcget shows file transfer progress every 5kB read data +2001-01-09_05 + * ic35mgr.c,mgrtrans.h,mgrtrans.c,mgrproto.h,mgrproto.c: + implemented "mmcget" - read file from IC35 MMCard, tested OK + * mgrproto.c: MPDUrecv() needs receive timeout increased to 2.0 sec +2001-01-08_03 + ic35mgr "mmcdir" corrected and tested OK with IC35 + * ic35mgr.c: mmcdir output to stdout for simpler redirect to file + mmcdir output also MMC file attribute, do not read IC35 status data + * mgrtrans.c: status() must always send '\x50' to init IC35, + status() optionally reads status data, fixed typo mmctstampstr() + * mgrproto.c: reduced MAXRETRY to 5 for MPDUsend(),MPDUrecv() + corrected retry protocol MPDUsend(), 10 sec timeout nn_nn response + err.corr sendmcmd(): puttxt0() append trailing '\0' to dirpath in pdu + * comio.c: delays 100 usec before send every 16th byte (slow MMCard ops) + * comio.c,comio.h: com_settimeout() return previous timeout + * ic35mgr.txt: details about MMCard ops filestatus block from IC35 SDK + read status data from IC35 is optional, initial send 50 is mandatory + negative acknowledge in MMCard operations protocol +2001-01-07_06 + * ic35mgr.c,mgrtrans.h,mgrtrans.c,mgrproto.h,mgrproto.c: + implemented directory listing of MMCard(s), tested OK with simulation + corrected max.length of MMC label to 11 chars (IC35 SDK API Ref.Guide) + * mgrtrans.c: corrected mdisconnect(): send 09 recv 90, send 01 recv 90 + mconnect() reads IC35 status only if statfname specified, log mconnect + problem: MMCard(s) directory listing does not work with IC35, + checksum error / timeout on send command get directory length + uncertain if read IC35 status data needed in ic35mgr.c:ic35mmcdir() +2001-01-04_07 + * ic35mgr.txt: description of MMCard operations and backup,restore + * ic35mgr.c,mgrtrans.h,mgrtrans.c,mgrproto.h,mgrproto.c,Makefile + first version of IC35 Manager, "status" command working with IC35 + * syntrans.c: removed not needed headers ,"dataio.h" +2001-01-01_20 + * ic35mgr.c: framework for (yet non-functional) IC35 Manager commands +2000-12-31_18 + * vcc.y: corrected compile warnings about unused functions + fixed yacc/bison warning: %expect 2 shift/reduce conflicts for "items" + use YYERROR_VERBOSE for parse error reports, disabled useless YYDEBUG + * vobject.c: correct delList(): return removed vobj even if first + * vcaconv.c: improved "sortvca": show properties in sort order sequence + sort todo by due-time,summary, memo by summary,modify-time + compare date+time independant of encoding + * ic35sync.c: corrected "sync": update record if on IC35, else write new + +2000-12-28_04 IC35SYNC-1-14 + ic35sync import,sync step-5 done: "sync" command, misc. improvements + - sync command for synchronize vCard,vCalendar format PIMdata with IC35 + - corrected convert of PIM BDAY birthday to IC35 for yyyy-mm-dd format + - PIM multiple CATEGORIES now preserved by prepend category from IC35, + single or standard of multiple CATEGORIES are replaced + - PIM fullname FN now preserved, IC35 updates first,last name into it + - put first 2 EMAIL entries from vCard into IC35 address Email1,Email2 + - put unspecific telno to IC35 TelWork for "Business", else to TelHome + - map IC35 address "(def.)"s in VCARD.NOTE marked "(def1):","(def2):" + - conversion IC35 to/from PIM record does now yet delete record fields + - extracted data formats from dataio.c into separate modules: + datatxt.c text format (output only) + databin.c binary IC35 record format + datavca.c vCard,vCalendar format + vcutil.c vCard,vCalendar utilities + vcutil.h header for vCard,vCalendar utilities + fixes for gnomecard from gnome-pim_1.2.0: + - compare VCARD.REV against old reference date+time from IC35, because + gnomecard does not maintain X-PILOTSTAT. + - write VCARD.REV:yyyy-mm-ddThh:mm:ss, because yyyymmddThhmmss was + ignored by gnomecard. + fixes for gnomecal from gnome-pim_1.2.0: + - first line of VEVENT.SUMMARY to/from IC35 Summary, remaining lines + to/from Notes, because gnomecal does not support VEVENT.DESCRIPTION + - keep unsupported property VTODO.DTSTART marked in VTODO.DESCRIPTION + - add VEVENT,VTODO.CLASS:PUBLIC, else gnomecal would crash on edit + fixes for korganizer v1.1.1: + - put UID:IC35id-xxxxxxxx to vCard,vCalender record if no UID there. + - produce output vCard,vCal in same CRLF/NL-mode as found on input, + as korganizer has NL-terminated lines in vCalendar. + - translate IC35 CRLF to/from PIM NL in NOTE and DESCRIPTION fields. + - keep unsupported properties VTODO.DTSTART,DUE,CATEGORIES as marked + lines in VTODO.DESCRIPTION + - korganizer supports only DALARM, map IC35 LED and Beep to/from it + vcaconv extensions: + - command "prvca" reports vCard/vCalendar parse error. + - command "sortvca" outputs sorted vCard,vCalendar to standard output. + - command "imphandy" appends vCards with category HANDY from vCard file + to standard output, after import the result back into IC35 they are + for upload to a mobile phone from IC35 addresses in category HANDY. + CAVEAT: + - no options for sync conflict resolve, PIMfile always overrides IC35! + - IC35 has no different times for LED,Beep alarm, DALARM takes priority + - vcaconv conversions do not transfer clean/modified record status + - memory leak in vobject.c: all setVObject_*_Value() do not free the + previously attached dupStr()-allocated string value + * datavca.c,vcutil.{c,h}: support delete empty fields IC35 to/from PIM + * vobject.c: correct delList(), delete (only) first in property list +2000-12-27_21 + * comio.c: for simulate do not set DTR, allow "RDx" in simulation file + * datavca.c,vcutil.c,vcutil.h: + extracted vCard,vCal utilities from datavca.c to vcutil.c,vcutil.h + new clr_vobjdirty(),SetModtimeIfdirty() for vCard,vCal modified time + * vcaconv.c: use vCard,vCal utilities vcutil.c +2000-12-27_01 + * datavca.c: correct vca_set_recid(): update UID if "IC35id-xxxxxxxx" +2000-12-26_04 + * datavca.c: fixes for gnomecal,korganizer + gnomecal does not support VTODO.DTSTART, keep in VTODO.DESCRIPTION + avoid gnomecal crash on edit by add VTODO,VEVENT.CLASS:PUBLIC + add VTODO,VEVENT.CLASS:PUBLIC, else gnomecal crashes on edit + korganizer supports only DALARM, map IC35 LED and Beep to/from it + * dataio.c,dataio.h,databin.c,datatxt.c,datavca.c: + fix open,close logic: new argument mode=r/w/r+ for xxx_open() + * dataio.c: no need to keep filenames +2000-12-25_17 + * ic35sync.c: initial "sync" with empty PIM must export all IC35 recs + * datavca.c: fixes for korganizer and gnomecal + korganizer does not support VTODO.DTSTART,DUE,CATEGORIES, keep them + as marked lines in VTODO.DESCRIPTION + gnomecal does not support VEVENT.DESCRIPTION, keep the first line + of VEVENT.SUMMARY in IC35 S_Summary, more lines in S_Notes + * IC35 address fields with marks "(def1):","(def2):" to/from VCARD.NOTE + * README: added installation, usage, disclaimer sections + * Makefile: added COPYING (GPL) to distribution +2000-12-23_19 + * datavca.c: default CLRF mode for output CRLF-terminated vCard,vCal + unspecific telno to IC35 TelWork for "Business", else to TelHome + replace single or standard of multiple categories, else prepend + new _vca_type() centralizes/simplifies vCard,vCal record type detect + * vcaconv.c: corrected "imphandy" to skip non-digit telno separators +2000-12-23_15 + * dataio.{c,h},Makefile: extracted data formats into separate modules + datatxt.c text format (output only) + databin.c binary IC35 record format + datavca.c vCard,vCalendar format + * fixed problem with korganizer: translate IC35 CRLF to PIM NL in NOTEs + * fixed SIGSEGV in SetCategory() +2000-12-22_01 + * dataio.c,vcc.y,vobject.c,vobject.h: + fixed problem with korganizer, has NL-terminated lines in vCalendar + produce output vCard,vCal in same CRLF/NL-mode as found on input +2000-12-21_22 + * dataio.c: corrected some conversions IC35 to/from PIM + conversion to IC35 of PIM BDAY birthday failed with yyyy-mm-dd format + PIM multiple CATEGORIES now preserved by prepend category from IC35 + PIM fullname FN now preserved, IC35 updated first,last name into it + * vcaconv.c: "sortvca" now sorts VObject property lists to ease tests + corrected "imphandy": created vCards lacked REV revised time +2000-12-21_15 + improved reference date+time from IC35: + * syntrans.{c,h},ic35sync.c: ReadSysInfo() and WriteSysInfo() replace + old get_date_time(),set_date_time(), names like IC35Comm.dll + * dataio.{c,h},ic35sync.c: new set_oldic35dt() and get_newic35dt() + to convert IC35 old/new reference date+time to/from internal + fixes for gnomecard from gnome-pim_1.2.0: + * dataio.c: compare VCARD.REV against old reference date+time from IC35 + because gnomecard does not maintain X-PILOTSTAT + write VCARD.REV:yyyy-mm-ddThh:mm:ss, because yyyymmddThhmmss was + ignored by gnomecard +2000-12-21_03 + * vobject.{c,h},dataio.c: delList(),delProp() do not cleanVObject() + * fixed SEGV: dataio.c:_backup_and_openwr, ic35frec.c:_del_ic35recbuffs + * dataio.c: correct vcard_to_ic35addr(): 1st/2nd EMAIL to A_Email1/2 + * vcaconv.c: new command "sortvca" - sort vCard,vCal file to stdout + new command "imphandy" - import vCards for upload to handy telbook +2000-12-19_02 + * syntrans.c: set_date_time() reports set sysinfo + * ic35frec.c: set_ic35recdata(NULL,0) clears record-,field-buffers + * ic35frec.h: added IC35 record change flag definitions + * dataio.c: use IC35 record change flag definitions + correct vca_open(): handle empty vCal file, report parse error + (e.g. empty vCal file makes "sync" delete all IC35 records!) + ic35xxx_to_vyyy(): put UID to record if not there, korganizer needs + support pim_delrec(), vca_delrec() uses new delProp(),delList() + * vobject.{c,h}: support delete property from vobj, vobj from list + * vcaconv.c: command "prvca" reports vCard/vCal parse error + (cannot use pim_openinp() like other commands, as pim_getrec() + skips the VCALENDER property from vcafile with vCalender only.) + * ic35sync.c: implemented "sync" command as ic35sync(),syncfile() + importfile() collects IC35 actions, report num.of write/update/commit + CAVEAT: no conflict resolve options, PIMfile always overrides IC35! + +2000-12-17_21 IC35SYNC-1-13 not released to public + ic35sync import,sync step-4 done: "import" command, misc. improvements + - algorithm details described in ic35sync.c:ic35import(),ic35export() + - import command for vCard,vCalendar or binary format PIMdata into IC35 + - status command (new) shows IC35 version, sysinfo, total,mod records + - export command improved: + export into existing PIMfile(s), backup e.g. ic35vcal to ic35.vcal~ + map IC35 address fields "(def.)" to/from VCARD:NOTE marked lines + change vCard,vCalendar record modified time only if record changed + support DCREATED in vCalendar: creation time of VEVENT,VTODO record + corrected VEVENT:AALARM,DLARM, were sometimes wrongly created + - support X-PILOTSTAT in vCard,vCal: 0=same, other=changed vs. IC35 + - default format changed to "vca" for primary use vCard,vCalendar + - vcaconv may be used as filter with input/output from stdin/stdout + - fixed QUOTED-PRINTABLE problem with multi-field properties (like + VCARD:N, VCARD:ADR): due to problem with parser vcc.y and for + compatibility with other programs using the vCard,vCal output + QUOTED-PRINTABLE is not used on multi-field properties. + (otherwise the split into field sub-properties fails and the + first field (e.g. VCARD:ADR:STREET) gets the string of ALL the + fields (e.g. STREET,CITY,etc.) with ";" within it.) + todo: sync command, fix memory leak in vobject.c + conversion IC35 to/from PIM record does not yet delete record fields, + i.e. record on destination may differ and have fields not in source. + may be delete field needed only for sync, because import/export are + specified to not delete on destination. + * ic35sync.c,syntrans.c,synproto.c: support new "status" command +2000-12-17_08 + * dataio.c: attach CHARSET to toplevel prop (e.g. VCARD:N, VCARD:ADR) + do not use QUOTED-PRINTABLE on e.g. VCARD:ADR due to parser problem + move IC35 address "(def.)" fields with markers to/from VCARD:NOTE + corrected for "vcaconv bin2txt .." conversion to standard output + corrected StringValue(): handle C and Unicode string values + ignore harmless LED,Beep, Start,EndTime differences (xxx_cmpic35rec) + log pimfile open,close operations + * ic35sync.c: default format now "vca", log ic35sync command line + corrected importfile(): commit was too often (vca) or not done (bin) + CAVEAT: delete field in convert vCard,vcal to/from IC35 not supported, + i.e. record on destination may differ and have fields not in source +2000-12-15_23 + * ic35sync.c: re-designed import, commented import,export algorithms + compare all PIMrecs with IC35 and do commit/update/write to IC35 + do not set sysinfo (date+time) if any IC35 record not in PIMfile + do not commit record on IC35 if not in PIMfile + use new _xxx_ic35recs() for records from IC35 + * vcaconv.c,dataio.c: optionally use stdin/stdout to act as filter + * dataio.c: set vCard,vCal record modified-time only if record changed + added bin_cmpic35rec() - compare record from IC35 with binary record + corrected _ChangeString(): handle C and Unicode string values + fixed memory leak: pim_close() calls inpfmt close to free record list +2000-12-14_23 + * dataio.c: + ignore categoryIDs, repeat fields if no repeat (vca_cmpic35rec) + corrected bin_open(): start with binlist after bin_rewind() + corrected vca_open(): must add all memo records separately to vcalist + corrected VCARD:FN space in fullname only if FirstName AND LastName + new bin_updic35rec() for pim_putrec() with e.g. "vcaconv bin2txt .." + * vcaconv.c: re-designed for generic PIM operations in dataio.c +2000-12-14_00 + * ic35sync.c: corrections and improvements in importfile(): + must always update_frec() owner address data with recid=05000001, + otherwise on IC35 in category "Address", but not as owner data + write back recordID and CLEAN status to PIM only if write to IC35 OK + fix memory leak: release newly created ic35rec + shorten progress,error messages of import and export commands + pim_cmpic35rec() params were swapped, close IC35 file after error + * dataio.{c,h}: prepare for vcaconv with generic operations pim_xxx() + pim_getrec() with fileid FILE_ANY=0 to get next record from any file + pim_putrec() converts inpfmt record to pimfmt and writes to outfile + pim_openinp() opens file for input and sets new inpfmt + pim_openout() opens file for output and sets pimfmt + removed old put_record(),_get_binrec(),_get_vcarec() + CAVEAT memory leak: record list from pim_openinp() not freed +2000-12-12_06 + * dataio.{c,h}: support DCREATED creation time of VEVENT,VTODO record + support X-PILOTSTAT in vCard,vCal: 0=same, other=changed vs. IC35 + generic operations pim_xxx(), use function tables per format + ic35xxx_to_vyyy() update existing instead of create vCard,vCal record + read from existing pimfile(s), so that "ic35sync export" preserves + corrected ic35sched_to_vevent(): create VEVENT:AALARM,DALARM only + if S_AlarmBefore is non-zero, S_Alarm_Repeat may be absent and thus + AlarmNoLED,AlarmNoBeep misleading + * syntrans.{c,h}: readfile() extracted to ic35sync.c:exportfile() + * ic35sync.c: implemented "import" command as ic35import(),importfile() + stub for "sync" command ic35sync(),syncfile(), yet non-functional + CAVEAT: memory leak in vobject.c: all setVObject_*_Value() do not free + previously attached dupStr()-allocated string value + dataio.c lacks some bin_xxx operations for import from binary format + delete PIM-record not supported yet, missing support in vobject.c + +2000-12-07_04 IC35SYNC-1-12 not released to public + ic35sync import,sync step-3 done: convert vCard,vCal to IC35 record + * dataio.c: convert vCard,vCal to IC35 record vxxxx_to_ic35yyyy() + correct ic35addr_to_vcard(): set VCAdrProp if have any address field + * dataio.{c,h}: export vCard,vCal to IC35 conversion as _get_vcarec() + * ic35frec.c: corrected errors found with vCard,vCal to IC35 conversion + * vcaconv.c: support "vca2bin", new "bin2txt" conversions + * ic35sync.c: renamed ic35sync() to ic35export(), as that's what it does + stubs ic35import(), ic35sync() for un-implemented functions + connect() argument rdtime to retrieve value from IC35 +2000-12-03_23 + * ic35sync.txt: marked uncertain topics with "???" + experiments found new commands: read record by record-ID (01 05) + and write record data update (01 09), keeps recID on IC35 unchanged + * syntrans.{c,h},synproto.{c,h}: implemented new commands + read record by record-ID and write updated record data + * ic35log.sh: support new commands readrecrid,updatrec +2000-12-02_07 + ic35sync import,sync step-2 done: + - new protocol functions for import,sync tested OK + - new knowledge about import,sync from experiments + todo: vCard,vCal to IC35 record, commands import,sync + * synproto.c: corrected CMDfgetmlen, CMDcategory, CMDsetdtime + * syntrans.{c,h}: connect() argument rdtime to retrieve value from IC35 + * vcaconv.c,dataio.c: handle binfile records without data + * ic35log.sh: process ic35sync logfiles, fragmented response readmodrec + * ic35sync.txt: new knowledge about open file, changeflag, write record + first all read then write,del,commit, "date+time" may be any string + +2000-12-01_20 IC35SYNC-1-11 not released to public + intermediate freeze after ic35sync import,sync step-1 done: + - new functions: set_date_time, category, get_mod_flen, read_mod_frec, + write_frec, delete_frec, commit_frec + - IC35 record access functions ic35frec.{c,h} + - 4byte record-ID combined from file-id (MSB) and record-id (LSW) + - support last-modified-time for vCard,vCalendar + - protocol-analyser ic35log.sh replaces and extends ic35prot.awk + todo: test new funcs, vCard,vCal to IC35 record, commands import,sync + * dataio.c: use 4byte record-ID in binary format + * ic35sync.c: IC35 file descriptions now from "ic35frec.h" +2000-12-01_19 + * ic35sync.txt,ic35frec.h: comment max.lengths of record fields + * dataio.c,syntrans.c,ic35frec.{c,h}: + hide ic35 file description internals, use acces functions + * ic35frec.c: truncate to max.field-length, set nflds from recID + added missing 1byte-fields T_Completed,T_Priority + * vcaconv.c: usage on "-h", report unknown conversion +2000-12-01_02 + * ic35frec.{c,h}: IC35 record access and file descriptions + * dataio.{c,h},syntrans.{c,h},vcaconv.c: + adapted for IC35 record access functions and 4byte record-ID + * synproto.c: adapted for 4byte record-ID combined of fileid,recid + * Makefile: new module ic35frec.{c,h}, ic35log replaces ic35prot.awk + target "clean" also removes .depend file +2000-11-29_21 + * syntrans.{c,h}: missed commit_frec() for import,sync (yet untested) +2000-11-28_04 + * dataio.c: support last-modified-time for vCard,vCalendar + concatenate IC35 file-id and record-id for XPilotIdProp + export ic35_dtime() for use in syntrans.c:set_date_time() + * syntrans.c: do disconnect on welcome() error to avoid hang IC35 + * syntrans.{c,h}: functions for import,sync (untested): set_date_time, + category, get_mod_flen, read_mod_frec, write_frec, delete_frec + * synproto.{c,h}: import,sync commands: CMD{fdelrec,fgetmlen,fgetmrec} +2000-11-27_15 + * ic35sync.txt: record-id, file-id, get number of modified records + new commands: read next modified record, delete record + updated correspondance with IC35Comm.dll to current knowledge + comment logs Delete.tar.gz:*.log, how M.Bruennert made them + * ic35log.sh: replaces ic35prot.awk, ic35prot2.awk + ic35prot.awk,ic35prot2.awk: moved to Attic/ +2000-11-27_01 + * ic35prot.awk: err.corr prxdata(), _plen was undefined if skip < 3 +2000-11-25_18 + * ic35prot2.awk: cosmetics, comment IC35Comm.dll functions +2000-11-25_03 + * ic35prot2.awk: decode record field contents, comment variables +2000-11-24_04 + * ic35prot.awk: prepend data with WRx,RDx (hex) WRa,RDa (ASCII) + skip also checksum if Level-1 headers are skipped with "-vskip=3" + * ic35prot2.awk: decode IC35sync PDUs from WRx,RDx transmission logs + from 'portmon' processed with 'awk -f ic35prot.awk -vskip=3' + +2000-11-22_08 IC35SYNC-1-10 + * ic35sync supports output IC35 PIM data in vCard,vCalendar format + new option "-f format" and command,pimfile args replace "-o outfile" + write date+time to logfile + * restrictions of implemented vCard,vCalendar output: + - strings with non-printable chars get "QUOTED-PRINTABLE" property + uncertain if it should be "ENCODING=QUOTED-PRINTABLE" + - strings with 8bit-chars also get "CHARSET=ISO-8859-1" property + uncertain if this is compliant to standard, because vobject.h + does not contain "CHARSET", but both "vCard Version 2.1" and + "vCalendar Version 1.0" explicitely allow "Character Set" + - IC35 "Memo" has no standard vCard,vCalendar format representation + using proprietary extension of VMEMO records + * vcc.y,vobject.{c,h}: extended by non-standard "VMEMO" records + for IC35 "Memo" representation + * dataio.c: VEVENT recurrence rules and alarms + * ic35sync.txt: corrected LED-,Beep-alarmflags +2000-11-21_04 + * moved IC35 file descriptions from syntrans.* to dataio.* + to avoid vcaconv link needing syntrans.o,synproto.o etc. + * dataio.c: put_record() supports output vCard,vCalendar output +2000-11-20_02 + * added vCard,vCalendar API: vcc.h vcc.y vobject.h vobject.c port.h + from gnome-pim-1.2.0.tar.gz:gnome-pim-1.2.0/libversit/ + see ftp://ftp.gnome.org/pub/GNOME/stable/sources/gnome-pim/ + other sources for vCard,vCalendar API: + - kpilot_3.1.10-1.0.tar.gz (based on KPilot 3.1b9) + from ftp://kde.tdyc.com/pub/kde/debian/dists/potato/contrib/source/ + see also http://www.slac.com/pilone/kpilot_home/index.html + - versit consortium' SDK for Windows DLLs + from http://www.imc.org/pdi/sdkdllsr.zip + * new vcaconv.c vCard,vCalendar conversions, e.g. for offline tests + with IC35 binary data + * restrictions of implemented vCard,vCalendar output: + - sometimes non-7bit-ASCII chars lack QuotedPrintable and CharSet + - VEVENT recurrence rules not implemented + - VEVENT alarms AALARM,DALARM not implemented + - IC35 "Memo" has no standard vCard,vCalendar format representation +2000-11-19_07 + * Makefile: ask before overwrite distribution tar.gz + * ic35sync.c: write date+time to logfile; replaced "-o outfile" + with new option "-f format" and command,pimfile arguments + * syntrans.c: reset serial device on abort with Ctrl-C + * comio.c: correct comrecv(): fdsets undefined if select() returns -1 + * avoid gcc warnings about rcsid[]s not used + +2000-11-19_00 IC35SYNC-1-9 + added README, ic35sync.txt,ic35mgr.txt, ic35prot.awk + * Makefile: integrated help, target for tar.gz distribution + * syntrans.{c,h},ic35sync.c: export connect(), details internal + abort on bad power and wrong password responses + check for ready-reponse '\x80' from IC35 + * ic35sync.c: write ic35sync version/rcsid to logfile + * util.{c,h}: log_init() interface changed (loglevel argument) + * ic35sync.txt: mention logfiles used for protocol analysis + +2000-11-07_03 IC35SYNC-1-8 + ic35sync.c split into modules + +2000-11-05_05 IC35SYNC-1-7 + version 1.7 released to Michael Bruennert + read PIM data from IC35 tested OK + CAVEAT: + - PIM data export in "home grown" format not compatible to anything + - cannot write PIM data to IC35, neither synchronize + - unsupported commands "set date+time", "category", "reset chgflag" + - ugly one-module-source +2000-11-04_19 ic35sync.c 1.1 + hacking finished, now for compile and tests .. +2000-10-31 + start hacking code diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..8865734 --- /dev/null +++ b/INSTALL @@ -0,0 +1,368 @@ +Installation Instructions +************************* + + Copyright (C) 1994-1996, 1999-2002, 2004-2016 Free Software +Foundation, Inc. + + 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 warranty of any kind. + +Basic Installation +================== + + Briefly, the shell command './configure && make && make install' +should configure, build, and install this package. The following +more-detailed instructions are generic; see the 'README' file for +instructions specific to this package. Some packages provide this +'INSTALL' file but do not implement all of the features documented +below. The lack of an optional feature in a given package is not +necessarily a bug. More recommendations for GNU packages can be found +in *note Makefile Conventions: (standards)Makefile Conventions. + + The 'configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a 'Makefile' in each directory of the package. +It may also create one or more '.h' files containing system-dependent +definitions. Finally, it creates a shell script 'config.status' that +you can run in the future to recreate the current configuration, and a +file 'config.log' containing compiler output (useful mainly for +debugging 'configure'). + + It can also use an optional file (typically called 'config.cache' and +enabled with '--cache-file=config.cache' or simply '-C') that saves the +results of its tests to speed up reconfiguring. Caching is disabled by +default to prevent problems with accidental use of stale cache files. + + If you need to do unusual things to compile the package, please try +to figure out how 'configure' could check whether to do them, and mail +diffs or instructions to the address given in the 'README' so they can +be considered for the next release. If you are using the cache, and at +some point 'config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file 'configure.ac' (or 'configure.in') is used to create +'configure' by a program called 'autoconf'. You need 'configure.ac' if +you want to change it or regenerate 'configure' using a newer version of +'autoconf'. + + The simplest way to compile this package is: + + 1. 'cd' to the directory containing the package's source code and type + './configure' to configure the package for your system. + + Running 'configure' might take a while. While running, it prints + some messages telling which features it is checking for. + + 2. Type 'make' to compile the package. + + 3. Optionally, type 'make check' to run any self-tests that come with + the package, generally using the just-built uninstalled binaries. + + 4. Type 'make install' to install the programs and any data files and + documentation. When installing into a prefix owned by root, it is + recommended that the package be configured and built as a regular + user, and only the 'make install' phase executed with root + privileges. + + 5. Optionally, type 'make installcheck' to repeat any self-tests, but + this time using the binaries in their final installed location. + This target does not install anything. Running this target as a + regular user, particularly if the prior 'make install' required + root privileges, verifies that the installation completed + correctly. + + 6. You can remove the program binaries and object files from the + source code directory by typing 'make clean'. To also remove the + files that 'configure' created (so you can compile the package for + a different kind of computer), type 'make distclean'. There is + also a 'make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + + 7. Often, you can also type 'make uninstall' to remove the installed + files again. In practice, not all packages have tested that + uninstallation works correctly, even though it is required by the + GNU Coding Standards. + + 8. Some packages, particularly those that use Automake, provide 'make + distcheck', which can by used by developers to test that all other + targets like 'make install' and 'make uninstall' work correctly. + This target is generally not run by end users. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the 'configure' script does not know about. Run './configure --help' +for details on some of the pertinent environment variables. + + You can give 'configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here is +an example: + + ./configure CC=c99 CFLAGS=-g LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you can use GNU 'make'. 'cd' to the +directory where you want the object files and executables to go and run +the 'configure' script. 'configure' automatically checks for the source +code in the directory that 'configure' is in and in '..'. This is known +as a "VPATH" build. + + With a non-GNU 'make', it is safer to compile the package for one +architecture at a time in the source code directory. After you have +installed the package for one architecture, use 'make distclean' before +reconfiguring for another architecture. + + On MacOS X 10.5 and later systems, you can create libraries and +executables that work on multiple system types--known as "fat" or +"universal" binaries--by specifying multiple '-arch' options to the +compiler but only a single '-arch' option to the preprocessor. Like +this: + + ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CPP="gcc -E" CXXCPP="g++ -E" + + This is not guaranteed to produce working output in all cases, you +may have to build one architecture at a time and combine the results +using the 'lipo' tool if you have problems. + +Installation Names +================== + + By default, 'make install' installs the package's commands under +'/usr/local/bin', include files under '/usr/local/include', etc. You +can specify an installation prefix other than '/usr/local' by giving +'configure' the option '--prefix=PREFIX', where PREFIX must be an +absolute file name. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +pass the option '--exec-prefix=PREFIX' to 'configure', the package uses +PREFIX as the prefix for installing programs and libraries. +Documentation and other data files still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like '--bindir=DIR' to specify different values for particular +kinds of files. Run 'configure --help' for a list of the directories +you can set and what kinds of files go in them. In general, the default +for these options is expressed in terms of '${prefix}', so that +specifying just '--prefix' will affect all of the other directory +specifications that were not explicitly provided. + + The most portable way to affect installation locations is to pass the +correct locations to 'configure'; however, many packages provide one or +both of the following shortcuts of passing variable assignments to the +'make install' command line to change installation locations without +having to reconfigure or recompile. + + The first method involves providing an override variable for each +affected directory. For example, 'make install +prefix=/alternate/directory' will choose an alternate location for all +directory configuration variables that were expressed in terms of +'${prefix}'. Any directories that were specified during 'configure', +but not in terms of '${prefix}', must each be overridden at install time +for the entire installation to be relocated. The approach of makefile +variable overrides for each directory variable is required by the GNU +Coding Standards, and ideally causes no recompilation. However, some +platforms have known limitations with the semantics of shared libraries +that end up requiring recompilation when using this method, particularly +noticeable in packages that use GNU Libtool. + + The second method involves providing the 'DESTDIR' variable. For +example, 'make install DESTDIR=/alternate/directory' will prepend +'/alternate/directory' before all installation names. The approach of +'DESTDIR' overrides is not required by the GNU Coding Standards, and +does not work on platforms that have drive letters. On the other hand, +it does better at avoiding recompilation issues, and works well even +when some directory options were not specified in terms of '${prefix}' +at 'configure' time. + +Optional Features +================= + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving 'configure' the +option '--program-prefix=PREFIX' or '--program-suffix=SUFFIX'. + + Some packages pay attention to '--enable-FEATURE' options to +'configure', where FEATURE indicates an optional part of the package. +They may also pay attention to '--with-PACKAGE' options, where PACKAGE +is something like 'gnu-as' or 'x' (for the X Window System). The +'README' should mention any '--enable-' and '--with-' options that the +package recognizes. + + For packages that use the X Window System, 'configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the 'configure' options '--x-includes=DIR' and +'--x-libraries=DIR' to specify their locations. + + Some packages offer the ability to configure how verbose the +execution of 'make' will be. For these packages, running './configure +--enable-silent-rules' sets the default to minimal output, which can be +overridden with 'make V=1'; while running './configure +--disable-silent-rules' sets the default to verbose, which can be +overridden with 'make V=0'. + +Particular systems +================== + + On HP-UX, the default C compiler is not ANSI C compatible. If GNU CC +is not installed, it is recommended to use the following options in +order to use an ANSI C compiler: + + ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" + +and if that doesn't work, install pre-built binaries of GCC for HP-UX. + + HP-UX 'make' updates targets which have the same time stamps as their +prerequisites, which makes it generally unusable when shipped generated +files such as 'configure' are involved. Use GNU 'make' instead. + + On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot +parse its '' header file. The option '-nodtk' can be used as a +workaround. If GNU CC is not installed, it is therefore recommended to +try + + ./configure CC="cc" + +and if that doesn't work, try + + ./configure CC="cc -nodtk" + + On Solaris, don't put '/usr/ucb' early in your 'PATH'. This +directory contains several dysfunctional programs; working variants of +these programs are available in '/usr/bin'. So, if you need '/usr/ucb' +in your 'PATH', put it _after_ '/usr/bin'. + + On Haiku, software installed for all users goes in '/boot/common', +not '/usr/local'. It is recommended to use the following options: + + ./configure --prefix=/boot/common + +Specifying the System Type +========================== + + There may be some features 'configure' cannot figure out +automatically, but needs to determine by the type of machine the package +will run on. Usually, assuming the package is built to be run on the +_same_ architectures, 'configure' can figure that out, but if it prints +a message saying it cannot guess the machine type, give it the +'--build=TYPE' option. TYPE can either be a short name for the system +type, such as 'sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS + KERNEL-OS + + See the file 'config.sub' for the possible values of each field. If +'config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the option '--target=TYPE' to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with '--host=TYPE'. + +Sharing Defaults +================ + + If you want to set default values for 'configure' scripts to share, +you can create a site shell script called 'config.site' that gives +default values for variables like 'CC', 'cache_file', and 'prefix'. +'configure' looks for 'PREFIX/share/config.site' if it exists, then +'PREFIX/etc/config.site' if it exists. Or, you can set the +'CONFIG_SITE' environment variable to the location of the site script. +A warning: not all 'configure' scripts look for a site script. + +Defining Variables +================== + + Variables not defined in a site shell script can be set in the +environment passed to 'configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the 'configure' command line, using 'VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +causes the specified 'gcc' to be used as the C compiler (unless it is +overridden in the site shell script). + +Unfortunately, this technique does not work for 'CONFIG_SHELL' due to an +Autoconf limitation. Until the limitation is lifted, you can use this +workaround: + + CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash + +'configure' Invocation +====================== + + 'configure' recognizes the following options to control how it +operates. + +'--help' +'-h' + Print a summary of all of the options to 'configure', and exit. + +'--help=short' +'--help=recursive' + Print a summary of the options unique to this package's + 'configure', and exit. The 'short' variant lists options used only + in the top level, while the 'recursive' variant lists options also + present in any nested packages. + +'--version' +'-V' + Print the version of Autoconf used to generate the 'configure' + script, and exit. + +'--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally 'config.cache'. FILE defaults to '/dev/null' to + disable caching. + +'--config-cache' +'-C' + Alias for '--cache-file=config.cache'. + +'--quiet' +'--silent' +'-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to '/dev/null' (any error + messages will still be shown). + +'--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + 'configure' can determine that directory automatically. + +'--prefix=DIR' + Use DIR as the installation prefix. *note Installation Names:: for + more details, including other options available for fine-tuning the + installation locations. + +'--no-create' +'-n' + Run the configure checks, but stop before creating any output + files. + +'configure' also accepts some other, not widely useful, options. Run +'configure --help' for more details. diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..8d4deaa --- /dev/null +++ b/Makefile.am @@ -0,0 +1,21 @@ +## Process this file with automake to produce Makefile.in +## $Id: Makefile.am,v 1.4 2001/08/12 03:29:57 thosch Rel $ +## Copyright (C) 2001 Thomas Schulz +## +## automakefile for ic35link + +pkgdata_DATA = \ + README \ + COPYING + +EXTRA_DIST = $(pkgdata_DATA) + +SUBDIRS = doc src + +if MAINTAINER_MODE +MAINTAINERCLEANFILES = \ + aclocal.m4 install-sh missing mkinstalldirs INSTALL \ + Makefile.in stamp-h.in config.h.in configure +else +MAINTAINERCLEANFILES = +endif diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..af6cc44 --- /dev/null +++ b/NEWS @@ -0,0 +1,248 @@ + $Id: NEWS,v 1.18 2001/08/12 03:39:35 thosch Rel $ + NEWS: release info about IC35Link + ================================= + +2001-08-12 version 1.18 IC35LINK-1-18 + long pending release with several improvements and corrections + according to suggestions and bug reports by users. + improvements: + - improved README: table of contents, restrictions, more explanations + - ic35mgr restore and MMC write 30% faster (proposal by Harald Becker) + - ic35sync, ic35mgr, vcaconv now support long options, e.g. "--help" + info enquiry with "--help" or "--version" now returns exitcode 0 + - new autogen.sh script for generate configure etc. after CVS checkout + (thanks Konrad Mader for report about missing configure in CVS) + - new configure option "--disable-logandcomsim" for conditional compile + without logging and com-simulation (proposal by Harald Becker) + - new configure options "--enable-ansi" and "--enable-pedantic" + bugfixes: + - fixed problem where ic35sync with many (ca. 860) addresses failed + (thanks Konrad Mader for bug report "get_mod_flen failed") + - ic35sync now accepts also empty PIMfile(s), e.g. empty ic35.memo + (thanks Malte Schmidt for bug report "ic35.memo: parse error") + - ic35mgr MMCard functions now check ic35path before accessing IC35 + e.g. warn if shell eat backslash (\) in unquoted MMCard1\test.txt + (thanks Thomas Lichtenberg for bug report) + - restricted ic35mgr to max.blocksize 16350 found by experiments + - ic35sync, ic35mgr now abort on open failure of logfile or simfile + - fixed com-simulation for adjacent "RD nn" lines and non-logged bytes + - fixed compile problem util.c with Linux Mandrake-8.0 and SuSE-7.2 + (thanks Christian Theile, Karl Stroetmann for bug reports) + - corrected warnings from "--enable-ansi" and "--missing-prototypes" + - fixed all Makefile.am: maintainer-clean keeps only sources in CVS, + install also README and COPYING, clean dependencies and versinfo.c + +2001-02-05 version 1.17 IC35LINK-1-17 + package renamed to "ic35link" and automake/autoconf/CVS reorganization. + no changes in sources, programs are unchanged except for version. + sources,documents now reside in src,doc subdirectories respectively. + +2001-02-04 version 1.16 IC35SYNC-1-16 + this is a bugfix and redesign release and is the last named "ic35sync". + (the package will be renamed to "ic35link" in the next release, when + the reorganizations for automake/autoconf and public CVS are done.) + improvements and redesign: + - ic35sync,ic35mgr,vcaconv now report package version and build info + - distribution includes vcc.c to handle missing/bad 'bison' or 'yacc' + (e.g. SuSE-7.0's yacc produced bad vcc.o, thanks Michael Bruennert) + - reworked and clearified the IC35 manager protocol documentation and + implementation (see ic35mgr.txt, mgrtrans.*, mgrproto.*), refer to + the IC35 SoftwareDevelopmentKit API MMCard functions in mgrproto.c + bugfixes in ic35mgr and ic35sync: + - fixed compile error RedHat-7.0 gcc-2.96 (thanks Dieter Schultschik) + - corrections for 'ic35mgr status' (thanks Michael Bruennert): + mgrtrans.c:status() tries to get IC35 response instead of only wait + mgrproto.c:MMCsend() sends blocklength with initial wait + corrected output of MMCard2 label + - ic35mgr commands backup and restore show error messages on new line + - avoid failing 'ic35mgr restore ..' at "disconnect" by delay 3.25 sec + after writedatabase() done + - avoid failure of 'ic35mgr mmcput file' with occasional slow IC35 + MMCard write with 3.5 sec timeout for length of response block + - corrected MMC closedir,closefile response PDUs having no status-word + - corrected to make 'ic35sync status' work also with simulation file + +2001-01-20 version 1.15 IC35SYNC-1-15 + new IC35 manager 'ic35mgr' supports access of IC35 MMCard(s) + - command "mmcdir" lists contents of MMCard(s) + - commands "mmcget","mmcput","mmcdel" read,write,delete MMCard file + - commands "backup","restore" IC35 organizer database + - ic35mgr.txt now really describes the IC35 manager protocol + (previously it was just extracts of Windows 'portmon' logfiles) + bugfixes in ic35sync and vcaconv: + - "ic35sync sync" now updates record if on IC35, else writes new record + - "vcaconv sortvca ic35.vcal" did produce empty output, fixed vobject.c + - fixed vcc.y compile warnings + - "vcaconv sortvca .." improved: shows properties in sort order sequence + todo: + - ic35mgr install application on IC35 + - ic35sync initial sync does not yet write all records to IC35 + workaround: do "ic35sync import .." before first "ic35sync sync .." + +2000-12-28 version 1.14 IC35SYNC-1-14 + ic35sync import,sync step-5 done: "sync" command, misc. improvements + - sync command for synchronize vCard,vCalendar format PIMdata with IC35 + - corrected convert of PIM BDAY birthday to IC35 for yyyy-mm-dd format + - PIM multiple CATEGORIES now preserved by prepend category from IC35, + single or standard of multiple CATEGORIES are replaced + - PIM fullname FN now preserved, IC35 updates first,last name into it + - put first 2 EMAIL entries from vCard into IC35 address Email1,Email2 + - put unspecific telno to IC35 TelWork for "Business", else to TelHome + - map IC35 address "(def.)"s in VCARD.NOTE marked "(def1):","(def2):" + - conversion IC35 to/from PIM record does now yet delete record fields + - extracted data formats from dataio.c into separate modules: + datatxt.c text format (output only) + databin.c binary IC35 record format + datavca.c vCard,vCalendar format + vcutil.c vCard,vCalendar utilities + vcutil.h header for vCard,vCalendar utilities + fixes for gnomecard from gnome-pim_1.2.0: + - compare VCARD.REV against old reference date+time from IC35, because + gnomecard does not maintain X-PILOTSTAT. + - write VCARD.REV:yyyy-mm-ddThh:mm:ss, because yyyymmddThhmmss was + ignored by gnomecard. + fixes for gnomecal from gnome-pim_1.2.0: + - first line of VEVENT.SUMMARY to/from IC35 Summary, remaining lines + to/from Notes, because gnomecal does not support VEVENT.DESCRIPTION + - keep unsupported property VTODO.DSTART marked in VTODO.DESCRIPTION + - add VEVENT,VTODO.CLASS:PUBLIC, else gnomecal would crash on edit + fixes for korganizer v1.1.1: + - put UID:IC35id-xxxxxxxx to vCard,vCalender record if no UID there. + - produce output vCard,vCal in same CRLF/NL-mode as found on input, + as korganizer has NL-terminated lines in vCalendar. + - translate IC35 CRLF to/from PIM NL in NOTE and DESCRIPTION fields. + - keep unsupported properties VTODO.DSTART,DUE,CATEGORIES as marked + lines in VTODO.DESCRIPTION + - korganizer supports only DALARM, map IC35 LED and Beep to/from it + vcaconv extensions: + - command "prvca" reports vCard/vCalendar parse error. + - command "sortvca" outputs sorted vCard,vCalendar to standard output. + - command "imphandy" appends vCards with category HANDY from vCard file + to standard output, after import the result back into IC35 they are + for upload to a mobile phone from IC35 addresses in category HANDY. + CAVEAT: + - no options for sync conflict resolve, PIMfile always overrides IC35! + - IC35 has no different times for LED,Beep alarm, DALARM takes priority + - vcaconv conversions do not transfer clean/modified record status + - memory leak in vobject.c: all setVObject_*_Value() do not free the + previously attached dupStr()-allocated string value + +2000-12-17 version 1.13 IC35SYNC-1-13 (not released to public) + ic35sync import,sync step-4 done: "import" command, misc. improvements + - algorithm details described in ic35sync.c:ic35import(),ic35export() + - import command for vCard,vCalendar or binary format PIMdata into IC35 + - status command (new) shows IC35 version, sysinfo, total,mod records + - export command improved: + export into existing PIMfile(s), backup e.g. ic35vcal to ic35.vcal~ + map IC35 address fields "(def.)" to/from VCARD:NOTE marked lines + change vCard,vCalendar record modified time only if record changed + support DCREATED in vCalendar: creation time of VEVENT,VTODO record + corrected VEVENT:AALARM,DLARM, were sometimes wrongly created + - support X-PILOTSTAT in vCard,vCal: 0=same, other=changed vs. IC35 + - default format changed to "vca" for primary use vCard,vCalendar + - vcaconv may be used as filter with input/output from stdin/stdout + - fixed QUOTED-PRINTABLE problem with multi-field properties (like + VCARD:N, VCARD:ADR): due to problem with parser vcc.y and for + compatibility with other programs using the vCard,vCal output + QUOTED-PRINTABLE is not used on multi-field properties. + (otherwise the split into field sub-properties fails and the + first field (e.g. VCARD:ADR:STREET) gets the string of ALL the + fields (e.g. STREET,CITY,etc.) with ";" within it.) + todo: sync command, fix memory leak in vobject.c + conversion IC35 to/from PIM record does not yet delete record fields, + i.e. record on destination may differ and have fields not in source. + may be delete field needed only for sync, because import/export are + specified to not delete on destination. + +2000-12-07 version 1.12 IC35SYNC-1-12 (not released to public) + ic35sync import,sync step-3 done: + convert vCard,vCalendar format to IC35 record + ic35sync import,sync step-2 done: + new protocol functions for import,sync tested OK + new knowledge about import,sync from experiments + new features: + - vcaconv supports "vca2bin", new "bin2txt" conversions + - ic35log.sh supports new commands readrecrid,updatrec + processes ic35sync logfiles, fragmented response readmodrec + - syntrans.{c,h}: connect() argument rdtime to retrieve value from IC35 + corrections: + - corrected ic35addr_to_vcard(): set VCAdrProp if have any address field + - vcaconv.c,dataio.c: handle binfile records without data + - synproto.c: corrected CMDfgetmlen, CMDcategory, CMDsetdtime + documentation ic35sync.txt: + - marked uncertain topics with "???" + - experiments found new commands: read record by record-ID (01 05) and + write record data update (01 09), keeps recID on IC35 unchanged + - new knowledge about open file, changeflag, write record + first all read then write,del,commit, "date+time" may be any string + todo: ic35sync commands import,sync + +2000-12-01 version 1.11 IC35SYNC-1-11 (not released to public) + intermediate freeze after ic35sync import,sync step-1 done: + - new functions: set_date_time, category, get_mod_flen, read_mod_frec, + write_frec, delete_frec, commit_frec + - IC35 record access functions ic35frec.{c,h} + - 4byte record-ID combined from file-id (MSB) and record-id (LSW) + - support last-modified-time for vCard,vCalendar + - protocol-analyser ic35log.sh replaces and extends ic35prot.awk + todo: test new funcs, vCard,vCal to IC35 record, commands import,sync + +2000-11-22 version 1.10 IC35SYNC-1-10 + - ic35sync supports output IC35 PIM data in vCard,vCalendar format + new option "-f format" and command,pimfile args replace "-o outfile" + write date+time to logfile + - added vCard,vCalendar API: vcc.h vcc.y vobject.h vobject.c port.h + from gnome-pim-1.2.0.tar.gz:gnome-pim-1.2.0/libversit/ + see ftp://ftp.gnome.org/pub/GNOME/stable/sources/gnome-pim/ + other sources for vCard,vCalendar API: + - kpilot_3.1.10-1.0.tar.gz (based on KPilot 3.1b9) + from ftp://kde.tdyc.com/pub/kde/debian/dists/potato/contrib/source/ + see also http://www.slac.com/pilone/kpilot_home/index.html + - versit consortium' SDK for Windows DLLs + from http://www.imc.org/pdi/sdkdllsr.zip + - restrictions of implemented vCard,vCalendar output: + - strings with non-printable chars get "QUOTED-PRINTABLE" property + uncertain if it should be "ENCODING=QUOTED-PRINTABLE" + - strings with 8bit-chars also get "CHARSET=ISO-8859-1" property + uncertain if this is compliant to standard, because vobject.h + does not contain "CHARSET", but both "vCard Version 2.1" and + "vCalendar Version 1.0" explicitely allow "Character Set" + - IC35 "Memo" has no standard vCard,vCalendar format representation + using proprietary extension of VMEMO records + - new vcaconv: vCard,vCalendar conversions, e.g. for offline tests + with IC35 binary data + moved IC35 file descriptions from syntrans.* to dataio.* + to avoid vcaconv link needing syntrans.o,synproto.o etc. + - ic35sync.txt: corrected LED-,Beep-alarmflags + - Makefile: ask before overwrite distribution tar.gz + - syntrans.c: reset serial device on abort with Ctrl-C + - comio.c: correct comrecv(): fdsets undefined if select() returns -1 + - avoid gcc warnings about rcsid[]s not used + +2000-11-19 version 1.9 IC35SYNC-1-9 (not released to public) + - added README, ic35sync.txt,ic35mgr.txt, ic35prot.awk + - Makefile: integrated help, target for tar.gz distribution + - syntrans.{c,h},ic35sync.c: export connect(), details internal + abort on bad power and wrong password responses + check for ready-reponse '\x80' from IC35 + - ic35sync.c: write ic35sync version/rcsid to logfile + - util.{c,h}: log_init() interface changed (loglevel argument) + - ic35sync.txt: mention logfiles used for protocol analysis + +2000-11-07 version 1.8 IC35SYNC-1-8 (not released to public) + ic35sync.c split into modules + +2000-11-05 version 1.7 IC35SYNC-1-7 + first usable version, read PIM data from IC35 tested OK. + CAVEAT: + - PIM data export in "home grown" format not compatible to anything + - cannot write PIM data to IC35, neither synchronize + - unsupported commands "set date+time", "category", "reset chgflag" + - ugly one-module-source + +2000-11-04 version 1.1 + hacking finished, now for compile and tests .. + +2000-10-31 + start hacking code + diff --git a/README b/README new file mode 100644 index 0000000..7794708 --- /dev/null +++ b/README @@ -0,0 +1,417 @@ + $Id: README,v 1.19 2001/08/12 03:36:44 thosch Rel $ + IC35Link for Linux + ================== + + IC35Link provides tools to communicate with the Siemens IC35 + over a serial port. + +Contents + Abstract + Installation + configure, make, install + create configure script + Usage of ic35sync + Examples + Restrictions + Usage of ic35mgr + Restrictions + Files in the distribution + Documents + Miscellaneous + Tools + Sources in the src directory + References + Standard Disclaimer, Warnings, Copyright + +Abstract +-------- + The tools included in the IC35Link package are: + - ic35sync + synchronizes IC35 address, schedule, todo and memo data with + Personal Information Management databases (PIMfiles). + - ic35mgr + The IC35 manager is for accessing IC35 MMCard(s), backup,restore + the IC35 PIM-database and install applications on the IC35. + CAVEAT: install applications on the IC35 is (as of version 1.17) + not yet implemented. + - vcaconv + is a converter for PIMfiles in vCard,vCalendar and binary formats. + it was mainly used during development. For non-developers the + "imphandy" conversion might be useful to manage the phonebook of + a mobile phone with the IC35: it converts vCards into the "HANDY" + category for uploading them from this category on the IC35 to the + phonebook of the mobile phone (after import or sync with IC35). + - ic35log + is for pretty-printing ic35sync, ic35mgr (and 'portmon') logfiles + and useful mainly for developers. + - ic35sync.txt + describes the IC35 synchronization protocol (in german). + - ic35mgr.txt + describes the IC35 manager protocols (in german). + All programs give help about usage if called with the short "-h" or + the long "--help" option. + + Starting with version 1.17 the package is named "ic35link", because + it provides more than just IC35 synchronization under GNU/Linux. + Up to version 1.16 the package name was "ic35sync". + +Installation +------------ + Unpack the archive ic35link-*.tar.gz (you appeareantly did that ;-). + configure, make, install + Do the usual + ./configure + make + su # superuser login for install + make install-strip # strip symbols and debugging info + exit # superuser logout + Installation is done to /usr/local/bin and /usr/local/share/ic35link + by default, change that with 'configure --prefix=/some/other/path'. + For details run 'configure --help' and see the file INSTALL. + The 'configure' options of major interest are: + --prefix=/opt + install to /opt/bin/ instead of default /usr/local/bin/ + --disable-logandcomsim + compile without logging and simulated communication support + --disable-warnings + disable compile warnings + --enable-ansi + enable strict ANSI-C compilation + --enable-pedantic + enable pedantic compile warnings + be warned to get several compile errors and/or warnings + feel free to fix them (and send me the patches :-) + * The programs are compiled with debugging info. Should you want + to install them with that, use 'make install' instead of the + 'make install-strip' above. + * The programs and documents can be uninstalled with 'make uninstall'. + * If you modify the vCard/vCalendar parser source vcc.y, you will + need 'bison' version 1.25 or newer. Compilation with 'yacc' will + not work and/or create a vcc.c producing 'parser stack overflow' + errors. + create configure script + If you use an archive ic35link-*.tar.gz you should not need this. + If you obtained ic35link from the public CVS repository you have + to generate the configure script and Makefile.in with + sh autogen.sh + It will check for autoconf and automake, you'll have to install + these packages if some of the checks fail. + If all goes well it will automatically run './configure' with the + options passed to autogen.sh and "--enable-maintainer-mode". The + latter enables 'make maintainer-clean', which deletes everything + not in the CVS repository, e.g. also the configure script. + Setting the environment variable NOCONFIGURE to some non-empty + value will prevent autogen.sh from running configure. + + +Usage of ic35sync +----------------- + ic35sync synchronizes IC35 address, schedule, todo and memo data + with Personal Information Management databases (PIMfiles) mainly + in vCard,vCalendar format. Get information about usage with: + ic35sync --help + ic35sync supports 4 modes of operation, given as command argument: + - status + reports IC35 status: firmware version, timestamp of last sync + operation and total and modified number of records per IC35 file. + No PIMfiles are accessed, contents of IC35 are not changed, thus + good for an initial test. + - export + does export IC35 data to PIMfile(s), data in IC35 is not changed + and no IC35 or PIM records will be deleted. The sychronisation + stamp will not be written to IC35 and any changemarks on records + in the IC35 remain intact. + This is the initial operation to start using IC35 data also with + Linux Personal Information Managers. It is also usable to backup + all the IC35 data. + Records from existing PIMfile(s) can afterwards be propagated to + IC35 with an 'import' or 'sync' operation. + - import + does import data from PIMfile(s) into IC35, changes in PIMfile(s) + reflect only the IC35 record numbers and status, no IC35 or PIM + records will be deleted. The synchronisation stamp (current date + and time) will be written to IC35 only if all IC35 records match + with PIMfile, e.g. if the IC35 was empty before the import. + This is the initial operation to start using Linux PIM databases + also on IC35. it can be used also to restore data into IC35. + Any records previously existing in IC35 can then be propagated + into PIMfile(s) with an 'export' or 'sync' operation. + - sync + synchronizes changes in IC35 and PIMfile(s): records modified in + IC35 will be updated to PIMfile(s), records deleted in IC35 will + be deleted from PIMfile(s) and vice versa. + If changes to the same record were made in both the IC35 and the + PIMfile, the latter takes precedence, i.e. PIM overrides IC35. + All record changemarks on IC35 will be cleared and the current + date and time will be written to IC35 as synchronisation stamp, + because after sync all IC35 records match with PIMfile(s). + With the vCard/vCalendar format (default or option "--format vca") + up to 3 PIMfile(s) can be specified on the ic35sync commandline: + If 3 PIMfiles are specified, the first file is for Addresses, the + second file is for Schedule and ToDo, the third file is for Memo. + If 2 PIMfiles are specified, the first file is for Adresses and the + second file for Schedule, ToDo and Memo. Korganizer and gnomecal + will work with the second file only if there are not VMEMO-records + in it, i.e. only if there is not any Memo note on the IC35! + If 1 PIMfile is specified, this single file is used for all data + from Adresses, Schedule, ToDo and Memo. Korganizer, gnomecal and + and gnomecard cannot work with that single file! + If no PIMfiles are specified, the default filenames are used, which + are ic35.vcard for Adresses, ic35.vcal for Schedule and ToDo, and + ic35.memo for Memo, the files are assumed in the current directory. + + Examples + * GNOME addressbook and calendar + IC35 syncstation connected at /dev/ttyS1, IC35 password is "secret" + initial export from IC35: + ic35sync -d /dev/ttyS1 -p secret export \ + ~/.gnome/GnomeCard.gcrd ~/.gnome/user-cal.vcf ic35.memo + synchronize changes in IC35 and addressbook,calendar: + ic35sync -d /dev/ttyS1 -p secret sync \ + ~/.gnome/GnomeCard.gcrd ~/.gnome/user-cal.vcf ic35.memo + * KDE KOrganizer and GNOME addressbook + initial import into IC35 at /dev/ic35, no IC35 password: + ic35sync import ~/.gnome/GnomeCard.gcrd \ + ~/.kde/share/apps/korganizer/calendar.vcs ic35.memo + synchronize changes in IC35 and addressbook,KOrganizer: + ic35sync sync ~/.gnome/GnomeCard.gcrd \ + ~/.kde/share/apps/korganizer/calendar.vcs ic35.memo + + Restrictions + * ic35sync yet has no options for resolving synchronize conflicts. + If the same record is modified on both sides, the modification in + the PIMfile takes precedence and overrides the changes on IC35. + * ic35sync yet has a proprietary VMEMO format for memo records. + The VMEMO format was invented by me following the vCard/vCalendar + standard. Unfortunately there is no GUI software supporting it. + Suggestions for the memo records are welcome. + * ic35sync should be used with _existing_ vCalendar files, because + Korganizer and gnomecal rely on their own file formats and do not + work properly with vCalendar files _created_ by ic35sync. + Korganizer uses newline characters as line separators and wants + its own PRODID. + gnomecal uses carriagereturn linefeed sequences as line separators + and wants its own PRODID and VERSION. + ic35sync does respect and not change the format of an existing + vCalendar file to avoid confusing Korganizer/gnomecal. + * ic35sync's sync operation is rather slow, because always all IC35 + records are read to find the records, which were deleted from the + PIMfile(s). This is needed, as Korganizer, gnomecal and gnomecard + leave no traces (e.g. delete-flags) from deleted records. + * gnomecal does not support VEVENT.DESCRIPTION but multiple lines + in VEVENT.SUMMARY. + ic35sync maps the first line of VEVENT.SUMMARY to/from the IC35 + "Summary" field and all remaining lines to/from the IC35 "Notes" + field. + * gnomecal does not support VTODO.DTSTART. + ic35sync maps the IC35 "Start" field to/from a line marked with + "DTSTART:" in VTODO.DESCRIPTION. + * gnomecard does not support the X-PILOTSTAT change mark. + ic35sync regards VCARD records as changed if their with revised + time VCARD.REV is newer than the date and time of the last sync. + * korganizer does not support VTODO.CATEGORIES, VTODO.DTSTART and + VTODO.DUE + ic35sync maps the IC35 Category and the "Start" and "Due" fields + to/from lines marked "CATEGORIES:", "DTSTART:" and "DUE:" in the + VTODO.DESCRIPTION. + * korganozer supports only VEVENT.DALARM, but not VEVENT.AALARM + ic35sync maps IC35 "LED" and "Beep" alarms to/from VEVENT.DALARM. + * the IC35 address fields "(Def.)" have no VCARD counterparts. + ic35sync maps them to/from lines marked "(def1):" and "(def2):" + in VCARD.NOTE. + + +Usage of ic35mgr +---------------- + The IC35 manager currently supports access of the IC35 MMCard(s) + and backup,restore the IC35 internal PIM-database to/from a file. + Get help about usage with + ic35mgr --help + and experiment (and please tell me about problems, suggestions and + wanted features, e-mail address below). + + Restrictions + * The ic35mgr MMCard access commands mmcget,mmcput,mmcdel accept the + filepath in the IC35 native form (e.g. MMcard1\IC35\APP\REVERSI.APP) + or more convenient with lowercase characters and slash separators + (e.g. mmcard1/ic35/app/reversi.app). + The filepath argument in the IC35 native form must be enclosed in + single (') or double (") quotes to prevent the shell from eating + the backslash (\) characters. + * Reading a MMCard file with mmcget will set the timestamp of the + local file to that of the MMCard file. + Even when only reading a MMCard file, the IC35 will set the MMCard + file's timestamp to the current date and time. + * Installing application programs on the IC35 is planned, but not yet + implemented. If someone (from Siemens) would provide me with the + specification of the installation protocol would ease this a lot. + + +Files in the Distribution +------------------------- + Documents + README + sic! you're reading this + NEWS + release information about IC35Link + ChangeLog + sic! details about what was changed + TODO + sic! what remains to be done and whish list + COPYING + GNU GENERAL PUBLIC LICENSE + AUTHORS + who has written IC35Link + THANKS + who has contributed to IC35Link + doc/ic35sync.txt + description of IC35sync protocol, yet only (?) german + doc/ic35mgr.txt + description of IC35manager protocol, yet only (?) german + + Miscellaneous + INSTALL + generic installation instructions for 'configure' + autogen.sh + generates configure and Makefile.in etc. + config.h + configuration header created by configure + config.h.in + input for configure to create config.h, created by autoheader + configure + configuration script, created by autoconf + configure.in + input for autoconf to create configure + Makefile + makes 'make' make it ;-) created by configure + Makefile.in + input for configure to create Makefile, created by automake + Makefile.am + input for automake to create Makefile.in + + Tools + src/ic35log.sh + make logfiles of IC35 communications more readable. supported are + logfiles created by "portmon" (from www.sysinternals.com) and the + logfiles from ic35sync and ic35mgr (of course ;-). + + Sources in the src directory + ic35sync.c + IC35 synchronize PersonalInformationManagement (PIM) data + ic35mgr.c + IC35 manager: access MMCard, backup,restore database + dataio.c + dataio.h + PIM data import to / export from IC35 database records + datatxt.c + support for text output IC35 record + databin.c + support for binary IC35 record format (PIM)files + datavca.c + support for vCard,vCalendar format PIMfiles + vcutil.h + vcutil.c + utilities for vCard,vCalendar + ic35frec.h + ic35frec.c + IC35 record access and file descriptions + syntrans.c + syntrans.h + IC35 synchronize transactions: + connect, disconnect + open, close, read, write IC35 database file + synproto.c + synproto.h + IC35 synchronize protocol + mgrtrans.c + mgrtrans.h + IC35 manager transactions: + connect, disconnect + MMCard status, directory, file access + mgrproto.c + mgrproto.h + IC35 manager protocol + genproto.c + genproto.h + general IC35 protocol support + comio.c + comio.h + basic serial communication with IC35 + util.c + util.h + utilities: logging, errors, messages + vcc.h + export header for vCard,vCalendar API + vcc.y + parser for vCard-,vCalendar-files + vobject.h + vobject.c + API for vCard,vCalendar VObject + port.h + compilation environment for vCard,vCalendar API + vcaconv.c + vCard,vCalendar conversion, e.g. to/from binary IC35 data + + +References +---------- + The vCard,vCalendar API sources were taken from gnome-pim-1.2.0, + and fixed a bit to reduce compile warnings. + Sources for vCard,vCalendar API: + - Gnome-PIM gnome-pim-1.2.0.tar.gz + from ftp://ftp.gnome.org/pub/GNOME/stable/sources/gnome-pim/ + - KPilot kpilot_3.1.10-1.0.tar.gz (based on KPilot 3.1b9) + from ftp://kde.tdyc.com/pub/kde/debian/dists/potato/contrib/source/ + see also http://www.slac.com/pilone/kpilot_home/index.html + - versit consortium's SDK for Windows DLLs + from http://www.imc.org/pdi/sdkdllsr.zip + The autogen.sh script was taken from gnome-pim-1.3.2 and fixed + a bit to fit the needs of ic35link. + + +Standard Disclaimer, Warnings, Copyright +---------------------------------------- + THIS SOFTWARE HAS BUGS (as any software has ;-). Use it at your + own risk. I take no responsibility for any data loss or damage, + etc. done by this software, i.e. should your IC35 crash or explode + while using ic35sync or ic35mgr, sorry. + Besides my home machine this software was successfully used with: + - KDE-1.1.2 Korganizer v1.1.1 + - Gnome-1.2 gnomecard,gnomecal from gnome-pim-1.2.0 + - IC35 Firmware V1.28 + - IC35 Firmware V1.38 (thanks Thomas Lichtenberg) + - Debian-2.2r2 kernel 2.0.36 gcc-2.95.2 Pentium-I/133MHz /dev/ttyS3 + - Debian-2.2r2 kernel-2.2.13 gcc-2.95.2 Pentium-III/400MHz /dev/ttyS1 + - SuSE-7.2 (thanks Karl Stroetmann) + - SuSE-7.0 kernel-2.2.16 gcc-2.95.2-98 AMD-K5/100MHz /dev/ttyS1 + - SuSE-7.0 kernel-2.2.16 gcc-2.95.2-98 Pentium-III/450MHz /dev/ttyS0 + - SuSE-6.0 kernel-2.0.36 gcc-2.7.2.3-5 BAYCOM Notebook Pentium-I/166MHz + - Mandrake-7.2 AMD-Duron/750MHz /dev/ttyS1 (thanks Malte Schmidt) + - Mandrake-8.0 gcc-2.96 (thanks Christian Theile) + + That being said, I _really_ want comments regarding this software + as well as suggestions and bug reports. + + t.schulz@d2mail.de Thomas Schulz 2000-12-25 + Geibelstrasse 57 + D-22303 Hamburg + Germany + +Copyright (C) 2000,2001 Thomas Schulz + + 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 in the file COPYING; if not, write to the + Free Software Foundation, Inc. + 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + diff --git a/THANKS b/THANKS new file mode 100644 index 0000000..a175e6f --- /dev/null +++ b/THANKS @@ -0,0 +1,62 @@ +People who contributed to IC35Link, in almost chronological order: + +- Thomas Schulz + the initial author (myself :-), needed it for lack of a Windows-System + +- Michael Bruennert + was the first one (besides me) interested in IC35sync/Linux, + supplied lots of 'portmon' logs from IC35sync under Windows/NT, + which made protocol analysis and implementation possible, and + gave feedback about bugs under SuSE-7.0 + +- "Logbuch IC35" + supplied webspace for ic35sync_1.14.tar.gz and ic35sync_1.15.tar.gz + +- Uwe Stobinski + made an excellent IC35 website, hosted an "IC35 Wettbewerb" and + supplied webspace for ic35sync_1.14.tar.gz and ic35sync_1.15.tar.gz + +- Norbert Kolb + offered means for publishing IC35sync/Linux + and made many proposals for improving it + +- Dieter Schultschik + alias + gave feedback about bugs under RedHat-7.0 + +- Harald Becker + contributed ideas for faster write MMCard files and + suggested compile without logging and com-simulation + +- Oliver Zechlin + promised to supply me with the IC35 communication protocol specs + and gave me "Goodies" for my "IC35 Wettbewerb" contribution + +- Thomas Lichtenberg + reported some bugs and did tests with IC35 firmware V1.38 + and under SuSE-7.0 and SuSE-6.0 + +- Malte Schmidt + reported some bugs and did tests with Mandrake-7.2 + +- Konrad Mader + reported bug with ic35sync many addresses and some more + inspired autogen.sh due to configure missing in CVS + +- Christian Theile + reported compile problem with Mandrake-8.0 + +- Karl Stroetmann + reported compile problem with SuSE-7.2 + +- Hans-Michael Stahl + contributed ic35mgr "mmcgettree","mmcputtree" commands for whole dirtree + +organizations whose work helped me: +- Debian http://www.debian.org + for the best (in my opinion) FreeSoftware GNU/Linux System +- IMC consortium http://www.imc.org + for the vCard,vCalendar standards and API +- SourceForge http://sourceforge.net + for hosting IC35Link and its CVS Repository + diff --git a/TODO b/TODO new file mode 100644 index 0000000..98ae944 --- /dev/null +++ b/TODO @@ -0,0 +1,741 @@ + $Id: TODO,v 1.21 2001/08/09 23:59:22 thosch Rel $ + TODO for IC35 communication + =========================== + +legend +------ +F task to do + yyyy-mm-dd_hh WHAT + details .. + F sub-tasks .. +F is current state from: +- todo += work ++ done +x canc cancelled as no more need/want +WHAT is task change from: + todo task invented + +nn% task nn% done + done task done + canc task cancelled + +- documentation for newbies + 2001-08-03_13 todo Klaus Krenz had problems + - explain COM?-ports are /dev/ttyS?, symlink /dev/ic35, access rights + - initial test with 'ic35sync status' and/or 'ic35mgr mmcdir' + - for 'mmc*' commands put backslashed path in quotes +- segfault empty fields in gcrd-file 'TITLE:' or 'BDAY:' + 2001-06-17_21 todo bug report by Konrad Mader + empty 'BDAY:' does not segfault, but put 'BDAY' (no colon) into PIMfile + and next ic35sync complains "parse error" + seem like output error, i.e. shall output 'BDAY:' not 'BDAY' +- report protocol error reasons (e.g. get_modflen timeout) + 2001-06-17_21 todo bug report by Konrad Mader + should do better error reports for easier finding protocol problems + get_modflen timeout failure for 860 addresses fixed 2001-06-17_23 +- combine ic35sync,ic35mgr into one program ic35link + 2001-02-25_20 todo proposed by Harald Becker + - backward compatible: symlink ic35sync -> ic35link and ic35mgr -> ic35link + behave like before if argv[0] is "ic35sync" or "ic35mgr" + - partial help like CVS + --help + --help-options + --help-commands + -H or --help +- fixes suggested by GNU Coding Standards and automake/autoconf + 2001-02-03_15 todo + + configure.in: use AC_REVISION + + fix warnings from -Wmissing-prototypes + + fix warnings from -ansi + - fix warnings from -pedantic + + long options --version --help etc. --info shows build-info + + info enquiry with --version --help --info shall exit(0) ! + - use config.h in sources where needed (e.g. VERSION), see gnotepad+ + + open device with O_NOCTTY + - test on okosuse (yacc/bison), test on tls + - misc: scripts for sync with gnome, korganizer + - bugs to at end of --help message + - use 'autogen' for options and usage, see /usr/doc/autogen/autogen.ps + - update copyright 2001, add license notice +- ic35mgr.txt extensions + 2001-01-31_02 todo + - new command 05 - retry read statusblock 16400-16 bytes + in WinNT logs from Michael Logs270101.tar.gz + - reponse 91 from IC35 for powerfail ? (see ic35mmcput52rcv91.log) + - analyse & describe contents of IC35 statusblock, logo-bitmap + see ic35mgr.txt 1.8.1 +- ic35mgr communication improves + 2001-01-30_01 todo + - use #define for busywait delaytime and blocksize + PIII/500MHz 16/3212 1/187 + - measure and improve timeouts in mgrproto.c + ? handle backup problem (Michael Bruennert): recv 90 instead of datablock + - handle mgrproto problem: abort on recv 90 instead A0 after send ack/nak + - mgrtrans.c: more disconn retry to catch >8.5 sec tmo on lost char + maybe do twice Mcmdrsp(MCMDreset,MRSPgotcmd) +- ic35sync syn{trans,proto}.* re-organize like mgr{trans,proto}.* + 2001-01-29_12 todo +- ic35sync local database + 2001-01-26_01 todo + to avoid read all IC35 records to check for deleted in PIMfiles + and for store "sysinfo" date+time to check match with IC35 + may also need sync local databases between e.g. work-,home-system + use XML-format for local database ?! +- ic35mgr handle Ctl-C user interrupt + 2001-01-12_21 todo + for mmcput,mmcget do mmc_close on SIGINT, for other ignore SIG_INT + e.g. mmc_readfile,mmc_writefile return ERRor on SIG_INT +- ic35mgr get/put multiple file, sync dirtree + 2001-01-12_19 todo + - get multiple files: + mmcget MMCard1/dir/* get all files from dir + mmcget MMCard1/*.I35 get multiple files + mmcget MMCard1/dir/ get recursively all files in dir and below + mmcget MMCard1/ get recursively all files from MMCard + recursive get creates local directory tree like to MMCard subtree + additional localdir/ arg puts files below localdir/ + put multiple files with same logic + problem: make dir on IC35 MMCard, use mmc_opendir(,MMCcreatrunc) ? + - synchronize MMCard with local dirtree + option "-n" to test what would be sync'd + - interactive transfer multiple files and walk dirs (like ftp) + 2001-05-30_22 todo request guenther dreher-esders + - guenther wants to 'ic35mgr mmcput MMCard1/x*.txt' for reading big text + on IC35 split into many smaller files +- ic35sync support XML format + 2001-01-09_17 todo proposed by Norbert Kolb + see xmlFiles.pdf.gz from Harald Becker + 2001-02-25_20 work Harald Becker +- ic35sync sync/import/export e.g. ToDoList alone + 2001-01-09_17 todo proposed by Norbert Kolb +- analyse and implement IC35 manager install application,bitmap,midi + 2000-12-31 todo + - send application + - send IC35 start graphic bitmap + - send MIDI file + ? remove application + ? remove bitmap, MIDI file +- problem IC35 does not alarm + 2000-12-30_20 todo + problem after: restore cfgmsg27.i35, set date+time, import 20001230.* + reason: old repeated events "ww" 20001023,20001027 with endrepeat before today + 2000-12-31_17 50% reason found + ic35sync do not write events with endrepeat before today ? +- ic35sync corrections + 2000-12-30_19 todo + + on read_id_frec from IC35 fail do writerec instead of updaterec + 2000-12-31_18 +30% + - write to IC35 all Sched,ToDo,Memo which not on IC35 (for initial sync) + - sync overwrites IC35 changes after: reset,delall,restore IC35 + because IC35 appearently does not restore sysinfo date+time ?! +- shared library e.g. for perl module ic35-communications + 2000-12-29_02 todo proposed by Norbert Kolb +- vcaconv vca2bin,bin2vca,bin2txt transfer recstat + design-problem ? updic35rec() and putic35rec() to not transfer recstat + see experimental dataio.c 1.32.2 + 2000-12-26_03 todo +- ic35sync conflict resolve options + 2000-12-25_06 todo + - PIM override IC35 (default) + - IC35 overrides PIM + - duplicate conflict records + - inspect for sync,import/export: gnome-pilot,-conduits, pilot-link + - search sourceforge etc. for other PIM-programs +- reduce ic35sync log volume + 2000-12-14_21 todo + option for loglevel, buffer higher level log lines in FIFO + flush buffered log to file on error, discard if buffer gets full +- ic35sync minor improvements + 2000-12-17_17 todo + + fix vcc.y compile warnings + x extract rcskvf() from ic35sync.c,ic35mgr.c to util.c + no more need with VERSION from configure.in + - pimfile(s) writable not pre-checked, pim_close() does not return write-error + - write and backup pimfile(s) only if really changed: global dirty-flag + - pim_open(),pim_close() are misleading names + xxx_open(),xxx_close re-design: who/where store filenames ? + see experimental dataio.c 1.32.1 and datavca.c 1.38.1 + - dataio.c: use field map tables for vCard,vCal to/from IC35 + - syntrans.c: internal fd, no need for fd-argument + implicit close on next open_file() and disconnect() + - synproto.c: finer checks in recvrsp() + - malloc/error traps with setjmp(),longjmp() +- improve gnomecard + 2000-12-17_21 todo + - does not change X-PILOTSTAT ! + fix: set X-PILOTSTAT:1 when VCARD changed + - changes VCARD.REV to UTC not local time ! + - does update *ALL* VCARD.REV times on exit + reason: gnomecard wants yyyy-mm-ddThh:mm:ss, drops yyyymmddThhmmss + fix: accept also yyyymmddThhmmss + - default e-mail type is AOL, better change to INTERNET + - default birthday 1970-01-01 is bad, should be empty +- improve gnomecal + 2000-12-25_19 todo + - extra field for VTODO.DTSTART + - extra field for VEVENT.DESCRIPTION + - do not crash if VEVENT,VTODO.CLASS:PUBLIC missing + - do not update *ALL* LAST-MODIFIED and DCREATED on exit + - add missing standard categories Personal,Business(,Unfiled) + - show/edit VTODO.STATUS + ? output correct VCAL.VERSION:1.0 instead of 1.2.0 + ? seems to mis-behave with X-PILOTSTAT,X-PILOTID +- improve korganizer + 2000-12-25_19 todo + - support VTODO.DTSTART,DUE + - support VTODO.CATEGORIES + - support also AALARM, distinguish from DALARM + ? korganizer prios are 1,2,3,4 ? prios are duplicate ? +- problem: memory leak in vobject.c setVObjectStringZValue() + 2000-12-12_02 todo + setVObjectStringZValue() and relatives discard previous string + although it may be malloc()ated'd and free() should be done. + * vobject.c has 'strTbl[]' possibly usable to fix memory leak + but strcasecmp prohibits use for any string + strTbl[] used in lookupStr unUseStr cleanStrTbl + lookupStr called by newVObject lookupProp + unUseStr called by deleteVObject + hashStr called by lookupStr unUseStr + - solution-1: + extend above vobject.c utilities with strcmp() instead of strcasecmp() + - solution-2: + always create new VObject instead of changing string values + re-create vobj-list and/or vprop-list, then use cleanVObject() + - solution-3: + deleteStr() previous string value before set new string value +- test-data for vCard,vCal to/from IC35 record conversion + 2000-12-11_12 todo + - max.fieldlengths for each record + - Addresses + - name only + - no, one, two, three email-addrs + - telnos home,work,cell,fax + - non-ASCII in Name,ADR,ORG,.. + - IC35 "(def.)" fields + - Schedule + - no alarm, LED-alarm, beep-alarm, LED+beep-alarm + - alarm 0,1,5,10,30 mins, 1,2,10 hours, 1,2 days before + - no repeat, repeat every d days, w weeks, m monwday, n monmday, y years + - ToDoList + - prio normal,high,low + - completed no,yes, other status + - Memo +- ic35sync tests for export + 2000-12-15_12 todo + restore IC35 from MMC, export from IC35 as reference PIMfile + run tests: save PIMfile-1, export to PIMfile-2, compare PIMfile-1,-2 + = export to new PIMfile + = export to existing same PIMfile + no changes in PIMfile + - add IC35 records, export + only added PIMrecords shall be affected (modified+created time, recID) + - change IC35 records, export + only changed PIMrecords shall be affected (modified time) + - add records to PIMfile, export + added records in PIMfile shall be unaffected + - change PIMrecords, export + changed PIMrecords shall be overwritten from IC35 +- ic35sync tests for import + 2000-12-15_12 todo + restore IC35 from MMC, export from IC35 as reference + run tests: export-1,import,export-2, compare export-1,-2 + = import reference to IC35, i.e. re-import same data + changeflags on IC35 shall be reset, PIMfile unaffected + no write/update to IC35 + = reset IC35, import reference into empty IC35 + write all PIMrecords, new IC35-recIDs in PIMfile + owner addr shall appear as such + = add records to PIMfile, import + added PIMrecords on IC35, IC35-recIDs in added PIMrecords + - small PIMfile with all new records, import + all PIMrecords on IC35, IC35-recIDs in PIMrecords + = add records to IC35, import reference + added IC35 records shall have changeflag, no set_date+time + = change records in IC35, import reference + changed IC35 records shall be overwritten from PIMfile +- ic35sync tests for sync + 2000-12-19_02 todo + = some test data in testdelfld{1,2,3}.vca + sync testdelfld1.vca + import testdelfld2.vca + export testout2.vca + copy testdelfld2.vca to testdelfld3t.vca, del fields,recs, set X-PILOTSTAT:1 + sync testdelfld3t.vca + export testout3.vca should be same as testdelfld3t.vca + IC35: mark all records edited + copy testout2.vca to testdelfld4.vca + sync testdelfld4.vca should be same as testdelfld3t.vca + - specify more tests +- ic35sync interactive ? + 2000-11-06_23 todo + - implicit connect, on exit disconnect + - set date, category + - open file, close file + - read file record + - write file record + - delete file record + - reset file record changeflag +- improve source comments + 2000-12-01_17 todo + - ic35frec.c: IC35REC access logic + - synproto.c: PDU parameters +- ic35log.sh improvements and tests + 2000-12-01_14 todo + - test with WindowsNT-2line logs: see below + - test with WindowsNT-1line logs: see below + - test with Windows98 logs: see below + - improve ic35prot() structure to make it more clear / readable + +DONE ++ split ic35sync.c into modules: + 2000-11-06_23 todo + + util.c logging, error,message + + comio.c serial communication + + synproto.c L1,L2,L3 protocols + + syntrans.c L4 protocol: IC35sync transactions + + dataio.c import/export Addresses,Memo,Schedule,ToDoList as vCard,vCal + + ic35sync.c main, IC35sync session + + Makefile + 2000-11-07_03 done ++ ic35sync minor improves + 2000-11-07_01 todo + + util.c + + log_init() set also loglevel, default logtag=NULL ? + + log ic35sync version/rcsid to logfile + + syntrans.c + + combined connect = welcome,identify,power,authenticate,getdtime ? + + welcome: "WELCOME\x80" no need send "A" ? + + stop on wrong password: response != 0101 + + Makefile + + targets dist,dist_co for ic35sync_.tar.gz + + integrated help + 2000-11-15_04 +70% todo: Makefile + 2000-11-18_23 done ++ ic35 record access + 2000-11-15_02 todo + + access record-field + + num.of record-fields to struct ic35file + + output binary record-data for offline vCard,vCal develop + 2000-11-19_01 done ++ ic35sync export in vCard, vCalendar format + 2000-11-06_23 todo + sources: + * /local/pkg/ic35/sdkdllsr.zip + vcc.h vcc.y vobject.h vobject.c + * kpilot_3.1.10-1.0.tar.gz + kpilot-3.1.10/conduits/vcalconduit/ vcal-conduit.h vcal-conduit.cc + kpilot-3.1.10/conduits/vcalconduit/versit/ vcc.h vcc.y vobject.h vobject.c + * gnome-pim_1.2.0.orig.tar.gz + gnome-pim-1.2.0/libversit/ vcc.h vcc.y vobject.h vobject.c + * gnome-pilot-conduits_0.4 + * gnome-pilot_0.1.55-0pre + * jpilot_0.97 + * pilot-manager_1.107 + * pilot-link_0.9.3 + 2000-11-19_20 work + + vcaconv: + + IC35 raw data -> vCard,vCal + + pretty-print vCard,vCal + + move ic35file definitions to dataio.c to vcaconv link syntrans etc. + + design output from IC35 to vCard,vCal file, maybe same file + + XPilotId instead of UID + + output text fields quoted printable if non-7bitASCII + use CHARSET=ISO-8859-1;QUOTED-PRINTABLE + + Schedule: repeated events, recurrence rule + + Schedule: DALARM = LED, AALARM = beep + + whattodo with Memo in vCal format ? + use VMEMO record VCMemoProp, extend vcc.*,vobject.* + 2000-11-20_05 +50% + 2000-11-21_06 +70% + 2000-11-22_08 done +x ic35sync export in vCard, vCalendar format: canceled tasks + 2000-11-22_08 todo + x vcaconv: vCard,vCal -> IC35 raw data + x what is XPilotStatus for ? ICAL_PILOT_SYNC_{NONE,MOD,DEL} + 2000-12-01_15 canc replaced by ic35sync import,sync step-2,-3 ++ improve ic35prot.awk + 2000-11-07_23 todo + + test with WindowsNT-2line: + + Simple_hex.log + + Portmon_export.log + + Import1.log + + Import.tar.gz:Import.log + + test with Windows98: + + mgr_conn_disc.log + + mgr_conn_disc_mmc.log.gz + + sync_missoutlook.log + + mgr_rwdfile_20001107.tar.gz:*.log + 2000-11-15_01 +90% usable, todo: missing tests & improve structure + 2000-11-27_13 canc ic35prot.awk replaced by ic35log.sh +x improve ic35prot.awk: canceled tasks + 2000-11-07_23 todo + x test with WindowsNT-1line: + x Manager.tar.gz:*.LOG + x BackupRestore.tar.gz:*.log + x improve structure to make it more clear / readable + 2000-11-27_13 canc ic35prot.awk replaced by ic35log.sh ++ improve ic35-prot-analyzer + 20001124_20 todo + + print 2nd ff. recdata fragments, 'currfid' less hacked + + integrate in one script + + ic35prot.awk faster and more clear + no need to support ASCII input + move ASCII output to separate pass + 20001127_13 done ++ analyse Delete.tar.gz delete record etc. + 2000-11-18_01 todo + + meaning of new commands 01 02, 01 07 + 01 07 = read next modified record + 01 02 = delete record + x meaning of category response 01 01, 00 01 + moved to "ic35sync experiments" + 2000-11-24_01 done ++ ic35sync.txt: document readrecmod, deleterec + 20001124_20 todo + 20001127_15 done ++ ic35sync import,sync step-1 + 20001124_20 todo + + dataio.c: use rec-id AND file-id as UID/X-PILOTID + + dataio.c: implement REV, LAST-MODIFIED + + get_mod_flen + + read_mod_frec + + delete_frec + + write_frec + + set_date_time + + category + 20001128_04 done ++ ic35 record access + 2000-11-21_02 todo + + use ulong frid instead of uchar fid, ushort rid + + internal IC35 record structure for easier access + + break into fields, _get_recfld()'s static buffer is bad! + 2000-12-01_02 +90% todo: opaque IC35FILE + + hide ic35 file description internals, use acces functions + + use 4byte record-ID in binary format + 2000-12-01_19 done ++ ic35log.sh process ic35sync logfiles, create simulation files from them + 2000-12-01_14 todo + 2000-12-02_02 done ++ ic35sync experiments: + 2000-11-06_23 todo + tests for category,filename in ic35sync.c 1.11.1 branch + + check wrong password: + complains with 0100 (not 0101) only if password on power-on enabled + + check category response + + unknown category + + category with no records, e.g. "Unfiled" + + category with modified record(s) + + category with new record(s) + + new category with new record(s) + + new category without records + + set category influence on read_mod_frec ? NO! + mostly response for existing category is 01 01, for non-exist 00 01 + but not always, still unclear what the category sematics are + + test bad filename or other files, e.g. Messages ? + open compares only first any-case letter, unmatched opens "Schedule + 2000-12-02_05 done ++ ic35sync import,sync step-2 + 20001124_20 todo + tests in ic35sync.c 1.11.1 branch + + commit_frec + + test functions for import,sync: + + category + + set_date_time + allows setting any text, tested up to 14 chars, maybe up to 16 chars + + get_mod_flen, read_mod_frec + + write_frec + + delete_frec + + commit_frec + + field modified on PC: only write_frec or also del old frec ? + must delete_frec old, as write_frec gets new rec-ID (does NOT overwrite) + 20001202_07 done ++ ic35sync experiments for new commands + 20001201_20 todo + + 00 01 ? file command ? ic35r{66,68}.log + IC35 accepts 1st command, response: E3,49 CD 0F + then IC35 hangs no more accepting commands + ? 01 01 ? del all? NO! ic35r{62,63,65}.log + IC35 returns "done" response A0 03 00, record unchanged + * 01 02 fd_-- ri_--_-- fi delete_frec + * 01 03 fd_-- 00 00 00 00 get_flen + * 01 04 fd_-- 00 00 00 00 get_mod_flen + + 01 05 fd_-- ri_--_-- fi read by recID ic35r{60,69,6A}.log + response non-exist Memo recID: A0,49 3E 81 D3 0D 60 xx xx xx 07 + i.e. recID=0DD3813E, ch=60, 4 flens for Memo record + response non-exist Schedule recID: A0,49 FE AA 20 0E 00 xx xx xx .. + i.e. recID=0E20AAFE, ch=00, 10 flens for Schedule record + * 01 06 fd_-- 00 00 00 00 read_frec + * 01 07 fd_-- 00 00 00 00 read_mod_frec + * 01 08 fd_-- 00 00 00 00 write_frec + + 01 09 fd_-- ri_--_-- fi ... update_frec ic35r{61,6B,6C}.log + re-writes record keeping same recID + * 01 0A fd_-- ri_--_-- fi commit_frec + 2000-12-03_09 done ++ ic35sync import,sync step-3 + 20001124_20 todo + vCard,vCal to IC35 record, test with vcaconv + + CategoryID cannot be mapped, use 0x00 and assume IC35 to create it + + Category: assume IC35 auto-creates new category + + vcard_to_ic35addr + + vevent_to_ic35sched + + vtodo_to_ic35todo + + vmemo_to_ic35memo + 20001207_04 done ++ problem: QUOTED-PRINTABLE fails with multi-field props like ADR,N,ORG,.. + 2000-12-16_15 todo + with QUOTED-PRINTABLE e.g. ADR fields are not separated, but + first field gets value of all fields with ';' embedded. + known but unfixed problem, see: http://www.imc.org/imc-vcard/mail-archive/ + + solution-1 + do not use QUOTED-PRINTABLE on multi-field props, but only CHARSET + x solution-2 + further debug problem to find real cause and fix + 2000-12-16_21 done ++ ic35sync import,sync step-4 (import) + 2000-11-24_20 todo + import,sync do both use set_date_time + + what is XPilotStatus for ? ICAL_PILOT_SYNC_{NONE,MOD,DEL} + + need update vCard,vCal from IC35rec to avoid destroy unmapped fields + + import vCard,vCal or binary into IC35 + 2000-12-17_08 done ++ vcaconv optionally sort output needed for test + 2000-12-17_06 todo + sort vCard,vCal to stdout + 2000-12-20_16 done ++ problems with gnomecard + 2000-12-17_21 todo + + does not change X-PILOTSTAT, cannot not rely on X-PILOTSTAT alone + fix: detect change with create/modify-time VCARD.REV > last-sync-time + + does update *ALL* VCARD.REV times on exit + reason: gnomecard wants yyyy-mm-ddThh:mm:ss, drops yyyymmddThhmmss + fix: produce VCARD.REV:yyyy-mm-ddThh:mm:ss + 2000-12-21_15 done ++ problems with korganizer + 2000-12-17_21 todo + + wants VCAL PRODID:-//K Desktop Environment//NONSGML KOrganizer//EN + fixed ic35sync: do not change found VCAL:PRODID + must manually edit vCal output before load new into korganizer + + complains if VEVENT,VTODO lack UID and does not load those without + fixed ic35sync: also put UID to VCAL:VEVENT,VTODO (and VCARD, VMEMO) + + expects and produces NL-terminated lines, misbehaves on CRLF-terminated + fix ic35sync: auto-detect CRLF or NL and produce same output + + output causes parse-error if CR in NOTE,DESC,.. + need translate NOTE,DESC: PIM NL <-> IC35 CRLF + + does not support VTODO.DTSTART,DUE + fix ic35sync: map to marked lines in VTODO + + does not support VTODO.CATEGORIES, keep IC35 on PIM->IC35 update + x has VEVENT.CATEGORIES, but IC35 has not + 2000-12-24_20 done ++ problems with gnomecal + 2000-12-17_21 todo + + does not support VTODO.DTSTART, but keeps in vCal on exit + fix ic35sync: keep marked line in VTODO.DESCRIPTION + + crashes if VTODO,VEVENT.CLASS:PUBLIC is missing + fix ic35sync: put CLASS:PUBLIC to VTODO,VEVENT if missing + + does not support VEVENT.DESCRIPTION, removes it from vCal on exit + vevent_to_ic35sched: 1st line to S_Subject, more lines to S_Notes + ic35sched_to_vevent: for gnomecal append S_Notes to VEVENT.SUMMARY + x outputs bad VCAL.VERSION:1.2.0, ic35sync keeps found version + 2000-12-25_19 done ++ ic35sync import,sync step-5 (sync) + 2000-12-15_12 todo + + sync vCard,vCal with IC35 + + conflict resolve: initial sync empty PIM must not delete all IC35! + + inspect for sync,import: gnome-pim, kpilot + 2000-12-25_06 done ++ ic35sync some improvements + 2000-12-17_17 todo + + comio.c: for simulate do not set DTR neither open comport + + comio.c: log input V.24 signals on change + + comio.c: allow also WRx and RDx in simulation file + + dataio.c: remove unused _get_vcarec() + + dataio.c big, split: dataio.c 341 datatxt.c 92 databin.c 385 datavca.c 1528 + + dataio.c: translate CRLF<->NL in NOTES,DESCRIPTION + + dataio.c utils, special conversions depend on PRODID Gnome,KOrganizer + put/get IC35 fields unsupported by PIM with mark in NOTE/DESCRIPTION + + KOrganizer: VTODO.CATEGORIES,DTSTART,DUE to/from VTODO.DESCRIPTION + + gnomecal: VTODO.DTSTART to/from VTODO.DESCRIPTION + + gnomecal: VEVENT.DESCRIPTION 1st line S_Subject, more S_Notes + + all: "(def1):","(def2):" to/from VCARD.NOTE + + cmp_ic35rec() from dataio.c to ic35frec.c:cmp_ic35rec ? + 2000-12-25_23 done ++ ic35sync delete fields + currently IC35 to/from PIM record conversions do not delete fields + 2000-12-27_19 todo + 2000-12-29_04 done ++ correct vcaconv + produces empty output on 'vcaconv sortvca ic35.vcal' + 2000-12-31_01 todo + fix: correct error in vobject.c + 2000-12-31_18 done ++ analyse IC35 manager protocol + 2000-11-06_23 todo + + init, exit + + backup database.org + + restore database.org + + read MMCard directories + + read file from MMCard + 2001-01-04_06 +70% + + write file to MMCard + + delete file on MMCard + 2001-01-10_03 done ++ implement IC35 manager MMCard operations + 2000-12-31_18 todo + + init, exit + + read MMCard directories + + read file from MMCard + 2001-01-10_02 +60% + + write file to MMCard + + delete file on MMCard + 2001-01-12_22 done ++ implement IC35 manager backup,restore operations + 2000-12-31_18 todo + + backup database.org + + restore database.org + 2001-01-17_06 done ++ improve ic35mgr throughput + 2001-01-12_20 todo + ic35mgr-1.17.4, busy wait 3.25ms every 16 byte block, 2 stopbits + backup 426128 read 16384 117.3248 3632 b/s + block recv 16384 3.3211 4924 b/s + restore 426128 write 16384 140.4816 3033 b/s + block send 16384 3.5056 4674 b/s + ic35mgr-1.12a, with com_sendw(), itimer pause every 29 chars, 2 stopbits + reversi.app 16452 read 5120 6.5236 2522 b/s + reversi.app 16452 write 2048 11.9835 1373 b/s + + comio.c,comio.h: separate com_sendw() with wait + + mgrproto.c: use com_sendw() with wait only for big blocks + measurements: + install MMCard:browser.app 376900 117.69 3202 b/s + install MMCard:sc_info.app 16452 10.07 1634 b/s + install MMCard:convert.app 49220 15.22 3234 b/s + restore MMCard1\20010124.I35 52222 25.962 2011 b/s + backup MMCard1\20010121.I35 50701 20.625 2458 b/s + ic35mgr-1.10a, with com_sendw(), itimer pause every 29 chars, 2 stopbits + reversi.app 16452 read 5120 6.5678 2505 b/s + reversi.app 16452 write 2048 12.0640 1364 b/s + backup 426128 read 16384 117.4514 3628 b/s + block recv 16384 3.3287 4922 b/s + restore 426128 write 16384 203.6398 2093 b/s + block send 16384 6.0531 2707 b/s + status 16400 recv 16384 1.8257 8983 b/s + itimer, pause every 29 chars, 2 stopbits + reversi.app 16452 write 5120 11.8633 1387 b/s + 2048 12.3224 1335 b/s rewrite 13.8034 1192 b/s + mgrep.app 32836 write 2048 24.6884 1330 b/s + domind.app 49220 write 2048 37.1055 1326 b/s + domind.app 49220 read 5120 20.0052 2460 b/s + usleep(29*400) every 29 bytes, 2 stopbits + reversi.app 16452 write 5120 17.69 929 b/s + 2048 18.42 893 b/s + usleep(100) every 16 bytes + reversi.app 16452 write 5120 26.965 610 b/s + 2048 27.764 593 b/s rewrite 29.094 565 b/s + 1024 29.465 558 b/s + reversi.app 16452 read 10240 6.825 2410 b/s + 5120 7.284 2259 b/s 7.385 2228 b/s + 2048 8.485 1939 b/s + 1024 10.204 1612 b/s + 256 20.396 807 b/s + 15 243.773 67 b/s + 2001-01-18_02 done ++ fix compile errors with gcc-2.96 under RedHat-7.0 + 2001-01-19_17 todo reported by Dieter Schultschik + + synproto.c:sendcmd() line-531,537,542,548,549,554,567,594 + 531:fd, 537:fd, 542:fd, 548:fd, 549:index, 554:fd, 567:fd, 594:fd + `short unsigned int' is promoted to `int' when passed through `...' + (so you should pass `int' not `short unsigned int' to `va_arg') + + mgrproto.c similar problem + + generally avoid va_arg with sendcmd() etc, replace sendcmd(CMDfclosem,fd) + with e.g. sendCMDfclose(fd) would yield compile-time prototype checking + 2001-01-29_13 done ++ improve ic35mgr + 2001-01-12_20 todo + + mgrproto.c: recvmrsp() / MPDUrecv() discard checksum + + mgrtrans.c: protocol comments, identify,disconn,.. belong to mgrproto.c + + move backup protocol from mgrtrans.c to mgrproto.{c,h} + concentrate log,progress,errmsg separate from communication protocol + + move putxxx(),getxxx(),chksum() from mgrtrans.c,xxxproto.{c,h} + and also welcome() etc. to genproto.{c,h} + 2001-01-24_03 +50% + + move _not_impl() from ic35mgr.c to util.{c,h} + + recvmrsp() always return mstate, not pdulen + + mgrproto.c: common structs for same PDUs + 2001-01-29_12 +70% + + mgrproto.c: common convert direntry and FILE_INFO + + mgrproto.c: define iden/FILE_IDEN details, export size, how mismatch? + + mgrproto.h: move attribs,modes from mgrtrans.h (else mgrproto.c unusable) + for backward compatible #include mgrproto.h in mgrtrans.c + x mgrtrans.c: common struct for MMCDIR MMCFILE + x extract rcskvf() from ic35sync.c,ic35mgr.c to util.c + with VERSION imported rcskvf() is obsolete + 2001-02-03_18 done ++ supply also vcc.c problem found by Harald Becker, Michael Bruennert + 2001-01-19_09 todo + supply also vcc.c for systems without or with bad bison/yacc + SuSE-7.0 yacc-91.7.30-220 is bad, need bison >= 1.25 + 2001-02-04_01 done ++ automake/autoconf + 2000-12-29_02 todo proposed by Norbert Kolb + * see Software-Release-Practice-HOWTO, GNU Coding Standards + * must unset CDPATH, else re-automake fails! + + configure.in Makefile.am, then run: + aclocal; autoheader; automake --add-missing --gnu; autoconf; ./configure + + re-org into dirs src/ docs/ + + make CVS-tree, use .cvsignore + + AUTHORS + + THANKS or CREDITS + + NEWS: compact version info + + README: re-org like Software-Release-Practice-HOWTO suggests + x acconfig.h: ISODATE NOT NEEDED + + program-versions from VERSION,ISODATE, remove rcskvf + + step-1: do it with current Makefile + + build-info like gnotepad+ + + fix also top-entry in logfiles + 2001-02-05_03 done ++ README / more docs, e.g. restrictions & workarounds + 2001-01-26_03 todo + + Korganizer: PRODID, workaround missing vCal properties (...) + + GnomeCal: PRODID, workaround missing vCal properties (...) + + GnomeCard: missiong X-PILOT-STAT support and workaround + + problem with bash eating backslash of ic35path + + errors/warnings with 'configure --enable-pedantic' + + compile problem with yacc (if vcc.y changed) + + no sync conflict resolve yet + + proprietary VMEMO format + 2001-02-17_21 done ++ ic35mgr mmcget/mmcput check ic35path before connect + bug-report from Thomas Lichtenberg + 2001-02-06_00 todo + valid ic35path: + + auto-translate leading anycase mmcard[12], lower to upper, '/' to '\' + + leading "MMCard[12]/" + + adjacent / or \ not allowed + + max 1 dot between '/' and '/' or end + + max 8 chars before dot + + max 3 chars after dot + tests (mail from Thomas Lichtenberg): + + ic35mgr mmcdel MMCard1\IC35\BITMAP\INDIANER.BMP + + ic35mgr mmcput MMCard1\TEST\TEST1.TXT test.txt + 2001-02-18_07 done ++ correct com_simrecv(): dummy for non-logged receive bytes, adjacent "RD nn" + 2001-01-04_05 todo + + err.corr: fill block from adjacent "RD nn" lines, dummy non-logged + x handle checksum problem due to dummy bytes + cannot, must patch simfile with expected checksum(s) + 2001-02-18_23 done ++ configure --disable-logandcomsim + 2001-02-25_20 todo proposed by Harald Becker + + configure option for disable logging and simulation for production version + default enabled + + use macros LPRINTF(()), LOG_INIT(()), etc. to reduce codesize + 2001-03-02_02 done ++ reduce priority when using busywait + 2001-02-25_20 todo proposed by Harald Becker + + use for restore,mmcput only, nice(+2), man 2 nice + + option "-n niceval" or "--nice niceval", default niceval 2 + allowed for users 0..19 for root -20..19, 0 is normal prio + 2001-03-03_04 done ++ autogen.sh script for generating configure, Makefile.in, etc. + 2001-05-30_11 todo problem report by Konrad Mader + 2001-08-02_22 done diff --git a/autogen.sh b/autogen.sh new file mode 100644 index 0000000..8997013 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,230 @@ +#!/bin/sh +# $Id: autogen.sh,v 1.4 2001/08/12 03:34:50 thosch Rel $ +# Copyright (C) 2001 Thomas Schulz +# +# Run this to generate configure and all the initial makefiles, etc. +# This is the long version with lots of checks instead of the short version +# aclocal && autoheader && automake --add-missing --gnu && autoconf +# It will automagically run './configure $CONFIGURE_FLAGS', prevent that +# by setting the environment variable NOCONFIGURE to a non-empty value. + +# NOCONFIGURE=true # would disable auto-configure +CONFIGURE_FLAGS="--enable-maintainer-mode" # default flags for configure +# ACLOCAL_FLAGS= # additional flags for aclocal + + +DIE=0 +srcdir=`dirname $0` +eval `grep '^PACKAGE=' $srcdir/configure.in` + +if [ -n "$GNOME2_PATH" ]; then + ACLOCAL_FLAGS="-I $GNOME2_PATH/share/aclocal $ACLOCAL_FLAGS" + PATH="$GNOME2_PATH/bin:$PATH" + export PATH +fi + +# +# check for autoconf +# +(autoconf --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`autoconf' installed to compile $PACKAGE" + echo "Download the appropriate package for your distribution," + echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/" + DIE=1 +} + +# +# check for xml-i18n-toolize if needed +# +(grep "^AM_PROG_XML_I18N_TOOLS" $srcdir/configure.in >/dev/null) && { + (xml-i18n-toolize --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`xml-i18n-toolize' installed to compile $PACKAGE" + echo "Get ftp://ftp.gnome.org/pub/GNOME/stable/sources/xml-i18n-tools/xml-i18n-tools-0.6.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 + } +} + +# +# check for libtool if needed +# +(grep "^AM_PROG_LIBTOOL" $srcdir/configure.in >/dev/null) && { + (libtool --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`libtool' installed to compile $PACKAGE" + echo "Get ftp://ftp.gnu.org/pub/gnu/libtool-1.2d.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 + } +} + +# +# check for gettext if needed +# +#grep "^AM_GNU_GETTEXT" $srcdir/configure.in >/dev/null && { +# grep "sed.*POTFILES" $srcdir/configure.in >/dev/null || \ +# (gettext --version) < /dev/null > /dev/null 2>&1 || { +# echo +# echo "**Error**: You must have \`gettext' installed to compile $PACKAGE" +# echo "Get ftp://alpha.gnu.org/gnu/gettext-0.10.35.tar.gz" +# echo "(or a newer version if it is available)" +# DIE=1 +# } +#} + +#grep "^AM_GNOME_GETTEXT" $srcdir/configure.in >/dev/null && { +# grep "sed.*POTFILES" $srcdir/configure.in >/dev/null || \ +# (gettext --version) < /dev/null > /dev/null 2>&1 || { +# echo +# echo "**Error**: You must have \`gettext' installed to compile $PACKAGE" +# echo "Get ftp://alpha.gnu.org/gnu/gettext-0.10.35.tar.gz" +# echo "(or a newer version if it is available)" +# DIE=1 +# } +#} + +# +# check for automake +# +(automake --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`automake' installed to compile $PACKAGE" + echo "Download the appropriate package for your distribution," + echo "or get ftp://ftp.gnu.org/pub/gnu/automake-1.3.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 + NO_AUTOMAKE=yes +} + +# +# check for aclocal +# +# if no automake, don't bother testing for aclocal +test -n "$NO_AUTOMAKE" || (aclocal --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: Missing \`aclocal'. The version of \`automake'" + echo "installed doesn't appear recent enough." + echo "Download the appropriate package for your distribution," + echo "or get ftp://ftp.gnu.org/pub/gnu/automake-1.3.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 +} + +if test "$DIE" -eq 1; then # some tools are missing + exit 1 +fi + +if test -z "$*" -a -z "$NOCONFIGURE"; then + echo "**Warning**: I am going to run \`configure $CONFIGURE_FLAGS'" + echo "If you wish to pass additional flags to it, please specify them on" + echo "the \`$0' command line." + echo +fi + +case $CC in +xlc ) + am_opt=--include-deps;; +esac + +for coin in `find $srcdir -name configure.in -print` +do + dr=`dirname $coin` + if test -f $dr/NO-AUTO-GEN; then + echo skipping $dr -- flagged as no auto-gen + else + echo processing $dr + macrodirs=`sed -n -e 's,AM_ACLOCAL_INCLUDE(\(.*\)),\1,gp' < $coin` + ( cd $dr + macrosdir=`find . -name macros -print` + for i in $macrodirs; do + if test -f $i/gnome-gettext.m4; then + DELETEFILES="$DELETEFILES $i/gnome-gettext.m4" + fi + done + + echo "deletefiles is $DELETEFILES" + aclocalinclude="$ACLOCAL_FLAGS" + for k in $aclocalinclude; do + if test -d $k; then + if [ -f $k/gnome.m4 -a "$GNOME_INTERFACE_VERSION" = "1" ]; then + rm -f $DELETEFILES + fi + fi + done + for k in $macrodirs; do + if test -d $k; then + aclocalinclude="$aclocalinclude -I $k" + if [ -f $k/gnome.m4 -a "$GNOME_INTERFACE_VERSION" = "1" ]; then + rm -f $DELETEFILES + fi + fi + done + if grep "^AM_GNU_GETTEXT" configure.in >/dev/null; then + if grep "sed.*POTFILES" configure.in >/dev/null; then + : do nothing -- we still have an old unmodified configure.in + else + echo "Creating $dr/aclocal.m4 ..." + test -r $dr/aclocal.m4 || touch $dr/aclocal.m4 + echo "Running gettextize... Ignore non-fatal messages." + echo "no" | gettextize --force --copy + echo "Making $dr/aclocal.m4 writable ..." + test -r $dr/aclocal.m4 && chmod u+w $dr/aclocal.m4 + fi + fi + if grep "^AM_GNOME_GETTEXT" configure.in >/dev/null; then + echo "Creating $dr/aclocal.m4 ..." + test -r $dr/aclocal.m4 || touch $dr/aclocal.m4 + echo "Running gettextize... Ignore non-fatal messages." + echo "no" | gettextize --force --copy + echo "Making $dr/aclocal.m4 writable ..." + test -r $dr/aclocal.m4 && chmod u+w $dr/aclocal.m4 + fi + if grep "^AM_PROG_XML_I18N_TOOLS" configure.in >/dev/null; then + echo "Running xml-i18n-toolize... Ignore non-fatal messages." + xml-i18n-toolize --copy --force --automake + fi + if grep "^AM_PROG_LIBTOOL" configure.in >/dev/null; then + if test -z "$NO_LIBTOOLIZE" ; then + echo "Running libtoolize..." + libtoolize --force --copy + fi + fi + echo "Running aclocal $aclocalinclude ..." + aclocal $aclocalinclude || { + echo + echo "**Error**: aclocal failed. This may mean that you have not" + echo "installed all of the packages you need, or you may need to" + echo "set ACLOCAL_FLAGS to include \"-I \$prefix/share/aclocal\"" + echo "for the prefix where you installed the packages whose" + echo "macros were not found" + exit 1 + } + + if grep "^AM_CONFIG_HEADER" configure.in >/dev/null; then + echo "Running autoheader..." + autoheader || { echo "**Error**: autoheader failed."; exit 1; } + fi + echo "Running automake --gnu $am_opt ..." + automake --add-missing --gnu $am_opt || + { echo "**Error**: automake failed."; exit 1; } + echo "Running autoconf ..." + autoconf || { echo "**Error**: autoconf failed."; exit 1; } + ) || exit 1 + fi +done + + +if test x$NOCONFIGURE = x; then + echo + echo Running $srcdir/configure $CONFIGURE_FLAGS "$@" ... + $srcdir/configure $CONFIGURE_FLAGS "$@" \ + && echo Now type \`make\' to compile $PACKAGE || exit 1 +else + echo + echo Skipping configure process. + echo "Now type \`./configure $CONFIGURE_FLAGS'" + echo "and then \`make' to compile $PACKAGE" + echo " or type \`./configure --help' and see README for configure options." +fi diff --git a/compile b/compile new file mode 100755 index 0000000..23fcba0 --- /dev/null +++ b/compile @@ -0,0 +1,348 @@ +#! /bin/sh +# Wrapper for compilers which do not understand '-c -o'. + +scriptversion=2018-03-07.03; # UTC + +# Copyright (C) 1999-2020 Free Software Foundation, Inc. +# Written by Tom Tromey . +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to or send patches to +# . + +nl=' +' + +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent tools from complaining about whitespace usage. +IFS=" "" $nl" + +file_conv= + +# func_file_conv build_file lazy +# Convert a $build file to $host form and store it in $file +# Currently only supports Windows hosts. If the determined conversion +# type is listed in (the comma separated) LAZY, no conversion will +# take place. +func_file_conv () +{ + file=$1 + case $file in + / | /[!/]*) # absolute file, and not a UNC file + if test -z "$file_conv"; then + # lazily determine how to convert abs files + case `uname -s` in + MINGW*) + file_conv=mingw + ;; + CYGWIN* | MSYS*) + file_conv=cygwin + ;; + *) + file_conv=wine + ;; + esac + fi + case $file_conv/,$2, in + *,$file_conv,*) + ;; + mingw/*) + file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` + ;; + cygwin/* | msys/*) + file=`cygpath -m "$file" || echo "$file"` + ;; + wine/*) + file=`winepath -w "$file" || echo "$file"` + ;; + esac + ;; + esac +} + +# func_cl_dashL linkdir +# Make cl look for libraries in LINKDIR +func_cl_dashL () +{ + func_file_conv "$1" + if test -z "$lib_path"; then + lib_path=$file + else + lib_path="$lib_path;$file" + fi + linker_opts="$linker_opts -LIBPATH:$file" +} + +# func_cl_dashl library +# Do a library search-path lookup for cl +func_cl_dashl () +{ + lib=$1 + found=no + save_IFS=$IFS + IFS=';' + for dir in $lib_path $LIB + do + IFS=$save_IFS + if $shared && test -f "$dir/$lib.dll.lib"; then + found=yes + lib=$dir/$lib.dll.lib + break + fi + if test -f "$dir/$lib.lib"; then + found=yes + lib=$dir/$lib.lib + break + fi + if test -f "$dir/lib$lib.a"; then + found=yes + lib=$dir/lib$lib.a + break + fi + done + IFS=$save_IFS + + if test "$found" != yes; then + lib=$lib.lib + fi +} + +# func_cl_wrapper cl arg... +# Adjust compile command to suit cl +func_cl_wrapper () +{ + # Assume a capable shell + lib_path= + shared=: + linker_opts= + for arg + do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as 'compile cc -o foo foo.c'. + eat=1 + case $2 in + *.o | *.[oO][bB][jJ]) + func_file_conv "$2" + set x "$@" -Fo"$file" + shift + ;; + *) + func_file_conv "$2" + set x "$@" -Fe"$file" + shift + ;; + esac + ;; + -I) + eat=1 + func_file_conv "$2" mingw + set x "$@" -I"$file" + shift + ;; + -I*) + func_file_conv "${1#-I}" mingw + set x "$@" -I"$file" + shift + ;; + -l) + eat=1 + func_cl_dashl "$2" + set x "$@" "$lib" + shift + ;; + -l*) + func_cl_dashl "${1#-l}" + set x "$@" "$lib" + shift + ;; + -L) + eat=1 + func_cl_dashL "$2" + ;; + -L*) + func_cl_dashL "${1#-L}" + ;; + -static) + shared=false + ;; + -Wl,*) + arg=${1#-Wl,} + save_ifs="$IFS"; IFS=',' + for flag in $arg; do + IFS="$save_ifs" + linker_opts="$linker_opts $flag" + done + IFS="$save_ifs" + ;; + -Xlinker) + eat=1 + linker_opts="$linker_opts $2" + ;; + -*) + set x "$@" "$1" + shift + ;; + *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) + func_file_conv "$1" + set x "$@" -Tp"$file" + shift + ;; + *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) + func_file_conv "$1" mingw + set x "$@" "$file" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift + done + if test -n "$linker_opts"; then + linker_opts="-link$linker_opts" + fi + exec "$@" $linker_opts + exit 1 +} + +eat= + +case $1 in + '') + echo "$0: No command. Try '$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: compile [--help] [--version] PROGRAM [ARGS] + +Wrapper for compilers which do not understand '-c -o'. +Remove '-o dest.o' from ARGS, run PROGRAM with the remaining +arguments, and rename the output as expected. + +If you are trying to build a whole package this is not the +right script to run: please start by reading the file 'INSTALL'. + +Report bugs to . +EOF + exit $? + ;; + -v | --v*) + echo "compile $scriptversion" + exit $? + ;; + cl | *[/\\]cl | cl.exe | *[/\\]cl.exe | \ + icl | *[/\\]icl | icl.exe | *[/\\]icl.exe ) + func_cl_wrapper "$@" # Doesn't return... + ;; +esac + +ofile= +cfile= + +for arg +do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as 'compile cc -o foo foo.c'. + # So we strip '-o arg' only if arg is an object. + eat=1 + case $2 in + *.o | *.obj) + ofile=$2 + ;; + *) + set x "$@" -o "$2" + shift + ;; + esac + ;; + *.c) + cfile=$1 + set x "$@" "$1" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift +done + +if test -z "$ofile" || test -z "$cfile"; then + # If no '-o' option was seen then we might have been invoked from a + # pattern rule where we don't need one. That is ok -- this is a + # normal compilation that the losing compiler can handle. If no + # '.c' file was seen then we are probably linking. That is also + # ok. + exec "$@" +fi + +# Name of file we expect compiler to create. +cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` + +# Create the lock directory. +# Note: use '[/\\:.-]' here to ensure that we don't use the same name +# that we are using for the .o file. Also, base the name on the expected +# object file name, since that is what matters with a parallel build. +lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d +while true; do + if mkdir "$lockdir" >/dev/null 2>&1; then + break + fi + sleep 1 +done +# FIXME: race condition here if user kills between mkdir and trap. +trap "rmdir '$lockdir'; exit 1" 1 2 15 + +# Run the compile. +"$@" +ret=$? + +if test -f "$cofile"; then + test "$cofile" = "$ofile" || mv "$cofile" "$ofile" +elif test -f "${cofile}bj"; then + test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" +fi + +rmdir "$lockdir" +exit $ret + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC0" +# time-stamp-end: "; # UTC" +# End: diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..9555de3 --- /dev/null +++ b/configure.in @@ -0,0 +1,201 @@ +dnl --------------------------------------------------------------------------- +dnl Process this file with autoconf to produce a configure script. +dnl $Id: configure.in,v 1.9 2001/11/20 23:28:10 thosch Exp $ +dnl Copyright (C) 2001 Thomas Schulz +dnl --------------------------------------------------------------------------- +AC_INIT(src/ic35sync.c) +AC_REVISION($Revision: 1.9 $ $Date: 2001/11/20 23:28:10 $) + + +dnl --------------------------------------------------------------------------- +dnl This is the only place where the package version appears +dnl --------------------------------------------------------------------------- +PACKAGE=ic35link +VERSION=1.18.1 +ISODATE=2001-11-10 +AC_SUBST(ISODATE) +AC_DEFINE_UNQUOTED(ISODATE, "$ISODATE", release date) + + +dnl --------------------------------------------------------------------------- +dnl Initialize automake +dnl --------------------------------------------------------------------------- +dnl ??? AUTOMAKE_OPTIONS=no-dependencies +AM_INIT_AUTOMAKE($PACKAGE, $VERSION) +AC_PREFIX_DEFAULT(/usr/local) +AM_MAINTAINER_MODE + + +dnl --------------------------------------------------------------------------- +dnl checks for needed compilers +dnl --------------------------------------------------------------------------- +AC_PROG_CC + +dnl --------------------------------------------------------------------------- +dnl option: disable logging and com-simulation +dnl --------------------------------------------------------------------------- +logandcomsim=yes +AC_MSG_CHECKING(if we want logging and com-simulation) +AC_ARG_ENABLE( + logandcomsim, + [ --enable-logandcomsim [default=yes] enable logging and com-simulation], + logandcomsim="$enableval", logandcomsim="yes" + ) +AC_MSG_RESULT($logandcomsim) +if test "$logandcomsim" != "yes"; then + AC_DEFINE_UNQUOTED(NO_LOGSIM, 1, disable log and com-simulation) +fi + +dnl --------------------------------------------------------------------------- +dnl option: disable clean compiles +dnl --------------------------------------------------------------------------- +compiler_warnings=yes +AC_MSG_CHECKING(if we want compiler warnings) +AC_ARG_ENABLE( + warnings, + [ --enable-warnings [default=yes] enable gcc compiler warnings], + compiler_warnings="$enableval", compiler_warnings="yes" + ) +AC_MSG_RESULT($compiler_warnings) + +dnl --------------------------------------------------------------------------- +dnl option: ansi warnings +dnl --------------------------------------------------------------------------- +AC_MSG_CHECKING(whether to compile with strict ANSI) +AC_ARG_ENABLE( + ansi, + [ --enable-ansi [default=no] enable gcc strict ansi checking], + , + [enable_ansi="no"]) +AC_MSG_RESULT($enable_ansi) + +dnl --------------------------------------------------------------------------- +dnl option: pedantic warnings +dnl --------------------------------------------------------------------------- +AC_MSG_CHECKING(whether to compile with pedantic warnings) +AC_ARG_ENABLE( + pedantic, + [ --enable-pedantic [default=no] enable gcc pedantic checking], + , + [enable_pedantic="no"]) +AC_MSG_RESULT($enable_pedantic) + +if test "$ac_cv_prog_CC" = gcc -o "$ac_cv_prog_CC" = g++; then + if test $compiler_warnings = yes; then + if echo "$CFLAGS" | grep "\-Wall" > /dev/null 2> /dev/null; then + CFLAGS="$CFLAGS" + else + echo "updating CFLAGS with extra '-Wall' option" + CFLAGS="$CFLAGS -Wall" + fi + + if echo "$CFLAGS" | grep "\-Wmissing-prototypes" > /dev/null 2> /dev/null; then + CFLAGS="$CFLAGS" + else + echo "updating CFLAGS with extra '-Wmissing-prototypes' option" + CFLAGS="$CFLAGS -Wmissing-prototypes" + fi + fi + + if test "$enable_ansi" = "yes"; then + if echo "$CFLAGS" | grep "\-ansi" > /dev/null 2> /dev/null; then + CFLAGS="$CFLAGS" + else + CFLAGS="$CFLAGS -ansi" + echo "updating CFLAGS with extra '-ansi' option" + fi + fi + + if test "$enable_pedantic" = "yes"; then + if echo "$CFLAGS" | grep "\-pedantic" > /dev/null 2> /dev/null; then + CFLAGS="$CFLAGS" + else + CFLAGS="$CFLAGS -pedantic" + echo "updating CFLAGS with extra '-pedantic' option" + fi + fi +fi + + +dnl --------------------------------------------------------------------------- +dnl generate the config header +dnl --------------------------------------------------------------------------- +AM_CONFIG_HEADER(config.h) + + +dnl --------------------------------------------------------------------------- +dnl Checks for programs. +dnl --------------------------------------------------------------------------- +AC_PROG_INSTALL +dnl AC_PROG_CC was checked above already +AC_PROG_YACC +AC_PROG_AWK +AC_PROG_LN_S + +dnl --------------------------------------------------------------------------- +dnl Checks for header files. +dnl --------------------------------------------------------------------------- +AC_HEADER_STDC +AC_CHECK_HEADERS(fcntl.h malloc.h sys/ioctl.h unistd.h) +AC_HEADER_TIME + +dnl --------------------------------------------------------------------------- +dnl Checks for typedefs, structures, and compiler characteristics. +dnl --------------------------------------------------------------------------- +AC_C_CONST +AC_TYPE_SIZE_T +AC_HEADER_TIME +AC_STRUCT_TM + +dnl --------------------------------------------------------------------------- +dnl Checks for library functions. +dnl --------------------------------------------------------------------------- +AC_PROG_GCC_TRADITIONAL +AC_FUNC_MEMCMP +AC_TYPE_SIGNAL +AC_FUNC_STRFTIME +dnl ??? AC_CHECK_USLEEP +dnl ??? AC_CHECK_ITIMER +AC_FUNC_UTIME_NULL +AC_FUNC_VPRINTF +AC_CHECK_FUNCS(gettimeofday mktime select strdup strerror strspn strstr) + + +dnl --------------------------------------------------------------------------- +dnl Makefiles to create: +dnl --------------------------------------------------------------------------- +AC_OUTPUT(Makefile src/Makefile doc/Makefile) + + +dnl --------------------------------------------------------------------------- +dnl output our configuration +dnl --------------------------------------------------------------------------- +echo +echo these are the options selected: +echo +if test "$USE_MAINTAINER_MODE" = "yes"; then + echo " maintainer mode : YES" +else + echo " maintainer mode : NO" +fi +if test "$logandcomsim" = "yes"; then + echo " log and com-simulation : YES" +else + echo " log and com-simulation : NO" +fi +if test "$compiler_warnings" = "yes"; then + echo " gcc compiler warnings : YES" +else + echo " gcc compiler warnings : NO" +fi +if test "$enable_ansi" = "yes"; then + echo " gcc compile with strict ansi : YES" +else + echo " gcc compile with strict ansi : NO" +fi +if test "$enable_pedantic" = "yes"; then + echo " gcc compiler pedantic checking: YES" +else + echo " gcc compiler pedantic checking: NO" +fi +echo diff --git a/debian/README.Debian b/debian/README.Debian new file mode 100644 index 0000000..28c0132 --- /dev/null +++ b/debian/README.Debian @@ -0,0 +1,6 @@ +ic35link for Debian +------------------ + +This package currently "builds" on debian unstable but I won't recommend it at the current state. + + -- crt0mega Tue, 15 Sep 2020 20:13:25 +0200 diff --git a/debian/README.source b/debian/README.source new file mode 100644 index 0000000..07dc73b --- /dev/null +++ b/debian/README.source @@ -0,0 +1,8 @@ +ic35link for Debian +------------------ + +Currently, every modifications to convince gcc of building this package are managed with quilt. + + + -- crt0mega Tue, 15 Sep 2020 20:13:25 +0200 + diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..32ba7b1 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +ic35link (1.18.1-1) unstable; urgency=medium + + * Initial release after adding compiler-workarounds and fixing some minor errors + + -- crt0mega Tue, 15 Sep 2020 20:13:25 +0200 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..0abf392 --- /dev/null +++ b/debian/control @@ -0,0 +1,16 @@ +Source: ic35link +Section: unknown +Priority: optional +Maintainer: crt0mega +Build-Depends: debhelper-compat (= 12) +Standards-Version: 4.5.0 +#Homepage: +#Vcs-Browser: https://salsa.debian.org/debian/ic35link +#Vcs-Git: https://salsa.debian.org/debian/ic35link.git + +Package: ic35link +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends} +Description: Revived managing and syncing software for IC35 + This is a revival of the old ic35link software by Thomas Schulz. + diff --git a/debian/copyright b/debian/copyright new file mode 120000 index 0000000..d24842f --- /dev/null +++ b/debian/copyright @@ -0,0 +1 @@ +COPYING \ No newline at end of file diff --git a/debian/files b/debian/files new file mode 100644 index 0000000..78f023f --- /dev/null +++ b/debian/files @@ -0,0 +1,3 @@ +ic35link-dbgsym_1.18.1-1_amd64.deb debug optional automatic=yes +ic35link_1.18.1-1_amd64.buildinfo unknown optional +ic35link_1.18.1-1_amd64.deb unknown optional diff --git a/debian/ic35link-docs.docs b/debian/ic35link-docs.docs new file mode 100644 index 0000000..efea0a6 --- /dev/null +++ b/debian/ic35link-docs.docs @@ -0,0 +1,2 @@ +README.Debian +README.source diff --git a/debian/ic35link.doc-base.EX b/debian/ic35link.doc-base.EX new file mode 100644 index 0000000..11eeebf --- /dev/null +++ b/debian/ic35link.doc-base.EX @@ -0,0 +1,20 @@ +Document: ic35link +Title: Debian ic35link Manual +Author: +Abstract: This manual describes what ic35link is + and how it can be used to + manage online manuals on Debian systems. +Section: unknown + +Format: debiandoc-sgml +Files: /usr/share/doc/ic35link/ic35link.sgml.gz + +Format: postscript +Files: /usr/share/doc/ic35link/ic35link.ps.gz + +Format: text +Files: /usr/share/doc/ic35link/ic35link.text.gz + +Format: HTML +Index: /usr/share/doc/ic35link/html/index.html +Files: /usr/share/doc/ic35link/html/*.html diff --git a/debian/ic35link.substvars b/debian/ic35link.substvars new file mode 100644 index 0000000..c2cdbe2 --- /dev/null +++ b/debian/ic35link.substvars @@ -0,0 +1,3 @@ +shlibs:Depends=libc6 (>= 2.15) +misc:Depends= +misc:Pre-Depends= diff --git a/debian/patches/fix-formats b/debian/patches/fix-formats new file mode 100644 index 0000000..9a90a54 --- /dev/null +++ b/debian/patches/fix-formats @@ -0,0 +1,16 @@ +--- a/src/datatxt.c ++++ b/src/datatxt.c +@@ -67,11 +67,11 @@ + ++idx; /* increment rec.index */ + } + get_ic35recdata( rec, NULL, &len ); +- fprintf(outfp,"File \"%s\" Record %3d IC35id=%08lX cflag=%02X length=%d\n", ++ fprintf(outfp,"File \"%s\" Record %3d IC35id=%08lX cflag=%02X length=%ld\n", + ic35fname(fileid), idx, ic35recid(rec), ic35recchg(rec), len); + for ( fi = 0; fi < ic35fnflds( fileid ); ++fi ) { + get_ic35recfld( rec, FILEfld(fileid,fi), &fld, &len ); +- fprintf( outfp, "f%d(%d)\t", fi, len ); ++ fprintf( outfp, "f%d(%ld)\t", fi, len ); + if ( len != 0 ) { + fprintf( outfp, "\"" ); + for ( i = 0; i < len; ++i ) { diff --git a/debian/patches/fix-label b/debian/patches/fix-label new file mode 100644 index 0000000..bafccb5 --- /dev/null +++ b/debian/patches/fix-label @@ -0,0 +1,11 @@ +--- a/src/ic35mgr.c ++++ b/src/ic35mgr.c +@@ -519,7 +519,7 @@ + mmcget(ic35path, NULL); + free(ic35path); + break; +- default: ++ default:; + } + } + rc = mmc_closedir( dirp ); diff --git a/debian/patches/gcc-build-workaround b/debian/patches/gcc-build-workaround new file mode 100644 index 0000000..4757b16 --- /dev/null +++ b/debian/patches/gcc-build-workaround @@ -0,0 +1,14 @@ +Adding several $CFLAGs as a workaround for building this currently broken package with current gcc versions +--- a/configure.in ++++ b/configure.in +@@ -116,6 +116,10 @@ + fi + fi + ++dnl --------------------------------------------------------------------------- ++dnl Add several cflags as a workaround ++dnl --------------------------------------------------------------------------- ++CFLAGS="$CFLAGS -Wno-format-overflow -Wno-stringop-overflow -Wno-builtin-declaration-mismatch -Wno-aggressive-loop-optimizations -Wno-unused-result -Wno-pointer-to-int-cast" + + dnl --------------------------------------------------------------------------- + dnl generate the config header diff --git a/debian/patches/series b/debian/patches/series new file mode 100644 index 0000000..3cd6af8 --- /dev/null +++ b/debian/patches/series @@ -0,0 +1,3 @@ +gcc-build-workaround +fix-formats +fix-label diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..31f06c9 --- /dev/null +++ b/debian/rules @@ -0,0 +1,25 @@ +#!/usr/bin/make -f +# See debhelper(7) (uncomment to enable) +# output every command that modifies files on the build system. +#export DH_VERBOSE = 1 + + +# see FEATURE AREAS in dpkg-buildflags(1) +#export DEB_BUILD_MAINT_OPTIONS = hardening=+all + +# see ENVIRONMENT in dpkg-buildflags(1) +# package maintainers to append CFLAGS +#export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic +# package maintainers to append LDFLAGS +#export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed + + +%: + dh $@ --with autoreconf + + +# dh_make generated override targets +# This is example for Cmake (See https://bugs.debian.org/641051 ) +#override_dh_auto_configure: +# dh_auto_configure -- # -DCMAKE_LIBRARY_PATH=$(DEB_HOST_MULTIARCH) + diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..163aaf8 --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/depcomp b/depcomp new file mode 100755 index 0000000..6b39162 --- /dev/null +++ b/depcomp @@ -0,0 +1,791 @@ +#! /bin/sh +# depcomp - compile a program generating dependencies as side-effects + +scriptversion=2018-03-07.03; # UTC + +# Copyright (C) 1999-2020 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Originally written by Alexandre Oliva . + +case $1 in + '') + echo "$0: No command. Try '$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: depcomp [--help] [--version] PROGRAM [ARGS] + +Run PROGRAMS ARGS to compile a file, generating dependencies +as side-effects. + +Environment variables: + depmode Dependency tracking mode. + source Source file read by 'PROGRAMS ARGS'. + object Object file output by 'PROGRAMS ARGS'. + DEPDIR directory where to store dependencies. + depfile Dependency file to output. + tmpdepfile Temporary file to use when outputting dependencies. + libtool Whether libtool is used (yes/no). + +Report bugs to . +EOF + exit $? + ;; + -v | --v*) + echo "depcomp $scriptversion" + exit $? + ;; +esac + +# Get the directory component of the given path, and save it in the +# global variables '$dir'. Note that this directory component will +# be either empty or ending with a '/' character. This is deliberate. +set_dir_from () +{ + case $1 in + */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;; + *) dir=;; + esac +} + +# Get the suffix-stripped basename of the given path, and save it the +# global variable '$base'. +set_base_from () +{ + base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'` +} + +# If no dependency file was actually created by the compiler invocation, +# we still have to create a dummy depfile, to avoid errors with the +# Makefile "include basename.Plo" scheme. +make_dummy_depfile () +{ + echo "#dummy" > "$depfile" +} + +# Factor out some common post-processing of the generated depfile. +# Requires the auxiliary global variable '$tmpdepfile' to be set. +aix_post_process_depfile () +{ + # If the compiler actually managed to produce a dependency file, + # post-process it. + if test -f "$tmpdepfile"; then + # Each line is of the form 'foo.o: dependency.h'. + # Do two passes, one to just change these to + # $object: dependency.h + # and one to simply output + # dependency.h: + # which is needed to avoid the deleted-header problem. + { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile" + sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile" + } > "$depfile" + rm -f "$tmpdepfile" + else + make_dummy_depfile + fi +} + +# A tabulation character. +tab=' ' +# A newline character. +nl=' +' +# Character ranges might be problematic outside the C locale. +# These definitions help. +upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ +lower=abcdefghijklmnopqrstuvwxyz +digits=0123456789 +alpha=${upper}${lower} + +if test -z "$depmode" || test -z "$source" || test -z "$object"; then + echo "depcomp: Variables source, object and depmode must be set" 1>&2 + exit 1 +fi + +# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. +depfile=${depfile-`echo "$object" | + sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} +tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} + +rm -f "$tmpdepfile" + +# Avoid interferences from the environment. +gccflag= dashmflag= + +# Some modes work just like other modes, but use different flags. We +# parameterize here, but still list the modes in the big case below, +# to make depend.m4 easier to write. Note that we *cannot* use a case +# here, because this file can only contain one case statement. +if test "$depmode" = hp; then + # HP compiler uses -M and no extra arg. + gccflag=-M + depmode=gcc +fi + +if test "$depmode" = dashXmstdout; then + # This is just like dashmstdout with a different argument. + dashmflag=-xM + depmode=dashmstdout +fi + +cygpath_u="cygpath -u -f -" +if test "$depmode" = msvcmsys; then + # This is just like msvisualcpp but w/o cygpath translation. + # Just convert the backslash-escaped backslashes to single forward + # slashes to satisfy depend.m4 + cygpath_u='sed s,\\\\,/,g' + depmode=msvisualcpp +fi + +if test "$depmode" = msvc7msys; then + # This is just like msvc7 but w/o cygpath translation. + # Just convert the backslash-escaped backslashes to single forward + # slashes to satisfy depend.m4 + cygpath_u='sed s,\\\\,/,g' + depmode=msvc7 +fi + +if test "$depmode" = xlc; then + # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information. + gccflag=-qmakedep=gcc,-MF + depmode=gcc +fi + +case "$depmode" in +gcc3) +## gcc 3 implements dependency tracking that does exactly what +## we want. Yay! Note: for some reason libtool 1.4 doesn't like +## it if -MD -MP comes after the -MF stuff. Hmm. +## Unfortunately, FreeBSD c89 acceptance of flags depends upon +## the command line argument order; so add the flags where they +## appear in depend2.am. Note that the slowdown incurred here +## affects only configure: in makefiles, %FASTDEP% shortcuts this. + for arg + do + case $arg in + -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;; + *) set fnord "$@" "$arg" ;; + esac + shift # fnord + shift # $arg + done + "$@" + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + mv "$tmpdepfile" "$depfile" + ;; + +gcc) +## Note that this doesn't just cater to obsosete pre-3.x GCC compilers. +## but also to in-use compilers like IMB xlc/xlC and the HP C compiler. +## (see the conditional assignment to $gccflag above). +## There are various ways to get dependency output from gcc. Here's +## why we pick this rather obscure method: +## - Don't want to use -MD because we'd like the dependencies to end +## up in a subdir. Having to rename by hand is ugly. +## (We might end up doing this anyway to support other compilers.) +## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like +## -MM, not -M (despite what the docs say). Also, it might not be +## supported by the other compilers which use the 'gcc' depmode. +## - Using -M directly means running the compiler twice (even worse +## than renaming). + if test -z "$gccflag"; then + gccflag=-MD, + fi + "$@" -Wp,"$gccflag$tmpdepfile" + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + # The second -e expression handles DOS-style file names with drive + # letters. + sed -e 's/^[^:]*: / /' \ + -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" +## This next piece of magic avoids the "deleted header file" problem. +## The problem is that when a header file which appears in a .P file +## is deleted, the dependency causes make to die (because there is +## typically no way to rebuild the header). We avoid this by adding +## dummy dependencies for each header file. Too bad gcc doesn't do +## this for us directly. +## Some versions of gcc put a space before the ':'. On the theory +## that the space means something, we add a space to the output as +## well. hp depmode also adds that space, but also prefixes the VPATH +## to the object. Take care to not repeat it in the output. +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +sgi) + if test "$libtool" = yes; then + "$@" "-Wp,-MDupdate,$tmpdepfile" + else + "$@" -MDupdate "$tmpdepfile" + fi + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + + if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files + echo "$object : \\" > "$depfile" + # Clip off the initial element (the dependent). Don't try to be + # clever and replace this with sed code, as IRIX sed won't handle + # lines with more than a fixed number of characters (4096 in + # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; + # the IRIX cc adds comments like '#:fec' to the end of the + # dependency line. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \ + | tr "$nl" ' ' >> "$depfile" + echo >> "$depfile" + # The second pass generates a dummy entry for each header file. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ + >> "$depfile" + else + make_dummy_depfile + fi + rm -f "$tmpdepfile" + ;; + +xlc) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +aix) + # The C for AIX Compiler uses -M and outputs the dependencies + # in a .u file. In older versions, this file always lives in the + # current directory. Also, the AIX compiler puts '$object:' at the + # start of each line; $object doesn't have directory information. + # Version 6 uses the directory in both cases. + set_dir_from "$object" + set_base_from "$object" + if test "$libtool" = yes; then + tmpdepfile1=$dir$base.u + tmpdepfile2=$base.u + tmpdepfile3=$dir.libs/$base.u + "$@" -Wc,-M + else + tmpdepfile1=$dir$base.u + tmpdepfile2=$dir$base.u + tmpdepfile3=$dir$base.u + "$@" -M + fi + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + do + test -f "$tmpdepfile" && break + done + aix_post_process_depfile + ;; + +tcc) + # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26 + # FIXME: That version still under development at the moment of writing. + # Make that this statement remains true also for stable, released + # versions. + # It will wrap lines (doesn't matter whether long or short) with a + # trailing '\', as in: + # + # foo.o : \ + # foo.c \ + # foo.h \ + # + # It will put a trailing '\' even on the last line, and will use leading + # spaces rather than leading tabs (at least since its commit 0394caf7 + # "Emit spaces for -MD"). + "$@" -MD -MF "$tmpdepfile" + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'. + # We have to change lines of the first kind to '$object: \'. + sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile" + # And for each line of the second kind, we have to emit a 'dep.h:' + # dummy dependency, to avoid the deleted-header problem. + sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile" + rm -f "$tmpdepfile" + ;; + +## The order of this option in the case statement is important, since the +## shell code in configure will try each of these formats in the order +## listed in this file. A plain '-MD' option would be understood by many +## compilers, so we must ensure this comes after the gcc and icc options. +pgcc) + # Portland's C compiler understands '-MD'. + # Will always output deps to 'file.d' where file is the root name of the + # source file under compilation, even if file resides in a subdirectory. + # The object file name does not affect the name of the '.d' file. + # pgcc 10.2 will output + # foo.o: sub/foo.c sub/foo.h + # and will wrap long lines using '\' : + # foo.o: sub/foo.c ... \ + # sub/foo.h ... \ + # ... + set_dir_from "$object" + # Use the source, not the object, to determine the base name, since + # that's sadly what pgcc will do too. + set_base_from "$source" + tmpdepfile=$base.d + + # For projects that build the same source file twice into different object + # files, the pgcc approach of using the *source* file root name can cause + # problems in parallel builds. Use a locking strategy to avoid stomping on + # the same $tmpdepfile. + lockdir=$base.d-lock + trap " + echo '$0: caught signal, cleaning up...' >&2 + rmdir '$lockdir' + exit 1 + " 1 2 13 15 + numtries=100 + i=$numtries + while test $i -gt 0; do + # mkdir is a portable test-and-set. + if mkdir "$lockdir" 2>/dev/null; then + # This process acquired the lock. + "$@" -MD + stat=$? + # Release the lock. + rmdir "$lockdir" + break + else + # If the lock is being held by a different process, wait + # until the winning process is done or we timeout. + while test -d "$lockdir" && test $i -gt 0; do + sleep 1 + i=`expr $i - 1` + done + fi + i=`expr $i - 1` + done + trap - 1 2 13 15 + if test $i -le 0; then + echo "$0: failed to acquire lock after $numtries attempts" >&2 + echo "$0: check lockdir '$lockdir'" >&2 + exit 1 + fi + + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + # Each line is of the form `foo.o: dependent.h', + # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. + # Do two passes, one to just change these to + # `$object: dependent.h' and one to simply `dependent.h:'. + sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process this invocation + # correctly. Breaking it into two sed invocations is a workaround. + sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp2) + # The "hp" stanza above does not work with aCC (C++) and HP's ia64 + # compilers, which have integrated preprocessors. The correct option + # to use with these is +Maked; it writes dependencies to a file named + # 'foo.d', which lands next to the object file, wherever that + # happens to be. + # Much of this is similar to the tru64 case; see comments there. + set_dir_from "$object" + set_base_from "$object" + if test "$libtool" = yes; then + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir.libs/$base.d + "$@" -Wc,+Maked + else + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir$base.d + "$@" +Maked + fi + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile1" "$tmpdepfile2" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" + do + test -f "$tmpdepfile" && break + done + if test -f "$tmpdepfile"; then + sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile" + # Add 'dependent.h:' lines. + sed -ne '2,${ + s/^ *// + s/ \\*$// + s/$/:/ + p + }' "$tmpdepfile" >> "$depfile" + else + make_dummy_depfile + fi + rm -f "$tmpdepfile" "$tmpdepfile2" + ;; + +tru64) + # The Tru64 compiler uses -MD to generate dependencies as a side + # effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'. + # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put + # dependencies in 'foo.d' instead, so we check for that too. + # Subdirectories are respected. + set_dir_from "$object" + set_base_from "$object" + + if test "$libtool" = yes; then + # Libtool generates 2 separate objects for the 2 libraries. These + # two compilations output dependencies in $dir.libs/$base.o.d and + # in $dir$base.o.d. We have to check for both files, because + # one of the two compilations can be disabled. We should prefer + # $dir$base.o.d over $dir.libs/$base.o.d because the latter is + # automatically cleaned when .libs/ is deleted, while ignoring + # the former would cause a distcleancheck panic. + tmpdepfile1=$dir$base.o.d # libtool 1.5 + tmpdepfile2=$dir.libs/$base.o.d # Likewise. + tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504 + "$@" -Wc,-MD + else + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir$base.d + tmpdepfile3=$dir$base.d + "$@" -MD + fi + + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + do + test -f "$tmpdepfile" && break + done + # Same post-processing that is required for AIX mode. + aix_post_process_depfile + ;; + +msvc7) + if test "$libtool" = yes; then + showIncludes=-Wc,-showIncludes + else + showIncludes=-showIncludes + fi + "$@" $showIncludes > "$tmpdepfile" + stat=$? + grep -v '^Note: including file: ' "$tmpdepfile" + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + # The first sed program below extracts the file names and escapes + # backslashes for cygpath. The second sed program outputs the file + # name when reading, but also accumulates all include files in the + # hold buffer in order to output them again at the end. This only + # works with sed implementations that can handle large buffers. + sed < "$tmpdepfile" -n ' +/^Note: including file: *\(.*\)/ { + s//\1/ + s/\\/\\\\/g + p +}' | $cygpath_u | sort -u | sed -n ' +s/ /\\ /g +s/\(.*\)/'"$tab"'\1 \\/p +s/.\(.*\) \\/\1:/ +H +$ { + s/.*/'"$tab"'/ + G + p +}' >> "$depfile" + echo >> "$depfile" # make sure the fragment doesn't end with a backslash + rm -f "$tmpdepfile" + ;; + +msvc7msys) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +#nosideeffect) + # This comment above is used by automake to tell side-effect + # dependency tracking mechanisms from slower ones. + +dashmstdout) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout, regardless of -o. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + # Remove '-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + test -z "$dashmflag" && dashmflag=-M + # Require at least two characters before searching for ':' + # in the target name. This is to cope with DOS-style filenames: + # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise. + "$@" $dashmflag | + sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile" + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process this sed invocation + # correctly. Breaking it into two sed invocations is a workaround. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +dashXmstdout) + # This case only exists to satisfy depend.m4. It is never actually + # run, as this mode is specially recognized in the preamble. + exit 1 + ;; + +makedepend) + "$@" || exit $? + # Remove any Libtool call + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + # X makedepend + shift + cleared=no eat=no + for arg + do + case $cleared in + no) + set ""; shift + cleared=yes ;; + esac + if test $eat = yes; then + eat=no + continue + fi + case "$arg" in + -D*|-I*) + set fnord "$@" "$arg"; shift ;; + # Strip any option that makedepend may not understand. Remove + # the object too, otherwise makedepend will parse it as a source file. + -arch) + eat=yes ;; + -*|$object) + ;; + *) + set fnord "$@" "$arg"; shift ;; + esac + done + obj_suffix=`echo "$object" | sed 's/^.*\././'` + touch "$tmpdepfile" + ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" + rm -f "$depfile" + # makedepend may prepend the VPATH from the source file name to the object. + # No need to regex-escape $object, excess matching of '.' is harmless. + sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process the last invocation + # correctly. Breaking it into two sed invocations is a workaround. + sed '1,2d' "$tmpdepfile" \ + | tr ' ' "$nl" \ + | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" "$tmpdepfile".bak + ;; + +cpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + # Remove '-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + "$@" -E \ + | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ + -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ + | sed '$ s: \\$::' > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + cat < "$tmpdepfile" >> "$depfile" + sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvisualcpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + IFS=" " + for arg + do + case "$arg" in + -o) + shift + ;; + $object) + shift + ;; + "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") + set fnord "$@" + shift + shift + ;; + *) + set fnord "$@" "$arg" + shift + shift + ;; + esac + done + "$@" -E 2>/dev/null | + sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile" + echo "$tab" >> "$depfile" + sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvcmsys) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +none) + exec "$@" + ;; + +*) + echo "Unknown depmode $depmode" 1>&2 + exit 1 + ;; +esac + +exit 0 + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC0" +# time-stamp-end: "; # UTC" +# End: diff --git a/doc/.cvsignore b/doc/.cvsignore new file mode 100644 index 0000000..282522d --- /dev/null +++ b/doc/.cvsignore @@ -0,0 +1,2 @@ +Makefile +Makefile.in diff --git a/doc/CVS/Entries b/doc/CVS/Entries new file mode 100644 index 0000000..f4e3b1f --- /dev/null +++ b/doc/CVS/Entries @@ -0,0 +1,5 @@ +/.cvsignore/1.1/Sun Feb 4 22:55:23 2001// +/Makefile.am/1.2/Mon Feb 19 01:10:46 2001// +/ic35mgr.txt/1.10/Sat Feb 3 18:39:07 2001// +/ic35sync.txt/1.10/Sun Dec 3 21:10:00 2000// +D diff --git a/doc/CVS/Repository b/doc/CVS/Repository new file mode 100644 index 0000000..ef383d5 --- /dev/null +++ b/doc/CVS/Repository @@ -0,0 +1 @@ +ic35link/doc diff --git a/doc/CVS/Root b/doc/CVS/Root new file mode 100644 index 0000000..0f95ef3 --- /dev/null +++ b/doc/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ic35link.cvs.sourceforge.net:/cvsroot/ic35link diff --git a/doc/Makefile.am b/doc/Makefile.am new file mode 100644 index 0000000..352299d --- /dev/null +++ b/doc/Makefile.am @@ -0,0 +1,17 @@ +## Process this file with automake to produce Makefile.in +## $Id: Makefile.am,v 1.2 2001/02/19 01:10:46 tsch Rel $ +## Copyright (C) 2001 Thomas Schulz +## +## automakefile for ic35link/doc + +pkgdata_DATA = \ + ic35sync.txt \ + ic35mgr.txt + +EXTRA_DIST = $(pkgdata_DATA) + +if MAINTAINER_MODE +MAINTAINERCLEANFILES = Makefile.in +else +MAINTAINERCLEANFILES = +endif diff --git a/doc/ic35mgr.txt b/doc/ic35mgr.txt new file mode 100644 index 0000000..209fa3e --- /dev/null +++ b/doc/ic35mgr.txt @@ -0,0 +1,783 @@ + $Id: ic35mgr.txt,v 1.10 2001/02/03 18:39:07 tsch Rel $ + IC35 Manager Protokoll + ====================== + +Inhalt +------ + IC35-Manager Operationen + IC35-Manager Protokoll + Basiskommandos + Datenblock senden PC->IC35 + Datenblock empfangen PC<-IC35 + Verbindungsaufbau + Verbindungsabbau + IC35 Datensicherung + IC35 Datenwiederherstellung + Protokoll MMCard-Operationen + MMCard Operationen + MMCard Status + MMCard Label + MMCard Directory Operationen + MMCard Directory Open + MMCard Directory Length + MMCard Directory Entry + MMCard Directory Close + MMCard File Operationen + MMCard File Open + MMCard File Status + MMCard File Read + MMCard File Write + MMCard File Close + MMCard File Delete + Anhang: Logfiles der Protokoll-Analyse + + +??? Unklarheiten ueber das IC35 Manager Protokoll sind wie hier mit ??? +??? am Zeilenanfang markiert. + + +IC35-Manager Operationen +------------------------ + - Verbindungsaufbau mit IC35 + - Welcome + - Identifikation und Status IC35 + - Verbindung zum IC35 trennen + - Sichern, Wiederherstellen + - IC35 Datensicherung nach database.org + - IC35 Datenwiederhestellung aus database.org + - MMCard Operationen + - Status, Inhalt IC35 MMCard + - Datei Transfer nach IC35 MMCard + - Datei Transfer von IC35 MMCard + - Datei loeschen in IC35 MMCard + (noch) nicht analysiert wurden: +??? - Transfer Applikationsprogramm ins IC35 +??? - Applikationsprogramm im IC35 loeschen +??? - Transfer einer Grafik-Bitmapdatei ins IC35 +??? - Transfer einer MIDI-Datei ins IC35 + + Fortschrittsanzeigen des IC35 Manager unter Windows: + - Zugriff auf IC35 Info .. + - Zugriff erfolgreich + - MMCard1 wird getestet + - ..\IC35 + - ..\APP + - ..\<*.APP ..> + - ..\<2000 ..> + - MMCard2 wird getested + - keine MMCard2 vorhanden + Standardablauf IC35 Manager unter Windows + - Verbindungsaufbau mit IC35 + ausgeloest durch MouseClick "connect" am IC35 Manager + fordert Knopfdruck an SyncStation + - Status, Inhalt IC35 MMCard + - Verbindung zum IC35 trennen + ausgeloest durch MouseClick "disconnect" am IC35 Manager + + +IC35-Manager Protokoll +---------------------- + Die Leitungsparameter fuer die Kommunikation zwischen PC und + IC35 sind Baudrate 115200, No parity, 8 databits, 2 stopbits. + Die Basisoperationen des IC35-Manager Protokolls sind + - Basiskommando mit Bestaetigung + - Datenblock senden und Verifikation mit Pruefsumme + - Datenblock emfangen und Verifikation mit Pruefsumme + + Basiskommandos + Einige Basiskommandos des IC35-Manager Protokolls bestehen + jeweils aus einem 1byte-Kommando vom PC an den IC35, gefolgt + von einer 1byte-Antwort vom IC35 an den PC. Wenn die Antwort + vom IC35 ausbleibt, wird das Kommando wiederholt. + Kommunikation PC -> IC35, PC <- IC35: + -> command + timeout 0.5 sec + -> command + <- response + Die beobachteten Basiskommandos sind: + Verbindungsabbau + -> 01 + <- 90 + Reset Manager-Protokoll + -> 09 + <- 90 + Identifikation + -> 10 + <- 90 + <- "DCS_SDK" 00 + IC35 Datensicherung + -> 13 + <- 90 + IC35 Datenwiederherstellung + -> 14 + <- 90 + -> 70 + <- C0 + Einleitung MMCard-Operation + -> 15 + <- 90 + Info Datensicherung + -> 18 + <- 90 + <- 30 31 32 38 + Start des Verbindungsaufbaus + -> 40 + <- 80 + Positive Quittung + -> 60 + <- A0 + Negative Quittung + -> 62 + <- A0 + Dieser Protokollablauf der Basiskommandos ist mit Mcmdrsp() + in mgrproto.c implementiert. + + Datenblock senden PC->IC35 + Bei IC35-Datenwiederherstellung und MMCard-Operationen werden + Datenbloecke vom PC an den IC35 gesendet und die Uebertragung + mit einer Pruefsumme verifiziert und ggf. wiederholt. + Bei den MMCard-Operationen wird die Laenge des Datenblocks an + den IC35 gesendet und vom IC35 bestaetigt. Vor dem Senden der + Laenge muss 10 Millisekunden gewartet werden. + Bei IC35-Datenwiederherstellung unterbleibt dies, weil die + Laenge (136 oder 16384 Bytes) a priori feststeht. + Beim Senden des Datenblocks muss erfahrungsgemaess mindestens + alle 29 Bytes jeweils 10 Millisekunden gewartet werden. Offenbar + ist der IC35 nicht faehig, den Datenblock bei 115200 Baud ohne + diese Verzoegerungen korrekt zu empfangen. + Auf die Pruefsumme muss bis zu 10.0 Sekunden gewartet werden. + Kommunikation PC -> IC35, PC <- IC35: + -> nn_nn Laenge des Datenblocks, niederwertiges Byte zuerst + <- E0 Empgangsbestaetigung der Laenge + -> (Datenblock von nn_nn Bytes) + <- cc_cc Pruefsumme des Datenblocks, niederwertiges Byte zuerst + Die Pruefsumme ist die arithmetische Summe aller Bytes des + Datenblocks abgeschnitten auf 16 Bit. Wenn Datenblock-Bytes + verloren gehen (Overrun error im IC35) oder verfaelscht werden, + trifft die Pruefsumme vom IC35 erst ca. 8.2 Sekunden nach dem + Versand des Datenblocks ein. + Bei unstimmiger Pruefsumme 'cc_cc' wird negativ quittiert: + -> 62 negative Quittung + <- A0 Empfangsbestaetigung der Quittung + und der Datenblock erneut an IC35 gesendet. + Bei uebereinstimmender Pruefsumme 'cc_cc' wird positiv quittiert: + -> 60 positive Quittung + <- A0 Empfangsbestaetigung der Quittung + Wenn die Empfangsbestaetigung der Quittung vom IC35 ausbleibt, wird + die positive oder negative Quittung wiederholt. + Dieser Protokollablauf ist mit Msendblk() in mgrproto.c implementiert. + + Datenblock empfangen PC<-IC35 + Bei IC35-Datensicherung und MMCard-Operationen werden Datenbloecke + vom IC35 am PC empfangen und die Uebertragung mit einer Pruefsumme + verifiziert und ggf. wiederholt. + Bei den MMCard-Operationen wird die Laenge des Datenblocks vom + IC35 empfangen und bestaetigt. Auf die Laenge muss 3.5 Sekunden + gewartet werden. + Bei IC35-Datensicherung unterbleibt dies, weil die Laenge (136 oder + 16384 Bytes) a priori feststeht. + Kommunikation PC -> IC35, PC <- IC35: + <- nn_nn Laenge des Datenblocks, niederwertiges Byte zuerst + -> E0 Empgangsbestaetigung der Laenge + <- (Datenblock von nn_nn Bytes) + <- cc_cc Pruefsumme des Datenblocks, niederwertiges Byte zuerst + Bei unstimmiger Pruefsumme 'cc_cc' oder kuerzerer Laenge als + angekuendigt wird negativ quittiert: + -> 62 negative Quittung + <- A0 Empfangsbestaetigung der Quittung + und der Datenblock wird erneut vom IC35 empfangen. + Bei uebereinstimmender Pruefsumme 'cc_cc' wird positiv quittiert: + -> 60 positive Quittung + <- A0 Empfangsbestaetigung der Quittung + Wenn die Empfangsbestaetigung der Quittung vom IC35 ausbleibt, + wird die positive oder negative Quittung wiederholt. + Dieser Protokollablauf ist mit Mrecvblk() in mgrproto.c implementiert. + + +Verbindungsaufbau +----------------- + 1. Welcome Phase + - Der PC sendet wiederholt einzelne Zeichen "@" (hex 40) bis vom + IC35 "WELCOME" (hex 57 45 4C 43 4F 4D 45) empfangen wird. + Dies passiert durch Druecken des Sync-Station Knopfs. + - Der PC sendet noch einmal ein Zeichen "@" (hex 40), der IC35 + antwortet mit einem Byte hex 80. + Kommunikation PC -> IC35, PC <- IC35 + -> 40 + timeout 1.15 sec + -> 40 + sync-station button + <- 57 45 4C 43 4F 4D 45 + W E L C O M E + 2. Basiskommando "Start Verbindungsaufbau" + -> 40 + <- 80 + 3. Basiskommando "Reset" + -> 09 + <- 90 + 4. Basiskommando "Identifikation" + -> 10 + <- 90 + <- 44 43 53 5F 53 44 4B 00 + D C S _ S D K . + 5. optional Empfang des Statusblocks vom IC35 + -> FF + <- 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ... + ... FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF + Block von 16400 Bytes + enthaelt u.a. Applikationsnamen, Email,SMS config, .. + 6. Initialisierung + -> 50 + <- 90 or timeout 0.1 sec +??? Beim Initialisierunskommando wurde keine Antwort beobachtet. +??? Es wird 100 Millisekunden auf etwaige Antwort vom IC35 gewartet +??? und diese verworfen, um sie nicht irrtuemlich fuer eine Antwort +??? auf ein nachfolgendes Basiskommando zu halten. + Der IC35 braucht mindestens 10 Millisekunden Ruhe, bis er zu + weiteren Aktionen bereit ist. (Ansonsten muss das nachfolgende + erste Basiskommando wiederholt werden.) + + +Verbindungsabbau +---------------- + 1. Basiskommando "Reset" + -> 09 + timeout 1.0 sec + -> 09 + <- 90 + 2. Basiskommando "Verbindungsabbau" + -> 01 + timeout 1.0 sec + -> 01 + <- 90 + + +IC35 Datensicherung +------------------- + Der Ablauf der Datensicherung mit den Protokoll-Basisoperationen + (s.o "IC35-Manager Protokoll") ist: + 1. Basiskommmando "IC35 Datensicherung" + -> 13 + <- 90 + 2. Empfang des Kopfblocks (136 Bytes) vom IC35 + <- B5 03 00 A2 01 00 F1 .. headblock 136 bytes + <- 98 0D checksum + -> 60 positive acknowledge + <- A0 got ack + Hier und unten ist jeweils nur die positive Quittung erwaehnt, + bei falscher Pruefsumme oder Blocklaenge wird jeweils negativ + quittiert und der Kopf- oder Daten-Block wiederholt. + 3. Empfang von 26 Datenblocks (je 16384 Bytes) vom IC35 + <- A0 09 00 40 01 00 00 .. datablock 16384 bytes + <- C0 4C checksum + -> 60 positive acknowledge + <- A0 got ack + 4. Zweimal Basiskommando "Info Datensicherung" + -> 18 command info-1 + timeout 1.0 sec + -> 18 command info-1 + <- 90 response got command + <- 30 31 32 38 info-1 + -> 18 command info-2 + timeout 0.5 sec + -> 18 command info-2 + <- 90 response got command + <- 30 31 32 38 info-1 +??? Der Inhalt der info response 30 31 32 38 = "0128" aehnelt sehr +??? der Firmware-Version "1.28" des IC35, es ist jedoch noch unklar, +??? ob diese Aehnlichkeit realen Gehalt hat. + Alle Empfangsdaten der Datensicherung werden gespeichert in der + Datei 'database.org', dies sind 426128 = 136 + 26*16384 + 4 +4 + Bytes insgesamt (headblock, 26 datablocks, 2 infoblocks). + + +IC35 Datenwiederherstellung +--------------------------- + Der Ablauf der Datenwiederherstellung mit den Protokoll-Basis- + operationen (s.o "IC35-Manager Protokoll") ist: + 1. Zweimal Basiskommando "Info Datensicherung" + -> 18 command info-1 + timeout 1.0 sec + -> 18 command info-1 + <- 90 response got command + <- 30 31 32 38 info-1 + -> 18 command info-2 + timeout 0.5 sec + -> 18 command info-2 + <- 90 response got command + <- 30 31 32 38 info-1 + Die beiden Antworten 30 31 32 38 werden mit den Eintraegen am + Ende der Datei 'database.org' verglichen. Bei Uebereinstimmung + wird die Datenwiederherstellung fortgesetzt, andernfalls wird + sie abgebrochen. + 2. Basiskommmandos "IC35 Datenwiederherstellung" + -> 14 command restore-1 + timeout 1.0 sec + -> 14 command restore-1 + <- 90 response got command + -> 70 command restore-2 + <- C0 response got restore-2 + 3. Senden des Kopfblocks (136 Bytes) an IC35 + -> AD 03 00 9A 01 00 2B .. headblock 136 bytes + <- 5B 0D checksum + -> 60 positive acknowledge + <- A0 response got ack + 4. Senden von 26 Datenblocks (je 16384 Bytes) vom IC35 + Auf die Pruefsumme muss bis zu 10.0 sec gewartet werden. + -> A0 09 92 50 01 00 00 .. datablock 16384 bytes + <- 4D F3 checksum + -> 60 positive acknowledge + <- A0 response got ack + 5. Basiskommando "Reset Manager-Protokoll" + -> 09 command reset + timeout 1.0 sec + -> 09 command reset + timeout 1.0 sec + -> 09 command reset + timeout 1.0 sec + -> 09 command reset + timeout 0.5 sec + -> 09 command reset + <- 90 response got command + Wie oben sind die Inhalte von Kopf- und Daten-Blocks Beispiele + und es ist jeweils nur die positive Quittung aufgefuehrt, bei + falscher Pruefsumme wird jeweils negativ quittiert und der Kopf- + oder Daten-Block wiederholt. + Insgesamt werden 426120 = 136 + 26*16384 Bytes uebertragen, d.h. + ein Kopfblock von 136 Bytes und 26 Datenblocks mit 16384 Bytes. + Die 2 bei der Datensicherung empfangenen 'infoblocks' von je 4 + Bytes werden nicht an IC35 gesendet. +??? Bei der Datenwiederherstellung werden Datum und Uhrzeit sowie +??? Telefontyp auf Defaultwerte zurueckgesetzt! Sie muessen manuell +??? korrigiert werden! + + +Protokoll MMCard-Operationen +---------------------------- + Der allgemeine Ablauf der MMCard-Operationen mit den Protokoll + Basisoperationen (s.o "IC35-Manager Protokoll") ist: + 1. Basiskommando "MMCard-Operation" + -> 15 command mmcard + <- 90 got command + Vor dem Senden der Blocklaenge des Kommandoblocks muss 10 msec + gewartet werden. + 2. Senden des MMCard Kommandoblocks + -> nn_nn block length + <- E0 got length + -> (cmdblock of nn_nn bytes) + <- cc_cc got cmdblock, checksum is cc_cc (arithmetic, LSB first) + Bei unstimmiger Pruefsumme 'cc_cc' wird negativ quittiert: + -> 62 neg.ack + <- A0 got neg.ack, send block again + und der cmdblock erneut an IC35 gesendet. + Bei uebereinstimmender Pruefsumme 'cc_cc' wird positiv quittiert: + -> 60 pos.ack + <- A0 got pos.ack + 3. Empfang des MMCard Antwortblocks + <- nn_nn block length + -> E0 got length + <- (rspblock of nn_nn bytes) + <- cc_cc checksum of rspblock is cc_cc (arithmetic, LSB first) + Bei unstimmiger Pruefsumme 'cc_cc' wird negativ quittiert: + -> 62 neg.ack + <- A0 got neg.ack + und der rspblock wird erneut vom IC35 empfangen. + Bei uebereinstimmender Pruefsumme 'cc_cc' wird positiv quittiert: + -> 60 pos.ack + <- A0 got pos.ack + + +MMCard Operationen +------------------ + Im "IC35 Software Development Kit API Reference Guide" (ic35_api.pdf + aus ic35_prog.zip) sind unter "13. Multi Media Card File Operations" + beschrieben. Sie entsprechen weitgehend den MMCard PDUs der Protokoll + Analyse. Zu den mit "???" markierten Funktionen wurden keine PDUs + beobachtet, die PDU command codes sind reine Vermutung. + + IC35 SDK API Funktion mgrtrans.c PDU command code + +-----------------------+--------------------------------------------- + minitialCard mmc_status 20 "MMCard1" 00 +??? mFormat - 32 ??? +??? mSetCardLabel - 33 ??? + mGetCardLabel mmc_label 34 "MMCard1" 00 +??? mGetCardInfo - 35 ??? + mOpenFile mmc_openfile 22 mm mm "MMCard1\FILE.EXT" 00 +??? mSetFilePointer - 25 ??? + mWriteToFile mmc_writefile 23 ... + mReadFromFile mmc_readfile 24 ... + mGetFileInfo mmc_statfile 26 ... + mCloseFile mmc_closefile 27 ... +??? mRenFile - 21 ??? + mDeleteFile mmc_delfile 28 "MMCard1\FILE.EXT" 00 + mOpenDirectory mmc_opendir 2A mm mm "MMCard1\IC35" 00 + mGetDirectorySubItemNum mmc_opendir 2B ... + mGetDirectorySubItem mmc_readdir 2C ... +??? mGetDirectoryInfo - 2D ??? + mCloseDirectory mmc_closedir 2E ... +??? mRenDirectory - 29 ??? +??? mDeleteDirectory - 2F ??? + + MMCard Status + -> command status "MMCard1" + 20 4D 4D 43 61 72 64 31 00 + cmd M M C a r d 1 nul + <- response MMCard OK + 01 00 + <- response MMCard not present + FF FF + + MMCard Label + -> command label "MMCard1" + 34 4D 4D 43 61 72 64 31 00 + . M M C a r d 1 . + cmd mcard-id____________ nul + <- reponse MMCard label + 01 00 00 4D 4D 43 61 72 64 31 00 20 20 20 00 00 00 00 00 00 00 00 48 + . . . M M C a r d 1 . . . . . . . . . . . . H + ok label___________________________ ?? + + MMCard Directory Operationen + MMCard Directory Open empfaengt vom IC35 einen 'dirstat' Block, + welcher in allen folgenden Operationen jeweils an den IC35 + gesendet und vom IC35 aktualisert wieder empfangen wird. Die + Details ueber den 'dirstat' Block wurden "Mmc.h" aus dem IC35 + SoftwareDevelopmentKit entnommen, offenbar entsprechen sie + dem uebertragenen 'dirstat' Block. Beispiele: + Beispiel-1 "MMCard1" rootdir: + 5F 00 00 00 00 00 00 00 00 00 01 00 00 00 + sect_ clust sec_o dir_o sclst fileptr____ + 00 00 00 00 FF 00 00 00 00 20 00 FE 01 + filesize___ cn cclst cclsn csecn rsrvd + Beispiel-2 "MMCard1\IC35\APP" + 00 00 02 00 00 00 40 00 15 00 01 00 00 00 + sect_ clust sec_o dir_o sclst fileptr____ + 00 00 00 00 FF 15 00 00 00 20 00 FE 01 + filesize___ cn cclst cclsn csecn rsrvd + MMCard Directory Open + Beispiel-1 "MMCard1" rootdir + -> command opendir "MMCard1" + 2A 01 00 4D 4D 43 61 72 64 31 00 + . . . M M C a r d 1 . + cmd dirpath="MMCard1"___ nul + <- response + 01 00 + ok + 5F 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + dirstat________________________________________ + 00 00 00 00 00 00 00 00 00 FE 01 + _________________________dirstat + Beispiel-2 "MMCard1\IC35\APP" + -> command opendir "MMCard1 + 2A 01 00 4D 4D 43 61 72 64 31 5C 49 43 33 35 5C 41 50 50 00 + . . . M M C a r d 1 \ I C 3 5 \ A P P . + cmd dirpath="MMCard1\IC35\APP"_____________________ nul + <- response + 01 00 + ok + 00 00 02 00 00 00 40 00 15 00 00 00 00 00 00 00 + dirstat________________________________________ + 00 00 00 15 00 00 00 00 00 FE 01 + _________________________dirstat + MMCard Directory Length + Beispiel-1 "MMCard1" rootdir + -> command dirlen + 2B + cmd + 5F 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + dirstat_from_opendir___________________________ + 00 00 00 00 00 00 00 00 00 FE 01 00 + _________________________dirstat nul + <- response dirlen + 5F 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + dirstat________________________________________ + 00 00 00 00 00 00 00 00 00 FE 01 01 00 04 00 + _________________________dirstat ? ? ndent + Beispiel-2 "MMCard1\IC35\APP" + -> command dirlen + 2B + cmd + 00 00 02 00 00 00 40 00 15 00 00 00 00 00 00 00 + dirstat_from_opendir___________________________ + 00 00 00 15 00 00 00 00 00 FE 01 00 + _________________________dirstat nul + <- response dirlen + 00 00 02 00 00 00 40 00 15 00 00 00 00 00 00 00 + dirstat________________________________________ + 00 00 00 15 00 00 00 00 00 FE 01 01 00 07 00 + _________________________dirstat ? ? ndent + MMCard Directory Entry + Beispiel-1 "MMCard1" rootdir + -> command readdir index=0000 + 2C + cmd + 5F 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + dirstat_from_opendir_or_previous_readdir_______ + 00 00 00 00 00 00 00 00 00 FE 01 01 00 00 + _________________________dirstat idx+1 nul + <- respnse readdir + 5F 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 + dirstat______________________ nxidx ___________ + 00 00 FF 00 00 00 00 20 00 FE 01 01 00 + _________________________dirstat ? ? + 49 43 33 35 00 20 20 20 00 00 20 20 00 10 + filename="IC35"___________ ext=""_____ ty + 2E B3 5E 29 00 00 00 00 00 00 + timestamp__ ? ? size_______ + -> command readdir index=0001 + 2C + cmd + 5F 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 + dirstat_from_opendir_or_previous_readdir_______ + 00 00 FF 00 00 00 00 20 00 FE 01 02 00 00 + _________________________dirstat idx+1 nul + <- reponse readdir + 5F 00 00 00 00 00 00 00 00 00 02 00 00 00 00 00 + dirstat______________________ nxidx ___________ + 00 00 FF 00 00 00 00 40 00 FE 01 01 00 + _________________________dirstat ? ? + 32 30 30 30 31 31 30 34 00 49 33 35 00 20 + filename="20001104"_______ ext="I35"__ ty + C1 14 65 29 00 00 FE 89 00 00 + timestamp__ ? ? size_______ + Beispiel-2 "MMCard1\IC35\APP" + -> command readdir index=0000 + 2C + cmd + 00 00 02 00 00 00 40 00 15 00 00 00 00 00 00 00 + dirstat________________________________________ + 00 00 00 15 00 00 00 00 00 FE 01 01 00 00 + _________________________dirstat idx+1 nul + <- response readdir + 00 00 02 00 00 00 40 00 15 00 01 00 00 00 00 00 + dirstat______________________ nxidx ___________ + 00 00 FF 15 00 00 00 20 00 FE 01 01 00 + _________________________dirstat ? ? + 2E 2E 00 20 20 20 20 20 00 00 20 20 00 10 + filename=".."_____________ ext=""_____ ty + A1 B5 61 29 00 00 00 00 00 00 + timestamp__ ? ? size_______ + -> command readdir index=0001 + 2C + cmd + 00 00 02 00 00 00 40 00 15 00 01 00 00 00 00 00 + dirstat______________________ index ___________ + 00 00 FF 15 00 00 00 20 00 FE 01 02 00 00 + _________________________dirstat idx+1 nul + <- response readdir + 00 00 02 00 00 00 40 00 15 00 02 00 00 00 00 00 + dirstat______________________ nxidx ___________ + 00 00 FF 15 00 00 00 40 00 FE 01 01 00 + _________________________dirstat ? ? + 42 52 4F 57 53 45 52 00 00 41 50 50 00 20 + filename="BROWSER"________ ext="APP"__ ty + 89 B6 61 29 00 00 44 C0 05 00 + timestamp__ ? ? size_______ + Fuer das Typfeld 'ty' wurden beobachtet + 10 Subdirectory + 20 File + Die Kodierung des Zeitstempels 'timestamp' ist wie bei DOS. + Die hoeherwertigen 16 Bits kodieren Jahr, Monat und Tag: + yyyyyyy mmmm ddddd + yyyyyyy Jahre seit 1980 + mmmm Monat 1-12 + ddddd Tag 1-31 + Die niederwertigen 16 Bits kodieren Stunde, Minute, Sekunde: + hhhhh mmmmmm sssss + hhhhh Stunde + mmmmmm Minute + sssss Sekunde / 2 + MMCard Directory Close + Beispiel-1 "MMCard1" rootdir + -> command closedir + 2E + cmd + 5F 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 + dirstat______________________ index ___________ + 00 00 FF 00 00 00 00 80 00 FE 01 00 + _________________________dirstat nul + <- response closedir + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 + Beispiel-2 "MMCard1\IC35\APP" + -> command closedir + 2E + cmd + 00 00 02 00 00 00 40 00 15 00 07 00 00 00 00 00 + dirstat______________________ index ___________ + 00 00 FF 15 00 00 00 E0 00 FE 01 00 + _________________________dirstat nul + <- response closedir + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 + + MMCard File Operationen + MMCard File Open + -> command openfile + 22 01 00 4D 4D 43 61 72 64 31 5C 49 43 33 35 5C 41 50 50 + mo_de filepath="MMCard1\IC35\APP\REVERSI.APP"________ + 5C 52 45 56 45 52 53 49 2E 41 50 50 00 + ___________________________filepath nul + Der mo_de Parameter ist 01 00 (IC35 SDK: OPEN_EXISTING) zum Oeffnen + einer existierended MMCard Datei (im Lesemodus). + Der mo_de Parameter ist 00 00 (IC35 SDK: CREATE_ALWAYS) zum Erzeugen + einer neuen MMCard Datei, eine existierende Datei wird auf Laenge 0 + gekuerzt. + <- response openfile + 01 00 + ok + 00 00 15 00 00 00 C0 00 F9 00 00 00 00 00 + filestatus_______________________________ + 44 40 00 00 00 F9 00 00 00 00 00 FE 01 + size=16452_ filestatus________________ + MMCard File Status + -> command filestat + 26 + 00 00 15 00 00 00 C0 00 F9 00 00 00 00 00 + filestatus_______________________________ + 44 40 00 00 00 F9 00 00 00 00 00 FE 01 00 + size=16452_ filestatus________________ nul + <- response filestat + 00 00 15 00 00 00 C0 00 F9 00 00 00 00 00 + 44 40 00 00 00 F9 00 00 00 00 00 FE 01 01 00 + 52 45 56 45 52 53 49 00 00 41 50 50 00 20 + filename="REVERSI"________ ext="APP"__ ty + DD B6 61 29 00 00 44 40 00 00 + timestamp__ ? ? size_______ + MMCard File Read + -> command fileread + 24 + cmd + 00 00 15 00 00 00 C0 00 F9 00 00 00 00 00 + filestatus_______________________________ + 44 40 00 00 00 F9 00 00 00 00 00 FE 01 01 18 00 + size=16452_ filestatus________________ rdlen nul + <- response fileread + 00 00 15 00 00 00 C0 00 F9 00 01 18 00 00 + filestatus_______________________________ + 44 40 00 00 00 FC 00 03 00 00 00 FE 01 01 00 01 18 + size=16452_ filestatus________________ ? ? rdlen + 80 F6 00 40 00 00 00 40 00 00 FF FF 1E 00 52 .. + filedata_____________________________________.. + -> command fileread + 24 + cmd + 00 00 15 00 00 00 C0 00 F9 00 01 18 00 00 + filestatus_______________________________ + 44 40 00 00 00 FC 00 03 00 00 00 FE 01 01 18 00 + size=16452_ filestatus________________ rdlen nul + <- response fileread + 00 00 15 00 00 00 C0 00 F9 00 02 30 00 00 + filestatus___________________ noffs _____ + 44 40 00 00 00 FF 00 06 00 00 00 FE 01 01 00 01 18 + size=16452_ filestatus________________ ? ? rdlen + 36 01 23 36 00 21 0E 00 39 5E 23 56 EB 29 29 .. + filedata_____________________________________.. + -> command fileread + 24 + cmd + 00 00 15 00 00 00 C0 00 F9 00 02 30 00 00 + filestatus_______________________________ + 44 40 00 00 00 FF 00 06 00 00 00 FE 01 42 10 00 + size=16452_ filestatus________________ rdlen nul + <- response fileread + 00 00 15 00 00 00 C0 00 F9 00 44 40 00 00 + filestatus___________________ noffs _____ + 44 40 00 00 00 01 01 08 00 00 00 FE 01 01 00 42 10 + size=16452_ filestatus________________ ? ? rdlen + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 .. + filedata_____________________________________.. + MMCard File Write + -> command filewrite + 23 + cmd + 00 00 15 00 00 00 00 01 2F 01 00 00 00 00 + filestatus_______________________________ + 00 00 00 00 00 2F 01 00 00 00 00 FE 01 01 18 + size=0_____ filestatus________________ wrlen + 80 F6 00 40 00 00 00 40 00 00 FF FF 1E 00 52 .. + filedata_____________________________________.. + <- response filewrite + 00 00 15 00 00 00 00 01 2F 01 01 18 00 00 + filestatus___________________ noffs______ + 01 18 00 00 00 32 01 03 00 00 00 FE 01 01 00 + size=6145__ filestatus________________ ok___ + -> command filewrite + 23 + cmd + 00 00 15 00 00 00 00 01 2F 01 01 18 00 00 + filestatus___________________ noffs______ + 01 18 00 00 00 32 01 03 00 00 00 FE 01 01 18 + size=6145__ filestatus________________ wrlen + 36 01 23 36 00 21 0E 00 39 5E 23 56 EB 29 29 .. + filedata_____________________________________.. + <- response filewrite + 00 00 15 00 00 00 00 01 2F 01 02 30 00 00 + filestatus___________________ noffs______ + 02 30 00 00 00 35 01 06 00 00 00 FE 01 01 00 + size=12290_ filestatus________________ ok___ + MMCard File Close + -> command fileclose + 27 + cmd + 00 00 15 00 00 00 C0 00 F9 00 44 40 00 00 + filestatus___________________ noffs _____ + 44 40 00 00 00 01 01 08 00 00 00 FE 01 00 + size=16452_ filestatus________________ nul + <- response fileclose + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 + MMCard File Delete + -> command filedel + 28 4D 4D 43 61 72 64 31 5C 49 43 33 35 5C 41 50 50 + cmd filepath="MMCard1\IC35\APP\REVERSI2.APP"_______ + 5C 52 45 56 45 52 53 49 32 2E 41 50 50 00 + ______________________________filepath nul + <- response filedel + 01 00 + ok___ + + +Anhang: Logfiles der Protokoll-Analyse +-------------------------------------- +- Manager.tar.gz 2000-10-26 Michael Bruennert + Windows-NT einzeiliges Format, keine MMCard + - Manager_Login.LOG + connect mit IC35mgr + - Manager_Backup.LOG + IC35 Datensicherung nach database.org + - database.org + die Daten vom IC35 backup + the data backuped from IC35 + - Manager_Logout.LOG + disconnect vom IC35mgr + - Manager_Reversi.LOG + Uebertragung des Reversi.app Programms ins IC35 + - Reversi.app + das uebertragene Programm +- BackupRestore.tar.gz 2000-10-30 Michael Bruennert + Windows-NT einzeiliges Format, keine MMCard + - Manager_Backup1.log + IC35 Datensicherung nach database.org + - Manager_Logout1.log + disconnect vom IC35mgr + - Manager_Restore1.log + IC35 Daten-Wiederherstellung von database.org +- mgr_conn_disc.log.gz 2000-10-26 Thomas Schulz + Windows-98, keine MMCard + connect, disconnect +- mgr_conn_disc_mmc.log.gz 2000-10-30 Thomas Schulz + Windows-98, mit MMCard1 + connect, disconnect +- mgr_rwd-file_20001107.tar.gz Thomas Schulz + Windows-98, mit MMCard1 + - mgr_rd-reversi_20001107.log + connect, Lesen von MMCard1\IC35\APP\REVERSI.APP, disconnect + - mgr_wr-reversi2_20001107.log + connect, Schreiben von MMCard1\IC35\APP\REVERSI2.APP, disconnect + connect, Schreiben von + - mgr_del-reversi2_20001107.log + connect, Loeschen von MMCard1\IC35\APP\REVERSI2.APP, disconnect + - REVERSI.APP + das Reversi Programm, REVERSI2.APP ist Kopie davon + diff --git a/doc/ic35sync.txt b/doc/ic35sync.txt new file mode 100644 index 0000000..daca5c3 --- /dev/null +++ b/doc/ic35sync.txt @@ -0,0 +1,846 @@ + $Id: ic35sync.txt,v 1.10 2000/12/03 21:10:00 tsch Rel $ + IC35-sync Protokoll + =================== + +Inhalt +------ + Ueberblick + Level-1 Protokoll + Level-2 Protokoll + Level-3 Protokoll + Kommunikations-Ablauf + identification + power + authentication + date+time + category + read,write file + disconnect + Zusammenfassung der PDUs + IC35 Record Felder + IC35Comm.dll Export Funktionen + Anhang: Logfiles der Protokoll-Analyse + + +Ueberblick +---------- +??? Unklarheiten ueber das IC35-sync Protokoll sind wie hier mit ??? +??? am Zeilenanfang markiert. + + Die IC35-sync Protokoll-"Suite" folgt offenbar dem ISO Schichten- + modell: Untere Protokoll-"Level" kapseln Daten hoeherer Protokoll- + Level ein und transportieren sie. + + Generell werden in allen Protokoll-Schichten Datenbloecke (oft, + aber nicht immer) kodiert nach dem Schema: + ii ll ll + Dabei bedeuten + ii Identifikations des Datenblocks + ll ll Laenge: Anzahl Bytes ueber alles, d.h. incl. ii ll ll + Kodierung mit niederwertigem Byte zuerst, gefolgt vom + hoeherwertigen Byte. + Datenblock Inhalt + +Level-1 Protokoll +----------------- + Level-1 PDU Kodierung allgemein: + - ii ll ll cc cc + ii Identifikations der PDU + ll ll Laenge: Anzahl der Bytes in der PDU, LSB zuerst MSB zuletzt + L2data transportierte Level-2 PDU Daten + cc cc Checksum: 16bit arithmetische Summe der PDU Bytes, LSB,MSB + - ii + kurze PDUs (nur Id) erscheinen nur von IC35 an PC. + Level-1 PDUs PC an IC35: + 01 03 00 init initiate data exchange + 02 ll ll cc cc datasel select which data to send + 04 03 00 datareq request to send selected data + 05 03 00 exit terminate data exchange + Level-1 PDUs IC35 an PC: + F0 ack0 acknowledge to init PDU + F1 ack1 acknowledge to datasel,exit PDUs + F2 ll ll cc cc datarsp response to datareq PDU + Level-1 Kommunikation PC->IC35, PC<-IC35: + -> command init + 01 03 00 + <- response ack0 + F0 + -> command select data + 02 ll ll cc cc + <- reponse ack1 + F1 + -> command request data + 04 03 00 + <- reponse data + F2 ll ll cc cc + -> command exit + 05 03 00 + <- reponse ack1 + F1 + Dieses Schema wird generell fuer den Transport der Level-2 Daten + benutzt. + +Level-2 Protokoll +----------------- + ll ll Laenge: Anzahl der Bytes in der Level-2 PDU (LSB,MSB) + L3data transportierte Level-3 PDU Daten + Level-2 PDUs PC an IC35: + 80 ll ll identify + 02 ll ll command write (more) + 82 ll ll command write (last) + 83 ll ll command read + 81 03 00 disconnect + Level-2 PDUs IC35 an PC: + 20 ll ll response (more) + A0 ll ll response (last) + A0 03 00 response done + 90 03 00 response write more + Transaktionen in hoeheren Schichten brauchen ggf. mehrere Blocks. + Soweit noch ein Block folgt werden die "(more)" PDUs benutzt, + fuer den letzten (oder einzigen) Block die "(last)" PDUs. + +Level-3 Protokoll +----------------- + ll ll Laenge: Anzahl der Bytes in der Level-2 PDU (LSB,MSB) + L3data transportierte Level-3 PDU Daten + Level-3 PDUs PC an IC35: + 10 00 64 00 4A ll ll identify request + 48 ll ll command1 (more) + 49 ll ll command2 (last) + Level-3 PDUs IC35 an PC: + 10 00 D0 07 4A ll ll identify response + 48 ll ll response1 (more) + 49 ll ll response2 (last) + Transaktionen in hoeheren Schichten brauchen ggf. mehrere Blocks. + Soweit noch ein Block folgt werden die "(more)" PDUs benutzt, + fuer den letzten (oder einzigen) Block die "(last)" PDUs. + +Kommunikations-Ablauf +--------------------- + Die Kommunikation zwischen PC und IC35 geschieht in zwei Phasen: + - Welcome + - Die Leitungsparameter fuer die Kommunikation zwischen PC und + IC35 sind Baudrate 115200, No parity, 8 databits, 2 stopbits. + - Der PC sendet wiederholt einzelne Zeichen "A" (hex 41) bis vom + IC35 "WELCOME" (hex 57 45 4C 43 4F 4D 45) empfangen wird. + - Der PC sendet noch einmal ein Zeichen "A", der IC35 antwortet + mit einem Byte hex 80. + - Unter Windows-98/-NT setzt der PC das DTR-Signal auf "AUS" + und schliesst den COM-Port. Nach ca. 0.015 sec oeffnet der + PC den COM-Port erneut mit Baudrate 115200, setzt das RTS- + Signal auf "AUS" und DTR-Signal auf "EIN", und stellt die + Leitungsparameter: No parity, 8 databits, 2 stopbits. + Dieses Verhalten scheint unnoetig, unter Linux ist es fuer + die Kommunikation nicht notwendig. + - PC setzt Timeouts: RC:500 und WC:500, vermutlich bedeutet das + Read Character und Write Character Timeout jeweils 500 ms. + Kommunikation der Welcome Phase: + -> 41 + timeout 1.15 sec + -> 41 + <- "WELCOME" + -> 41 + <- 80 + - Datenaustausch + In dieser Phase geschieht der Datenaustausch generell gemaess + dem Level-1 Protokoll. + Die Datenaustausch-Phase besteht aus folgenden Abschnitten: + - identification + - power + - authentication + - date+time + - category + - read,write "Addresses", "Memo", "Schedule", "To Do List" + - disconnect + +Im Folgenden ist nur die Level-2 Kommunikation notiert, die Uebertragung +der Level-2 Daten geschieht wie oben beschrieben mit dem Level-1 Protokoll. +Soweit die Level-2,-3 Header der generellen Form entsprechen, sind sie +in Kurzform L2id,L3id notiert, andernfalls explizit + +identification +-------------- + Kommunikation PC->IC35, PC<-IC35: + -> identify request + 80 26 00 # L2-header + 10 00 64 00 4A 1F 00 # L3-header + "INVENTEC CORPORATION PRODUCT" # L4-data + <- identify response + A0 29 00 # L2-header + 10 00 D0 07 4A 22 00 # L3-header + "INVENTEC CORPORATION DCS15 1.28" # L4-data + Der L2-header entspricht der generellen Form (80 ll ll). +??? Dem L3-header der "identify" PDUs in der generellen Form (4A ll ll) +??? gehen jeweils 4 Bytes Daten mit unklarer Bedeutung voraus. + +power +----- + Kommunikation PC->IC35, PC<-IC35: + -> power request + 82,49 03 01 "Power" 00 00 00 + <- power response + A0,49 03 01 + +authentication +-------------- + Kommunikation PC->IC35, PC<-IC35: + -> authenticate request + 82,49 03 00 [password] + <- authenticate response right password + A0,49 01 01 + oder + <- authenticate response wrong password + A0,49 00 01 + [password] ist das IC35 Kennwort als ASCII Klartext. + IC35 sendet "response wrong password" nur dann, wenn auf dem IC35 + die Kennwort-Abfrage beim Einschalten aktiviert ist. + +date+time +--------- + Kommunikation PC->IC35, PC<-IC35: + -> command get date+time + 83,49 02 00 00 + <- response date+time + A0,49 [mmddyyyyhhmmss] 00 00 + -> command set date+time + 82,49 02 01 00 [mmddyyyyhhmmss] 00 00 + <- response done + A0 + [mmddyyyyhhmmss] sind Monat(mm), Tag(dd), Jahr(yyyy), Stunde(hh), + Minute(mm), Sekunde(ss) jeweils in ASCII Ziffern (hex 30..39). + "set date+time" passiert offenbar nicht immer, es taucht nur auf + in Simple_hex.log, Portmon_export.log, Portmon_neukat2.log, aber + nicht in Import1.log. +??? Wenn "set date+time" passiert, dann nach "open file Addresses" +??? vor "get filelength Addresses". Ob das so noetig ist, ist unklar, +??? einfacher zu implementieren ist es vor der "read,write file" +??? Phase. + "get date+time" liefert 00-Bytes fuer [mmddyyyyhhmmss], wenn noch + nie ein "set date+time" zum IC35 geschehen ist. + Laut Experimenten laesst IC35 beliebigen Text bis 16 Zeichen zu. + Vermutlich sind die "get,set date+time" Kommandos gedacht fuer + die Hinterlegung eines Sync/Import Stempels vom PC im IC35 (auch + die Namen "AdsReadSysInfo" und "AdsWriteSysInfo" der IC35Comm.dll + Funktionen stuetzen diese Vermutung), IC35sync/Windows benutzt + dafuer offenbar einen Zeitstempel. + +category +-------- + Kommunikation PC->IC35, PC<-IC35: + -> command set category + 82,49 03 02 [category] + [category] wird mit 00 Bytes bis zur Laenge 8 Bytes aufgefuellt. +??? <- response category ok +??? A0,49 xx 01 +??? Die Bedeutung des Feldes xx ist unklar, es nimmt die Werte 01 +??? (Portmon_export.log) und 00 (Import.log aus Import.tar.gz) an. + Wenn die "category" Phase vorkommt, findet sie nach "set date+time" + statt, d.h. auch innerhalb des Filezugriffs auf Addresses. +??? Die Semantik der "category" Phase unklar. + +read,write file +--------------- + Der Zugriff auf die Files "Addresses", "Memo", "Schedule" und + "To Do List" ist beispielhaft fuer "Addresses" notiert. + Der Filezugriff besteht aus den Phasen: + - open file + liefert filedescriptor, der fuer die uebrigen Phasen benutzt wird + - get filelength (2 Varianten) + liefert die Anzahl der zu lesenden records + - read file record(s) + liefert record daten, ggf. in mehreren Bloecken + - write file record(s) + optional, ggf. in mehreren Bloecken + - optional delete file record(s) + - optional reset change flag(s) + - close file + + open file + -> command open file + 82,49 00 02 00 00 00 00 00 00 00 0B 00 00 00 09 "Addresses" 02 + cmd__ l1 l2 filename_ + l2 ist Laenge des filename, l1 = l2 + 2 + <- response fd=0001 + A0,49 01 00 + fd___ + Der filedescriptor fd wird anschliessend fuer den Zugriff benutzt. + Der Filename ist ziemlich egal, es wird nur der erste Buchstabe + unabhaengig von Gross- oder Kleinschreibung verglichen, d.h. es + wird fuer 'A','a' "Addresses", 'M','m' "Memo", 'T','t' "To Do List" + und fuer alle anderen Zeichen das File "Schedule" geoeffnet. + + get filelength 03: total number of records + Variante-1 aus Import1.log (command 01 03) + -> command get filelength fd=0001 + 83,49 01 03 01 00 00 00 00 00 + cmd__ fd___ + <- response filelength n=000B records + A0,49 0B 00 + n____ + Diese Variante wird bei "import" und "export" benutzt. + get filelength 04: number of modified records + -> command get filelength fd=0001 + 83,49 01 04 01 00 00 00 00 00 + cmd__ fd___ + <- response n=0000 records + A0,49 00 00 + n____ + Diese Variante wird bei "sync" benutzt. Sie liefert die Anzahl + der manuell auf dem IC35 modifizierten Records, d.h. veraenderte, + neu hinzugefuegte und insbesondere auch geloeschte. + + read file record by index + -> command read record fd=0001 idx=0000 + 83,49 01 06 01 00 00 00 00 00 + cmd__ fd___ idx__ + <- response record data (more) + 20,48 01 00 00 05 80 + ri___ fi ch + -> command read more + 83 + <- response record data (more) + 20,48 + -> command read more + 83 + <- response record data (last) + A0,49 + Die Records werden index-orientiert gelesen, der Index idx nimmt + Werte 0 bis n - 1 (filelength n). + read next modified record + (Beispiele aus Delete.tar.gz:NeueDatenSync_171100.log File "Memo") + -> command read next modified record + 83,49 01 07 03 00 00 00 00 00 + cmd__ fd___ + <- response record data (last) Beispiel geloeschter Record + A0,49 03 00 00 06 20 + ri___ fi ch + -> command read next modified record + 83,49 01 07 03 00 00 00 00 00 + cmd__ fd___ + <- response record data (last) Beispiel veraenderter Record + A0,49 05 00 00 06 80 + ri___ fi ch + read file record by record-ID + (Beispiele aus Experimenten mit File "Memo") + -> command read record by record-ID + 83,49 01 05 03 00 05 00 00 06 + cmd__ fd___ ri___ fi + <- response record data (last) Beispiel vorhandener Record + A0,49 05 00 00 06 80 + ri___ fi ch + Im Erfolgsfall enthaelt die Antwort die angeforderte RecordId + und gueltige Feldlaengen und Daten. + -> command read record by record-ID + 83,49 01 05 03 00 EE 78 00 06 + cmd__ fd___ ri___ fi + <- response record data (last) Bsp. nicht vorhandener Record + A0,49 3E 81 D3 0D 60 + ri___ fi ch + Ist der Record mit der spezifizierten RecordId im IC35 nicht + vorhanden, so enthaelt die Antwort eine ungueltige RecordId, + ungueltige Feldlaengen und keine Felddaten. + Im "response record data" ist fi die FileId und ri die RecordId + auf dem IC35. Beide zusammen ergeben identifizieren den Record + im IC35 global eindeutig. +??? Ob die RecordId ri sich auf 3 Bytes erstreckt wurde mangels +??? Geduld (es waeren >65536 Records zu erzeugen) nicht geklaert, +??? die Implementation unter Linux nimmt dies an. + Das Feld ch ist ein Aenderungskennzeichen (CHangeflag): + 80 Record im IC35 neu erzeugt oder von anderem kopiert + 40 Record im IC35 veraendert + 20 Record im IC35 geloescht (ohne ,) + 00 keine Aenderung in diesem Record + Das Aenderungskennzeichen wird nur durch manuelle Aenderungen + im IC35 auf ch!=00 gesetzt, mit "write file record" neu erzeugte + haben ch=00, mit "delete file record" geloeschte verschwinden + vollstaendig. + Nur von "read next modified record" werden Records mit ch=20 + geliefert, bei "read record by index" tauchen sie nicht auf. + Der gelesene Record wird falls noetig in mehreren Bloecken + uebertragen, vom IC35 haben alle Bloecke bis auf den letzten + L2id=20 und L3id=48, der letzte Block hat L2id=A0 und L3id=49. + Ein Record besteht aus einer Tabelle von Feldlaengen + (Bytes) gefolgt von den Felddaten . Die Inhalte der Felder + ergeben sich entsprechend den Feldlaengen aus den Felddaten. + Filename Feldanzahl fi + Addresses 21 05 + Memo 4 06 + Schedule 10 08 + To Do List 8 07 + Zumindest fuer "read next modified record" muessen erst soviel + Leseoperationen, wie "get number of modified record" lieferte, + durchgefuehrt werden, bevor "write record", "delete record" oder + "reset change flag" ausgefuehrt wird. Andernfalls geschieht + Undefiniertes, z.B. wurde ein read response ohne Record-Daten + mit ri=01 07 03 fi=00 ch=00 beobachtet. + + write file record + -> command write record (more) fd=0001 + 02,48 01 08 01 00 00 00 00 00 51 00 00 00 00 96 00 00 00 + cmd__ fd___ w1 w2 w3 lr + 01 08 01 00 00 00 00 00 51 00 00 00 00 96 00 00 00 Addresses + 01 08 03 00 00 00 00 00 58 48 99 00 00 16 00 00 00 Memo + 01 08 03 00 00 00 00 00 60 16 99 00 00 16 00 00 00 Memo + 01 08 00 00 00 00 00 00 10 00 00 00 00 32 00 00 00 Schedule + 01 08 02 00 00 00 00 00 10 00 00 00 00 46 00 00 00 To Do List + lr ist Gesamtlaenge des Record, d.h. Anzahl der Feldlaengen + (Bytes in ) plus Summe der Feldlaengen (Bytes in + ). +??? Die Bedeutung der "write record" PDU-Felder w1, w2, w3 ist unklar. + <- reponse write more + 90 + -> command write record (last) + 82,49 + <- response write ok + A0,49 12 00 00 05 + ri___ fi + 12 00 00 05 Addresses + 03 00 00 06 Memo + 1B 00 00 08 Schedule + 02 00 00 07 To Do List + Im "response write ok" sind fi die FileId und ri die RecordId, + die der neue Record im IC35 erhalten hat. Es wird dabei immer ein + neuer Record mit neuer RecordId erzeugt. Das Modifizieren unter + Beibehaltung der RecordId ist mit "update file record" moeglich. + Der neu erzeugte Record erhaelt Aenderungskennzeichen ch=00 (s.o.). + Der geschriebene Record wird falls noetig in mehreren Bloecken + uebertragen, alle bis auf den letzten Block habe L2id=02 und + und L3id=48, der letzte Block hat L2id=82 und L3id=49. + update file record + -> command update record (last) fd=0003 recid=06000005 + 82,49 01 09 03 00 05 00 00 06 60 19 99 00 00 46 00 00 00 + cmd__ fd___ ri___ fi w1 w2 w3 lr + ri ist die RecordId und fi die FileId auf dem IC35. +??? w1,w2,w3 sind in der "update record" PDU die gleichen Felder +??? wie in der "write record" PDU mit ebenso unklarer Bedeutung. + lr ist Gesamtlaenge des Record, d.h. Anzahl der Feldlaengen + (Bytes in ) plus Summe der Feldlaengen (Bytes in + ). + <- response write ok + A0,49 05 00 00 06 + ri___ fi + Im Unterschied zu "write record" wird die RecordId auf dem IC35 + hier beibehalten. Ansonsten verhaelt sich "update record" genauso + wie "write record" (Fragmentierung, Aenderungskennzeichen etc.). + + delete record + (Beispiele aus Delete.tar.gz:NeueDatenSync_171100.log File "Memo") + -> command delete record + 83,49 01 02 03 00 05 00 00 06 + cmd__ fd___ ri___ fi + <- response done + A0 + Der Record wird im IC35 direkt geloescht, d.h. er wird nicht mit + Aenderungskennzeichen ch=20 (s.o.) auftauchen. Im Gegensatz dazu + bleiben im IC35 manuell geloeschte Records mit ch=20 erhalten, + bis sie mit "reset record change flag" bestaetigt werden und dann + tatsaechlich verschwinden. + + reset record change flag + -> command reset record change flag + 82,49 01 0A 01 00 11 00 00 05 + cmd__ fd___ ri___ fi + <- response done + A0 + Das Aenderungskennzeichen des Record wird im IC35 zurueckgesetzt + auf ch=00, d.h. Record unveraendert. + Records mit ch=20 (ohne Daten, Reste von manuellem Loeschen auf + dem IC35) werden endgueltig entfernt. + + close file + -> command close file fd=0001 + 82,49 00 03 01 00 00 00 00 00 + cmd__ fd___ + <- response done + A0 + +disconnect +---------- + Kommunikation PC->IC35, PC<-IC35: + -> command disconnect + 81 03 00 # L2-header + <- response done + A0 03 00 # L2-header + Beim Verbindungsabbau werden nur Level-2 Header ohne Daten + uebertragen, d.h. Level-3 wird nicht benutzt. + +Zusammenfassung der PDUs +------------------------ + Level-4 commands + 00 file commands + 00 02 open file + 00 03 close file + 01 record commands + 01 02 delete record + 01 03 get filelength: total number of records + 01 04 get filelength: number of modified records + 01 05 read file record by record-ID + 01 06 read file record by index + 01 07 read next modified record + 01 08 write file record + 01 09 update file record + 01 0A reset file record change-flag + 02 date+time + 02 00 get date+time + 02 01 set date+time + 03 power,passwd,category + 03 00 passwd + 03 01 power + 03 02 category + + commands + - identify + 80, 10 00 64 00 ,4A "INVENTEC CORPORATION PRODUCT" + - disconnect + 81 + - power + 82,49 03 01 "Power" 00 00 00 + - authenticate + 82,49 03 00 [password] + - get date+time + 83,49 02 00 00 + - set date+time + 82,49 02 01 00 [mmddyyyyhhmmss] 00 00 + - category + 82,49 03 02 [category] 00 + - open file + 82,49 00 02 00 00 00 00 00 00 00 lf+2 00 00 00 lf [filename] 02 + - close file + 82,49 00 03 fd_.. 00 00 00 00 + - get filelength: total number of records + 83,49 01 03 fd_.. 00 00 00 00 + - get filelength: number of modified records + 83,49 01 04 fd_.. 00 00 00 00 + - read filerecord by record-ID + 83,49 01 05 fd_.. ri_.. 00 fi + - read filerecord by index + 83,49 01 06 fd_.. in_dx 00 00 + - read next modified filerecord + 83,49 01 07 fd_.. 00 00 00 00 + - read more + 83 + - write filerecord (more) + 02,48 01 08 fd_.. 00 00 00 00 w1 w2 w3 00 00 lr 00 00 00 + - update filerecord (more) + 02,48 01 09 fd_.. ri_.. 00 fi w1 w2 w3 00 00 lr 00 00 00 + write filerecord (last) + 82,49 + - delete record + 83,49 01 02 fd_.. ri_.. 00 fi + - reset filerecord change-flag + 82,49 01 0A fd_.. ri_.. 00 fi + + responses + - identify + A0, 10 00 D0 07 ,4A "INVENTEC CORPORATION DCS15 1.28" + - disconnect + A0 + - power + A0,49 03 01 + - authenticate ok + A0,49 01 01 + - get date+time + A0,49 [mmddyyyyhhmmss] 00 00 + - set date+time + A0 + - category + A0,49 xx 01 + record fields: + - xx unknown meaning, values: 00, 01 + - open file + A0,49 fd_.. + - close file + A0 + - get filelength + A0,49 n._.. + - read filerecord (more) + 20,48 ri_.. 00 fi ch + read filerecord (last) + A0,49 + record fields: + - ri_.. record-id on IC35 + - fi file-id on IC35 + - ch change-flag: 00, 80, 40 or 20 + - write/update filerecord + A0,49 ri_.. 00 fi + - write more + 90 + - delete record + A0 + - reset record changeflag + A0 + + +IC35 Record Felder +----------------- + Die nachfolgenden Tabellen zu den IC35 Files (Addresses, Memo, + Schedule, ToDoList) enthalten jeweils den 0-relativen Feld- + Index, die Feld-Bedeutung, die maximale Feld-Laenge und ggf. + Erlaeuterung zum Format. + Die maximalen Feld-Laengen sind dokumentiert bei Siemens unter + http://www.ic.siemens.com/mySiemens/lowres/content/ + ap_content_moreinfocontainer_lr/1,1908,2_IC35_4_0_61_0_2183,00.html +??? Der Zweck der "category-id"s ist unklar, jede neu angelegte +??? Kategorie erhaelt eine neue "category-id". + + Addresses (21 Felder) + 0 Vorname 50 + 1 Nachname 50 + 2 Firma 128 + 3 Tel.Privat 48 + 4 Tel.Buero 48 + 5 Handy 48 + 6 Fax 48 + 7 Strasse 128 + 8 Ort 60 + 9 PLZ 10 + 10 Bundesland 40 + 11 Land 15 + 12 E-Mail1 80 + 13 E-Mail2 80 + 14 URL 128 + 15 Geburtstag 10 + 16 Notizen 255 + 17 category-id 1 bin + 18 (def.)1 128 + 19 (def.)2 128 + 20 category 8 + category-id, -name + 02 Address (owner data) + 06 Business + 0B Personal + 0C Unfiled + 13 S35telb + 14 S35SIM.1 + 15 S35SIM.2 + 16 newcateg + + Memo (4 Felder) + 0 Betreff 60 + 1 Notizen 255 + 2 category-id 1 bin + 3 category 8 + category-id, -name + 0D Business + 0E Personal + 0F Unfiled + 17 n.memcat + + Schedule (10 Felder) + Schedule Records enthalten weder category-id noch category Text. + 0 Betreff 60 + 1 Start(Datum) 8 ? [yyyymmdd] + 2 Start(Zeit) 6 ? [hhmm]":1" + 3 Ende(Zeit) 6 ? [hhmm]"<1" + 4 AlrmBef 1 bin + 00=no 01=now 02=1min 03=5min 04=10min 05=30min + 06=1hour 07=2hour 08=10hour 09=1day 0A=2day + 5 Notizen 255 + 6 AlrmRep(Byte) 1 bin lb00-0iii + l: 0=LED 1=noLED, b: 0=beep 1=nobeep + iii: 0=norepeat 1=day 2=week 3=monwday 4=year 5=monmday + 7 Ende(Datum) 8 ? [yyyymmdd] + 8 EndRepeat 8 ? [yyyymmdd] + 9 RepAlln(Byte) 1 bin nn + repeat all nn days/weeks/mwdays/years/mmdays + Alarm/Repeat-Flags + f4 f6 f9 repeat alarm bef LED beep + 0A C0 01 no 2 day no no + 07 C0 01 no 2 hour no no + 06 C4 01 1 year 1 hour no no + 05 C5 02 2 mon md 30 min no no + 04 C3 01 1 mon wd 10 min no no + 03 82 03 3 week 5 min no yes + 02 41 02 2 day 1 min yes no + 01 01 01 1 day now yes yes + 00 C2 01 1 week none no no + + ToDoList (8 Felder) + 0 Start 8 ? [yyyymmdd] + 1 Ende 8 ? [yyyymmdd] + 2 Erledigt 1 bin 00=N 01=J + 3 Prioritaet 1 bin 00=low 01=normal 02=high + 4 Betreff 60 + 5 Notizen 255 + 6 category-id 1 bin + 7 category 8 + category-id, -name + 10 Business + 11 Personal + 12 Unfiled + + category-ids + 02 Address Address (owner data) + 06 Business Address + 0B Personal Address + 0C Unfiled Address + 0D Business Memo + 0E Personal Memo + 0F Unfiled Memo + 10 Business ToDoList + 11 Personal ToDoList + 12 Unfiled ToDoList + 13 S35telb Address + 14 S35SIM.1 Address + 15 S35SIM.2 Address + 16 newcateg Address + 17 n.memcat Memo + + +IC35Comm.dll Export Funktionen +------------------------------ +Addr IC35Comm.dll Export Funktion Aequivalent in ic35sync/Linux ++-------+-------------------------------+---------------------------- +3859 AdsBeginSession welcome +3BEB AdsCancelEndSession +11AE AdsCloseDatabase close_file ? +3FAD AdsCloseHandle close_file ? +1B87 AdsCommitRecord commit_frec +1341 AdsDeleteAllRecords +14CE AdsDeleteRecord delete_frec +3B89 AdsEndSession disconnect +1629 AdsGetModifiedRecordCount get_mod_flen +1585 AdsGetRecordCount get_flen +1D84 AdsLocalDeleteRecord +1D00 AdsLocalModifyRecord +1C3E AdsLocalNewRecord +1000 AdsOpenDatabase open_file ? +13FA AdsPurgeDeleteRecords +126D AdsReadCatagoryData category ? +189B AdsReadNextModifiedRecord read_mod_frec +16CD AdsReadRecordByID read_id_frec +1701 AdsReadRecordByIndex read_frec +1E3B AdsReadSysInfo get_date_time +1F7F AdsSendCommand sendcmd,recvrsp +37D0 AdsSetCancelHandle +1B03 AdsUpdateRecord update_frec +1A0C AdsWriteRecord write_frec +1ED5 AdsWriteSysInfo set_date_time ? +37EB AdsDeviceVersion identify +3804 AdsOpenComPort com_open + + +Anhang: Logfiles der Protokoll-Analyse +-------------------------------------- +- Simple_hex.log +setdt -cat modrec -write -del -res + get date+time 2000-08-20 21:28:10 + set date+time 2000-08-20 21:31:49 + no category + get modified record count (01 04), all files return 0 records + no writerec + no reset changeflag +- Portmon_export.log +setdt +cat allrec -write -del -res + get date+time 2000-09-10 18:28:42 + set date+time 2000-09-10 18:30:35 + category Addresses (rsp 01 01) + get record count (01 03) + no writerec + no reset changeflag +- Import.tar.gz:Import1.log -setdt -cat allrec +write -del -res + get date+time 2000-10-24 21:44:48 + no set date+time + get record count (01 03) + no category + no reset changeflag + writerec Addresses(1x),Memo(1x),Schedule(1x),ToDoList(1x) +- Import.tar.gz:Import.log +setdt +cat allrec +write -del +res + get date+time 2000-10-30 20:32:31 + set date+time 2000-10-30 20:59:13 + category Addresses(rsp 00 01), Memo(rsp 01 01), ToDoList(rsp 01 01) + get record count (01 03) + reset changeflag Addresses, Memo, Schedule, ToDoList + writerec Addresses(1x), Schedule(2x) +- Delete.tar.gz:Export_171100.log -setdt -cat allrec -write -del -res + Ich habe in Outlook die Daten komplett geloescht und die + IC35-Sync-Software zurueckgesetzt. + Dann habe ich die Daten des IC35 nach Outlook importiert. + get date+time 2000-11-01 17:42:13 + no set date+time + no category + get record count (01 03) + no writerec + no reset changeflag +- Delete.tar.gz:InitSync_171100.log +setdt +cat allrec -write -del +res + [Dann habe die Daten des IC35 nach Outlook importiert] und + anschliessend noch einmal einen normalen Sync durchgefuehrt. + get date+time 2000-11-01 17:42:13 + set date+time 2000-11-17 23:10:51 + category Addressses(rsp 00 01, 01 01), Memo(rsp 01 01), ToDoList(rsp 01 01) + get record count (01 03) + no writerec + reset changeflag Memo,Schedule,ToDoList +- Delete.tar.gz:NeueDatenSync_171100.log +setdt +cat modrec +write +del +res + Anschliessendhabe ich fuer jede Kategorie je 4 Testdatensaetze + angelegt: Je zwei in Outlook und je zwei im IC35. + Im Betreff des Datensatzes steht dieser Erzeugungsort an erster Stelle + (z.B Testadresse Outlook-IC35). + An zweiter Stelle steht das Teil, mit dem der Datensatz geloescht wird. + Anschliessend habe ich einen Sync durchgefuehrt, damit hatten der + IC35 und Outlook jeweils alle 4 Datensaetze. + addr 0018 IC35-IC35 + addr 0017 IC35-Outl + addr 0019 Outl-Outl + addr 001A Outl-IC35 + memo 0005 IC35-IC35 + memo 0004 IC35-Outl + memo 0006 Outl-IC35 + memo 0007 Outl-Outl + sched 0025 IC35-IC35 + sched 0024 IC35-Outl + sched 0026 Outl-Outl + sched 0027 Outl-IC35 + todo 000E IC35-IC35 + todo 000D IC35-Outl + todo 000F Outl-Outl + todo 0010 Outl-IC35 + get date+time 2000-11-17 23:22:45 + set date+time 2000-11-17 23:23:34 + category Addressses, Memo, ToDoList + get record count Addresses(01 03) Memo(01 04) Schedule(01 04) ToDoList(01 04) + reset changeflag Addresses, Memo, Schedule, ToDoList + writerec Addresses(3x), Memo(2x), Schedule(2x), ToDoList(2x) + readrec Addresses, readrecmod Memo, Schedule, ToDoList + deleterec Memo, Schedule +- Delete.tar.gz:DeleteSync_171100.log +setdt +cat modrec -write +del +res + Anschliessend habe ich auf jedem Teil pro Kategorie wieder je zwei + Datensaetze geloescht und zwar die, die durch den hinteren Namensteil + gekennzeichnet waren (Nur bei Testadresse IC35-* ist mir ein Fehler + passiert, dort ist es genau umgekehrt -Outlook wurde auf dem IC35 + geloescht und umgekehrt). Dann folgt wieder ein Sync (DeleteSync), bei + dem tatsaechlich aus beiden Teilen alle Datensaetze verschwunden sind. + addr 0018 IC35-IC35 0102-1 + addr 0017 IC35-Outl 0107-1 0102-3 + addr 0019 Outl-Outl 0102-2 + addr 001A Outl-IC35 0107-2 0102-4 + 0002 0107-3 0102-5 Verwenden Sie ... + 0012 0107-4 0102-6 Testnachname, Testvorname + 0011 0107-5 0102-7 Haid, Beppo + memo 0005 IC35-IC35 0107-2 0102-4 + memo 0004 IC35-Outl 0102-1 + memo 0006 Outl-IC35 0107-1 0102-3 + memo 0007 Outl-Outl 0102-2 + sched 0025 IC35-IC35 0107-2 0102-4 + sched 0024 IC35-Outl 0102-2 + sched 0026 Outl-Outl 0102-1 + sched 0027 Outl-IC35 0107-1 0102-3 + todo 000E IC35-IC35 0107-1 0102-3 + todo 000D IC35-Outl 0102-1 + todo 000F Outl-Outl 0102-2 + todo 0010 Outl-IC35 0107-2 0102-4 + get date+time 2000-11-17 23:23:34 + set date+time 2000-11-17 23:30:28 + category Addresses, Memo, ToDoList + get modified record count (01 04) + readrecmod + deleterec Addresses, Memo, Schedule, ToDoList + no writerec + reset changeflag +- Delete.tar.gz:Import_171100.log +setdt +cat allrec -write -del +res + Anschliessend habe ich wie gewuenscht noch einen Export der + Outlook-Daten in den IC35 durchgefuehrt. + get date+time 2000-11-17 23:30:28 + set date+time 2000-11-17 23:48:36 + category + reset changeflag + no writerec + diff --git a/src/.cvsignore b/src/.cvsignore new file mode 100644 index 0000000..9569617 --- /dev/null +++ b/src/.cvsignore @@ -0,0 +1,9 @@ +Makefile +Makefile.in +vcc.c +versinfo.c +ic35sync +ic35mgr +ic35log +vcaconv +.deps diff --git a/src/CVS/Entries b/src/CVS/Entries new file mode 100644 index 0000000..6e387b5 --- /dev/null +++ b/src/CVS/Entries @@ -0,0 +1,35 @@ +/.cvsignore/1.2/Mon Feb 19 01:09:14 2001// +/Makefile.am/1.3/Mon Feb 19 01:09:43 2001// +/comio.c/1.13/Sat Mar 3 16:30:45 2001// +/comio.h/1.7/Sat Mar 3 16:30:45 2001// +/databin.c/1.35/Fri Mar 2 02:09:59 2001// +/dataio.c/1.33/Fri Mar 2 02:09:59 2001// +/dataio.h/1.12/Tue Dec 26 01:25:22 2000// +/datatxt.c/1.32/Tue Dec 26 01:36:32 2000// +/datavca.c/1.46/Fri Mar 2 02:09:59 2001// +/genproto.c/1.2/Sun Jan 21 19:54:52 2001// +/genproto.h/1.2/Sun Jan 21 23:38:37 2001// +/ic35frec.c/1.8/Fri Mar 2 02:09:59 2001// +/ic35frec.h/1.5/Sat Dec 23 01:09:26 2000// +/ic35log.sh/1.6/Sun Jan 14 00:09:46 2001// +/ic35mgr.c/1.20/Tue Nov 20 23:08:35 2001// +/ic35sync.c/1.19/Fri Mar 2 02:10:42 2001// +/mgrproto.c/1.17/Tue Nov 20 23:08:35 2001// +/mgrproto.h/1.8/Tue Nov 20 23:08:35 2001// +/mgrtrans.c/1.17/Tue Nov 20 23:08:35 2001// +/mgrtrans.h/1.7/Tue Nov 20 23:08:35 2001// +/port.h/1.3/Sat Feb 10 03:08:41 2001// +/synproto.c/1.11/Sun Jun 17 23:37:36 2001// +/synproto.h/1.3/Sun Dec 3 07:50:44 2000// +/syntrans.c/1.19/Fri Mar 2 02:09:59 2001// +/syntrans.h/1.10/Thu Dec 21 11:05:52 2000// +/util.c/1.9/Mon Jun 11 09:14:59 2001// +/util.h/1.9/Fri Mar 2 02:08:32 2001// +/vcaconv.c/1.17/Sat Feb 17 20:56:49 2001// +/vcc.h/1.2/Sun Nov 19 18:14:45 2000// +/vcc.y/1.7/Sat Feb 10 03:09:24 2001// +/vcutil.c/1.44/Thu Dec 28 02:26:31 2000// +/vcutil.h/1.44/Wed Dec 27 22:26:52 2000// +/vobject.c/1.9/Wed Feb 7 01:44:42 2001// +/vobject.h/1.6/Thu Dec 21 23:01:45 2000// +D diff --git a/src/CVS/Repository b/src/CVS/Repository new file mode 100644 index 0000000..833ea8c --- /dev/null +++ b/src/CVS/Repository @@ -0,0 +1 @@ +ic35link/src diff --git a/src/CVS/Root b/src/CVS/Root new file mode 100644 index 0000000..0f95ef3 --- /dev/null +++ b/src/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@ic35link.cvs.sourceforge.net:/cvsroot/ic35link diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..b1b969b --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,64 @@ +## Process this file with automake to produce Makefile.in +## $Id: Makefile.am,v 1.3 2001/02/19 01:09:43 tsch Rel $ +## Copyright (C) 2001 Thomas Schulz +## +## automakefile for ic35link/src + +versinfo.c: Makefile + -@echo "Creating versinfo.c" + -@rm -f versinfo.c + -@echo '/* versinfo.c */' > versinfo.c + -@echo '/* automatically made by Makefile */' >> versinfo.c + -@echo '/* DO NOT EDIT! */' >> versinfo.c + -@echo '#include "../config.h"' >> versinfo.c + -@echo 'char * pkgvers = VERSION;' >> versinfo.c + -@echo 'char * pkgdate = ISODATE;' >> versinfo.c + -@echo 'char * bldinfo = "compiled '`date '+%Y-%m-%d %T'` \ + 'by '`whoami`'@'`hostname`'";' >> versinfo.c + +bin_PROGRAMS = ic35sync ic35mgr vcaconv +ic35sync_SOURCES = \ + ic35sync.c \ + syntrans.c syntrans.h \ + synproto.c synproto.h \ + genproto.c genproto.h \ + dataio.c dataio.h datatxt.c databin.c datavca.c \ + ic35frec.c ic35frec.h \ + vcutil.c vcutil.h \ + vcc.y vcc.h vobject.c vobject.h port.h \ + comio.c comio.h \ + util.c util.h \ + versinfo.c +ic35mgr_SOURCES = \ + ic35mgr.c \ + mgrtrans.c mgrtrans.h \ + mgrproto.c mgrproto.h \ + genproto.c genproto.h \ + comio.c comio.h \ + util.c util.h \ + versinfo.c +vcaconv_SOURCES = \ + vcaconv.c \ + dataio.c dataio.h datatxt.c databin.c datavca.c \ + ic35frec.c ic35frec.h \ + vcutil.c vcutil.h \ + vcc.y vcc.h vobject.c vobject.h port.h \ + util.c util.h \ + versinfo.c + +bin_SCRIPTS = ic35log +ic35log: + cat $@.sh >$@ + chmod a+x $@ + +EXTRA_DIST = ic35log.sh + +# tell automake to not strip scripts: +INSTALL_SCRIPT = $(INSTALL) + +CLEANFILES = ic35log .deps/* versinfo.c +if MAINTAINER_MODE +MAINTAINERCLEANFILES = vcc.c Makefile.in +else +MAINTAINERCLEANFILES = +endif diff --git a/src/comio.c b/src/comio.c new file mode 100644 index 0000000..0bfa1c9 --- /dev/null +++ b/src/comio.c @@ -0,0 +1,484 @@ +/************************************************************************ +* Copyright (C) 2000 Thomas Schulz * +* */ + static char rcsid[] = + "$Id: comio.c,v 1.13 2001/03/03 16:30:45 tsch Rel $"; /* +* * +* IC35 serial communication +* * +************************************************************************* +* * +* conditional compile on NO_LOGSIM: if #defined, the simulated * +* communication is NOT supported, default WITH com-simulation. * +* conditional compile on __STRICT_ANSI__: if #defined, substitute * +* functions, which are not available with the ANSI C standard. * +* (compilation with 'gcc -ansi ..' does #define __STRICT_ANSI__) * +* * +* simulated communication #ifndef NO_LOGSIM * +* com_siminit initialize communications with simulation file * +* com_simexit local: leave simulated communication * +* com_simul local: report if simulation active * +* com_simrecv local: simulate receive from simulation file * +* real communication +* com_setsigs local: set RS232 output signals * +* com_settimeout set receive timeout, return previous * +* com_init initialize serial communication device * +* com_waitnice lower process priority when using com_sendw() * +* com_sendw send datablock to comm.device with waiting * +* com_send send datablock to comm.device * +* com_recv receive datablock from comm.device * +* com_exit close serial communication device * +* * +************************************************************************/ + +#include /* FILE*, fopen(), .. */ +#include /* strncmp(), .. */ +#include /* read(), write(), .. */ +#include /* tcgetattr(), .. */ +#include /* F_GETFL, .. */ +#include /* ioctl(), .. */ +#include /* size_t, .. */ +#include /* struct timeval */ +#include /* sigaction(), .. */ + +#include "util.h" /* LPRINTF(), .. */ +#include "comio.h" +NOTUSED(rcsid) + + +/* ==================================== */ +/* simulated communication */ +/* ==================================== */ +/* +* uses post-processed log of real communication with IC35: +* WR nn xx xx xx ... +* RD nn xx xx xx ... +* and "receives" data using the "RD nn" lines. +*/ +#ifndef NO_LOGSIM +static FILE * simfp = NULL; + +static int +_com_siminit( char * s_fname ) /* init simulated comm */ +{ + if ( s_fname && *s_fname ) { + simfp = fopen( s_fname, "r" ); + if ( simfp == NULL ) + return ERR; + } + return OK; +} +void +com_siminit( char * s_fname ) +{ + if ( _com_siminit( s_fname ) != OK ) + fatal( "cannot open simulation file: %s", s_fname ); +} +static void +com_simexit( void ) /* leave simulated comm */ +{ + if ( simfp ) { + fclose( simfp ); + simfp = NULL; + } +} +static bool +com_simul( void ) /* report if simul active */ +{ + return (bool)( simfp != NULL ); +} +static int +com_simrecv( uchar * buff, size_t blen ) /* receive from simul.file */ +{ + static int simrlen = 0; + static int simridx = 0; + bool do_check; + uchar * bptr; + int chr, n, rbyte; + char xdir[8]; + + if ( buff == NULL || blen == 0 ) /* sanity */ + return 0; + + memset( buff, 0, blen); /* clear buffer sets dummy bytes */ + bptr = buff; do_check = FALSE; + while ( bptr < buff + blen ) { + /* forward to or check next "RD nn" line */ + if ( simridx >= simrlen ) { + for ( ; ; ) { + while ( (chr = fgetc( simfp )) != '\n' ) + if ( chr < 0 ) + return ERR; + if ( fscanf( simfp, "%s %d", xdir, &simrlen ) == 2 + && strncmp( xdir, "RD", 2 ) == 0 ) + break; + if ( do_check ) + return bptr - buff; + } + do_check = TRUE; + simridx = 0; + } + /* read bytes from "RD nn" line */ + for ( ; simridx < simrlen; ++simridx ) { + if ( bptr >= buff + blen ) + return blen; + ungetc( chr = fgetc( simfp ), simfp ); /* avoid fscanf() eat \n */ + if ( chr == '\n' + || fscanf( simfp, "%x", &rbyte ) != 1 ) + break; + *bptr++ = (uchar)rbyte; + } + /* dummy non-logged recv bytes */ + n = min( blen - (bptr - buff), simrlen - simridx); + simridx += n; + bptr += n; + } + return bptr - buff; +} +#endif /*NO_LOGSIM*/ + + +/* ==================================== */ +/* real communication */ +/* ==================================== */ + +static int com_fd = -1; +static int com_tmo = 500; /* timeout 500 ms */ + +/* local: set RS232 output signals +* ------------------------------- +*/ +static void +com_setsigs( int sigs ) +{ + int flags; + + if ( com_fd >= 0 + && ioctl( com_fd, TIOCMGET, &flags ) == 0 ) { + flags &= ~(TIOCM_DTR|TIOCM_RTS); + flags |= (TIOCM_DTR|TIOCM_RTS) & sigs; + ioctl( com_fd, TIOCMSET, &flags ); + LPRINTF(( L_NOISE, "com_setsigs(%08X) DTR %s RTS %s", + sigs, sigs & TIOCM_DTR ? "ON " : "off", + sigs & TIOCM_RTS ? "ON " : "off" )); + } +} + +/* local: log state of RS232 signals +* --------------------------------- +*/ +static void +_com_sigchg( void ) +{ + static int oflags = -1; + static struct { + char * name; + int sig; + } sigtab[] = { + { "CTS", TIOCM_CTS }, + { "DCD", TIOCM_CAR }, + { "DSR", TIOCM_DSR }, + { "RI", TIOCM_RNG }, + { NULL, 0 } + }, *psig; + int flags; + char sigtext[48]; + + if ( com_fd >= 0 + && ioctl( com_fd, TIOCMGET, &flags ) == 0 + && ( ((oflags ^ flags) & (TIOCM_CTS|TIOCM_CAR|TIOCM_DSR|TIOCM_RNG)) != 0 + || oflags == -1 ) ) { + strcpy( sigtext, "" ); + for ( psig = sigtab; psig->name; ++psig ) + sprintf( sigtext+strlen(sigtext), " %s %s%s", + psig->name, + flags & psig->sig ? "ON" : "off", + ((oflags ^ flags) & psig->sig) && oflags != -1 ? "*" : "" ); + LPRINTF(( L_NOISE, "com signals:%s", sigtext )); + oflags = flags; + } +} + +/* set,get receive timeout +* ----------------------- +* return previous timeout. zero or negative timeout argument 'msec' +* will not be set and can be used to enquire current timeout. +*/ +int +com_settimeout( int msec ) +{ + int old_tmo = com_tmo; + + old_tmo = com_tmo; + if ( msec > 0 ) + com_tmo = msec; + if ( old_tmo != com_tmo ) + LPRINTF(( L_NOISE, "com_settimeout %d -> %d", old_tmo, com_tmo )); + return old_tmo; +} + +/* initialize comm.device +* ---------------------- +* open in O_NONBLOCK mode to avoid hanging on inactice DCD signal, +* finally set back to blocking mode. +* set line parameters to 115200,N,8,2 and raw mode, 2 stopbits are +* needed to avoid IC35 receive errors with MMCard operations. +* raise DTR and RTS signals. +*/ +#ifdef __STRICT_ANSI__ +#define cfmakeraw(tiop) \ + (tiop)->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); \ + (tiop)->c_oflag &= ~(OPOST); \ + (tiop)->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); \ + (tiop)->c_cflag &= ~(CSIZE|PARENB); \ + (tiop)->c_cflag |= CS8; +#endif + +int +com_init( char * devname ) +{ + int i, flags; + struct termios tio; + +#ifndef NO_LOGSIM + if ( com_simul() ) { + com_fd = -1; + return OK; + } +#endif + /* open serial communication device */ + if ( (com_fd = open( devname, O_RDWR|O_NONBLOCK|O_NOCTTY )) == -1 ) { + return ERR; + } + if ( !isatty( com_fd ) ) { + close( com_fd ); + com_fd = -1; + return ERR; + } + /* set linepars 11500,N,8,2 and raw mode */ + /* HUPCL to clear DTR,RTS on last close */ + tcgetattr( com_fd, &tio ); + tio.c_oflag = 0; + tio.c_iflag = IGNBRK | IGNPAR; + tio.c_cflag = CREAD | CLOCAL | CS8 | HUPCL | CSTOPB; + cfsetispeed( &tio, B115200 ); + cfsetospeed( &tio, B115200 ); + tio.c_lflag = NOFLSH; + cfmakeraw( &tio ); + for ( i = 0; i <= NCCS; ++i ) tio.c_cc[i] = 0; + tio.c_cc[VMIN] = 1; + tio.c_cc[VTIME] = 0; + tcsetattr( com_fd, TCSANOW, &tio ); + /* set line signals DTR on, RTS off */ + com_setsigs( TIOCM_DTR|TIOCM_RTS ); + /* back to blocking mode for timeout */ + if ( (flags = fcntl( com_fd, F_GETFL, 0 )) != -1 ) { + flags &= ~O_NONBLOCK; + fcntl( com_fd, F_SETFL, flags ); + } + _com_sigchg(); + + return OK; +} + +/* send datablock with waiting +* --------------------------- +* IC35 gets receive receive errors if datablocks of more than 16 bytes +* are sent at full speed of 115200 baud, using 2 stopbits increases +* this size limit to 29 bytes. +* the problem occurred only with IC35 MMCard operations, import and +* synchronize PIM-data don't suffer this limitation. +* as a workaround do delay some time after sent every couple of bytes. +* the delay is done with busy waiting using gettimeofday(), because +* interrupt controlled delays are either too long (at least 10 msec +* with an "itimer" at Linux-2.x timer interrupt frequency of 100 Hz) +* or require root privilege (nanosleep() or fast RTC interrupts). +* other methods like usleep(), nanosleep(), select() yielded delays +* of ca. 20..30 msec and were therefore also not chosen. +* (see getitimer(2), sigaction(2), nanosleep(2), usleep(3), select(2) +* for reference). +* to avoid too much CPU hogging during busywait delays, the process +* scheduling priority may be lowered using nice(2) when comsendw() +* is called with a block of more than NICE_MINBLEN bytes. +* com_waitnice() is used to enable the priority lowering: each call +* of com_waitnice() causes one nice() systemcall. +* the maximum throughput measured with write file to IC35 MMCard was +* ca. 1740 b/s with 2kB buffersize (with "itimer" it was ca. 1350 b/s, +* with usleep() delay it was only ca. 600 b/s). +* measurements on a Pentium-I/133MHz showed that +* for blocks of 1 8 16 24 28 bytes +* the delay of 165 1570 3180 4795 5620 usec was needed at least. +* measurements on a Pentium-III/500MHz (by Harald Becker) showed that +* for blocks of 1 16 bytes +* the delay of 187 3212 usec was needed at least. +*/ +#define NICE_MINBLEN 32 /* min. block length for nice() */ +#define WAIT_BLKSIZE 16 /* number of bytes per write() */ +#define WAIT_USDELAY 3250 /* usec to wait before write() */ + +static int _waitnice_inc; /* increment to use for nice(2) */ + +static void /* busy wait some microseconds */ +_wait_usec( long usec ) /* contributed by Harald Becker */ +{ + long elapsed; /* elapsed delay in usec */ + struct timeval tbeg, tnow; /* start time and current time */ + + gettimeofday( &tbeg, NULL ); /* get start time of delay loop */ + do { /* loop .. */ + gettimeofday( &tnow, NULL ); /* get current time */ + if ( (elapsed = tnow.tv_sec - tbeg.tv_sec) ) /* seconds diff */ + elapsed *= 1000000; /* to usec if nonzero */ + elapsed += tnow.tv_usec - tbeg.tv_usec; /* add microsecs diff */ + } while( elapsed < usec ); /* .. until specified usec over */ +} + +void /* export interface to store increment */ +com_waitnice( int inc ) /* 'inc' for lowering process priority */ +{ + _waitnice_inc = inc; +} + +int /* send data to communication device */ +com_sendw( uchar * data, size_t dlen ) /* with busywait delays */ +{ + uchar * dptr; + int slen; + +#ifndef NO_LOGSIM + if ( com_simul() ) + slen = dlen; + else +#endif + { + if ( com_fd < 0 ) + return ERR; + LPRINTF(( L_DEBUG, "com_sendw(%p,%u) ..", data, dlen )); + if ( dlen >= NICE_MINBLEN && _waitnice_inc != 0 ) { + nice( _waitnice_inc ); /* lower own process priority */ + _waitnice_inc = 0; /* no more nice() before next */ + } /* com_waitnice() tells again */ + dptr = data; + while ( (slen = data+dlen - dptr) > 0 ) { + if ( slen > WAIT_BLKSIZE ) slen = WAIT_BLKSIZE; + _wait_usec( WAIT_USDELAY ); + if ( write( com_fd, dptr, slen ) != slen ) + break; + dptr += slen; + } + slen = dptr - data; + } + LDUMP(( L_NOISE, data, dlen, + "com_sendw(%p,%u) = %d", data, dlen, slen )); + return slen; +} + +/* send datablock to comm.device +* ----------------------------- +*/ +int +com_send( uchar * data, size_t dlen ) +{ + int slen; + +#ifndef NO_LOGSIM + if ( com_simul() ) + slen = dlen; + else +#endif + { + if ( com_fd < 0 ) + return ERR; + LPRINTF(( L_DEBUG, "com_send(%p,%u) ..", data, dlen )); + slen = write( com_fd, data, dlen ); + } + LDUMP(( L_NOISE, data, dlen, + "com_send(%p,%u) = %d", data, dlen, slen )); + return slen; +} + +/* receive datablock from comm.device +* ---------------------------------- +* use select() to wait for data, accumulate data with read() +* until buffer 'buff' gets full or receive timeout occurs. +*/ +int +com_recv( uchar * buff, size_t blen ) +{ + fd_set rfds; + struct timeval tmo; + uchar *rptr, *rend; + int rlen; + +#ifndef NO_LOGSIM + if ( com_simul() ) + rlen = com_simrecv( buff, blen ); + else +#endif + { + if ( com_fd < 0 ) + return ERR; + rend = (rptr = buff) + blen; + do { + FD_ZERO( &rfds ); + FD_SET( com_fd, &rfds ); + tmo.tv_sec = com_tmo / 1000; + tmo.tv_usec = (com_tmo % 1000) * 1000; + if ( select( com_fd+1, &rfds, NULL, NULL, &tmo ) > 0 + && FD_ISSET( com_fd, &rfds ) ) { + _com_sigchg(); + rlen = read( com_fd, rptr, rend - rptr ); + LPRINTF(( L_DEBUG, "com_recv: read(com,rptr,%d) = %d", + rend-rptr, rlen )); + if ( rlen > 0 ) + rptr += rlen; + } else { + rlen = 0; + } + } while ( rlen > 0 && rptr < rend ); + rlen = rptr - buff; + } + if ( rlen > 0 ) + LDUMP(( L_NOISE, buff, rlen, + "com_recv(%p,%u) = %d", buff, blen, rlen )); + else + LPRINTF(( L_NOISE, "com_recv(%p,%u) = %d", buff, blen, rlen )); + + return rlen; +} + +/* close comm.device +* ----------------- +* leave simulated communication if active. +* clear RS232 signals DTR and RTS and close comm.device. +*/ +void +com_exit( void ) +{ +#ifndef NO_LOGSIM + com_simexit(); +#endif + if ( com_fd < 0 ) + return; + com_setsigs( 0 ); /* DTR off, RTS off */ + _com_sigchg(); + close( com_fd ); + com_fd = -1; +} + +/* substitute for usleep() +* ----------------------- +* the usleep() function is unavailable on compilation with "-ansi", +* which #defines __STRICT_ANSI__ +*/ +#ifdef __STRICT_ANSI__ +void +usleep( unsigned long usec ) +{ + struct timeval tmo; + + tmo.tv_sec = usec / 1000000; + tmo.tv_usec = usec % 1000000; + select( 0, NULL, NULL, NULL, &tmo ); +} +#endif /*__STRICT_ANSI__*/ diff --git a/src/comio.h b/src/comio.h new file mode 100644 index 0000000..6ff628e --- /dev/null +++ b/src/comio.h @@ -0,0 +1,37 @@ +/************************************************************************ +* Copyright (C) 2000 Thomas Schulz * +* * +* $Id: comio.h,v 1.7 2001/03/03 16:30:45 tsch Rel $ * +* * +* header for IC35 serial communication * +* * +************************************************************************/ +#ifndef _COMIO_H +#define _COMIO_H 1 + +#include /* size_t */ + +#include "util.h" /* uchar, NO_LOGSIM */ + + +#ifdef NO_LOGSIM +#define COM_SIMINIT(arg) +#else +#define COM_SIMINIT(arg) com_siminit arg +#endif + + +void com_siminit( char * s_fname ); /* init comm.simulation */ +int com_settimeout( int msec ); /* set timeout [msec] */ +int com_init( char * devname ); /* initialize comm.device */ +void com_waitnice( int inc ); /* for nice() in com_sendw*/ +int com_sendw( uchar * data, size_t dlen ); /* send data with waiting */ +int com_send( uchar * data, size_t dlen ); /* send data to comm.dev */ +int com_recv( uchar * buff, size_t blen ); /* receive from comm.dev */ +void com_exit( void ); /* close comm.device */ + +#ifdef __STRICT_ANSI__ +void usleep( unsigned long usec ); +#endif + +#endif /*_COMIO_H*/ diff --git a/src/databin.c b/src/databin.c new file mode 100644 index 0000000..7d876b7 --- /dev/null +++ b/src/databin.c @@ -0,0 +1,362 @@ +/************************************************************************ +* Copyright (C) 2000 Thomas Schulz * +* */ + static char rcsid[] = + "$Id: databin.c,v 1.35 2001/03/02 02:09:59 tsch Rel $"; /* +* * +* IC35 synchronize data import/export: binary IC35 record format * +* * +************************************************************************* +* * +* ??? is "fixme" mark: sections of code needing fixes * +* * +************************************************************************/ + +#include /* fprintf(), .. */ +#include /* strcpy(), .. */ +#include /* isprint(), .. */ +#include /* access() */ +#include /* size_t, .. */ +#include /* struct tm, time() .. */ + +#include "vcc.h" /* VObject, .. */ +#include "util.h" /* ERR, uchar, .. */ +#include "ic35frec.h" /* IC35 record fields.. */ +#include "dataio.h" +NOTUSED(rcsid); + + +#pragma pack(1) +struct binrec { /* IC35 binary record data */ + size_t len; /* length of this record */ + ulong rid; /* record id on IC35 */ + uchar chg; /* changeflag on IC35 */ +}; /* flengths,fdata (dyn.length) */ +#pragma pack() +struct binelmt { /* IC35 binrec list element */ + struct binelmt * next; /* next element in list */ + IC35REC * rec; /* IC35 record */ +}; + +static struct binelmt * binlist; +static struct binelmt * binget; + +/* internal: write IC35 binary record to file +* ------------------------------------------ +*/ +static int +_bin_writerec( FILE * outfp, IC35REC * rec ) +{ + uchar * recdata; + size_t reclen; + struct binrec brec; + + get_ic35recdata( rec, &recdata, &reclen ); + brec.len = sizeof(brec) + reclen; + brec.rid = ic35recid( rec ); + brec.chg = ic35recchg( rec ); + if ( fwrite( &brec, sizeof(brec), 1, outfp ) != 1 + || fwrite( recdata, reclen, 1, outfp ) != 1 ) + return ERR; + return OK; +} +/* internal: read IC35 binary record from file +* ------------------------------------------- +*/ +static int +_bin_readrec( FILE * infp, IC35REC * rec ) +{ + struct binrec brec; + uchar * rbuff; + size_t rlen, i; + + if ( fread( &brec, sizeof(brec), 1, infp ) != 1 ) + return -1; + set_ic35recid( rec, brec.rid ); + set_ic35recchg( rec, brec.chg ); + rlen = brec.len - sizeof(brec); + if ( rlen > 0 + && (rbuff = malloc( rlen )) != NULL ) { + if ( fread( rbuff, rlen, 1, infp ) == 1 ) + set_ic35recdata( rec, rbuff, rlen ); + else + rlen = ERR; + free( rbuff ); + } else { + for ( i = 0; i < rlen; ++i ) + (void)fgetc( infp ); + } + return rlen; +} + +/* open IC35 binary record file +* ---------------------------- +* read input file into internal record list +*/ +static char * iomode; +static char * addr_fname; + +static int +bin_open( char * mode, char * addrfname, char * vcalfname, char * memofname ) +{ + FILE * infp; + IC35REC * ic35rec; + struct binelmt * newelmt; + struct binelmt * pelmt; + + LPRINTF(( L_INFO, "bin_open(%s,%s,%s)", + addrfname ? addrfname : "NULL", + vcalfname ? vcalfname : "NULL", + memofname ? memofname : "NULL" )); + iomode = mode; /* note input/output for close */ + addr_fname = addrfname; /* note filename for close */ + binlist = binget = NULL; /* reset record list */ + if ( !( addrfname && *addrfname ) + || iomode[0] != 'r' ) /* open for output only */ + return OK; + if ( strcmp( addrfname, "-" ) == 0 ) + infp = stdin; + else if ( (infp = fopen( addrfname, "r" )) == NULL ) + return access( addrfname, F_OK ) == 0 ? ERR : OK; + LPRINTF(( L_INFO, "bin_open: read %s ..", addrfname )); + pelmt = NULL; + for ( ; ; ) { + if ( (ic35rec = new_ic35rec()) == NULL + || _bin_readrec( infp, ic35rec ) < 0 ) + break; + if ( (newelmt = malloc( sizeof(*newelmt) )) != NULL ) { + newelmt->next = NULL; + newelmt->rec = ic35rec; + if ( pelmt == NULL ) + binlist = pelmt = newelmt; + else + pelmt = pelmt->next = newelmt; + } + } + if ( infp != stdin ) + fclose( infp ); + del_ic35rec( ic35rec ); + LPRINTF(( L_INFO, "bin_open: read %s done", addrfname )); + return OK; +} +/* close IC35 binary record file +* ----------------------------- +* write internal record list to output file +* if closing input file only release record list +*/ +static void +bin_close( void ) +{ + FILE * outfp = NULL; + struct binelmt * delelmt; + + if ( addr_fname && *addr_fname + && (iomode[0] == 'w' || iomode[1] == '+') + && (outfp = backup_and_openwr( addr_fname )) == NULL ) + return; + LPRINTF(( L_INFO, "bin_close: %s", + outfp && binlist ? "write .." : "no output" )); + LPRINTF(( L_INFO, "bin_close: write %s ..", addr_fname )); + while ( binlist ) { + if ( outfp ) + _bin_writerec( outfp, binlist->rec ); + delelmt = binlist; + binlist = binlist->next; + del_ic35rec( delelmt->rec ); + free( delelmt ); + } + binget = NULL; /* sanity for bin_getrec() */ + if ( outfp && outfp != stdout ) + fclose( outfp ); + LPRINTF(( L_INFO, "bin_close: done" )); +} + +/* rewind list of binary records +* ----------------------------- +*/ +static void +bin_rewind( void ) +{ + binget = NULL; +} + +/* get binary record for fileid +* ---------------------------- +*/ +static IC35REC * +bin_getrec( int fileid ) +{ + if ( binget == NULL ) + binget = binlist; + else + binget = binget->next; + while ( binget ) { + if ( fileid == FILE_ANY /* next record with any fileid */ + || FileId( ic35recid( binget->rec ) ) == fileid ) + return binget->rec; + binget = binget->next; + } + return NULL; +} +/* get binary record by record-ID +* ------------------------------ +*/ +static IC35REC * +bin_getrec_byID( ulong recid ) +{ + struct binelmt * pelmt; + + if ( RecId( recid ) == 0 ) + return NULL; + for ( pelmt = binlist; pelmt; pelmt = pelmt->next ) + if ( ic35recid( pelmt->rec ) == recid ) + return pelmt->rec; + return NULL; +} + +/* compare IC35 record with binary record +* -------------------------------------- +*/ +static int +bin_cmpic35rec( IC35REC * ic35rec, IC35REC * binrec ) +{ + return cmp_ic35rec( ic35rec, binrec ); +} + +/* update IC35 record with binary record +* ------------------------------------- +*/ +static IC35REC * +bin_updic35rec( IC35REC * ic35rec, IC35REC * rec ) +{ + uchar * data; + size_t dlen; + + if ( ic35rec == NULL + && (ic35rec = new_ic35rec()) == NULL ) + return NULL; + set_ic35recid( ic35rec, ic35recid( rec ) ); + get_ic35recdata( rec, &data, &dlen ); + set_ic35recdata( ic35rec, data, dlen ); + return ic35rec; +} + +/* put IC35 record to (new) binary record +* -------------------------------------- +*/ +static IC35REC * +bin_putic35rec( IC35REC * ic35rec ) +{ + struct binelmt * pelmt; + struct binelmt * newelmt; + struct binelmt * lastinfile; + struct binelmt * last; + IC35REC * rec; + uchar * data; + size_t dlen; + + if ( (rec = bin_getrec_byID( ic35recid( ic35rec ) )) == NULL ) { + if ( (newelmt = malloc( sizeof(*newelmt) )) == NULL + || (rec = newelmt->rec = new_ic35rec()) == NULL ) { + free( newelmt ); + return NULL; + } + last = lastinfile = NULL; + for ( pelmt = binlist; pelmt; pelmt = pelmt->next ) { + if ( FileId( ic35recid( pelmt->rec ) ) + == FileId( ic35recid( ic35rec ) ) ) + lastinfile = pelmt; + last = pelmt; + } + if ( lastinfile ) last = lastinfile; + if ( last == NULL ) { + binlist = newelmt; + newelmt->next = NULL; + } else { + newelmt->next = last->next; + last->next = newelmt; + } + } + set_ic35recid( rec, ic35recid( ic35rec ) ); + get_ic35recdata( ic35rec, &data, &dlen ); + set_ic35recdata( rec, data, dlen ); + return rec; +} + +/* delete binary record +* -------------------- +*/ +static void +bin_delrec( IC35REC * delrec ) +{ + struct binelmt ** pnext; + struct binelmt * pelmt; + + for ( pnext = &binlist, pelmt = *pnext; pelmt != NULL; + pnext = &pelmt->next, pelmt = *pnext ) + if ( pelmt->rec == delrec ) { + *pnext = pelmt->next; + del_ic35rec( pelmt->rec ); + free( pelmt ); + break; + } +} + +/* get,set binary record-ID +* ------------------------ +*/ +static ulong +bin_recid( IC35REC * rec ) +{ + return ic35recid( rec ); +} +static void +bin_set_recid( IC35REC * rec, ulong recid ) +{ + set_ic35recid( rec, recid ); +} + +/* get,set binary record status +* ---------------------------- +*/ +static int +bin_recstat( IC35REC * rec ) +{ + switch ( ic35recchg( rec ) ) { + case IC35_CLEAN: return PIM_CLEAN; + case IC35_NEW: + case IC35_MOD: return PIM_DIRTY; + case IC35_DEL: return PIM_DEL; + } + return PIM_DIRTY; +} +static void +bin_set_recstat( IC35REC * rec, int stat ) +{ + switch ( stat ) { + case PIM_CLEAN: set_ic35recchg( rec, IC35_CLEAN ); break; + case PIM_DIRTY: set_ic35recchg( rec, IC35_NEW ); break; + case PIM_DEL: set_ic35recchg( rec, IC35_DEL ); break; + } +} + + +/* binary format operations +* ------------------------ +*/ +struct pim_oper bin_oper = { + bin_open, + bin_close, + bin_rewind, + (void*(*)(int))bin_getrec, + (void*(*)(ulong))bin_getrec_byID, + (int(*)(IC35REC*,void*))bin_cmpic35rec, + (IC35REC*(*)(IC35REC*,void*))bin_updic35rec, + (void*(*)(IC35REC*))bin_putic35rec, + (void(*)(void*))bin_delrec, + (ulong(*)(void*))bin_recid, + (void(*)(void*,ulong))bin_set_recid, + (int(*)(void*))bin_recstat, + (void(*)(void*,int))bin_set_recstat, + }; + diff --git a/src/dataio.c b/src/dataio.c new file mode 100644 index 0000000..92858b6 --- /dev/null +++ b/src/dataio.c @@ -0,0 +1,428 @@ +/************************************************************************ +* Copyright (C) 2000 Thomas Schulz * +* */ + static char rcsid[] = + "$Id: dataio.c,v 1.33 2001/03/02 02:09:59 tsch Rel $"; /* +* * +* IC35 synchronize data import/export * +* * +************************************************************************* +* * +* ??? is "fixme" mark: sections of code needing fixes * +* * +* backup_and_openwr backup and open output file for write * +* revised date+time on IC35 * +* clr_newic35dt clear new IC35 revised to get it fresh * +* get_newic35dt new revised date+time to IC35 string * +* set_oldic35dt IC35 string to old revised date+time * +* * +* PIM format +* pimfmt2bin local: PIM format text to binary +* set_pim_format set PIM format +* pim_format report current PIM format +* pimop[] functions table of format-specific ops +* PIM file access +* pim_open open PIMfile(s) and read if present +* pim_openinp open PIMfile(s) for input and read +* pim_openout note PIMfile(s) for output +* pim_close close and flush to PIMfile(s) +* PIM record operations +* pim_rewind rewind PIM record list +* pim_getrec get next PIM record for fileid +* pim_getrec_byID get PIM record by rec-ID (and fileid) +* pim_updic35rec update IC35 record with PIM record +* pim_cmpic35rec compare IC35 record with PIM record +* pim_putic35rec put IC35 record to (new) PIM record +* pim_putrec output (new) PIM record +* pim_delrec delete PIM record +* PIM record IC35-recID and status +* pim_recid report IC35 record-ID from PIM record +* pim_set_recid set IC35 record-ID to PIM record +* pim_recstat report PIM record changeflag +* pim_set_recstat set PIM record changeflag +* * +************************************************************************/ + +#include /* fprintf(), .. */ +#include /* strcpy(), .. */ +#include /* isprint(), .. */ +#include /* access() */ +#include /* size_t, .. */ +#include /* struct tm, time() .. */ + +#include "vcc.h" /* VObject, .. */ +#include "util.h" /* ERR, uchar, .. */ +#include "ic35frec.h" /* IC35 record fields.. */ +#include "dataio.h" +NOTUSED(rcsid); + + +/* backup and open output file for write +* ------------------------------------- +*/ +FILE * +backup_and_openwr( char * fname ) +{ + char * bkpfname; + FILE * fp; + + if ( !( fname && *fname ) ) + return NULL; + if ( strcmp( fname, "-" ) == 0 ) + return stdout; + if ( (bkpfname = malloc( strlen( fname ) + 2 )) != NULL ) { + strcat( strcpy( bkpfname, fname ), "~" ); + remove( bkpfname ); + rename( fname, bkpfname ); + free( bkpfname ); + } + if ( (fp = fopen( fname, "w" )) == NULL ) + error( "cannot open outfile: %s", fname ); + return fp; +} + + +/* ============================================ */ +/* revised date+time on IC35 */ +/* ============================================ */ + +/* +* IC35 does not maintain last modified date+time per record +* instead there is a command to write date+time to IC35, +* which is used for synchronization. +* this timestamp is also used for "last modified" VObject property +*/ + +/* revised date+time for vCard,vCal records +* ---------------------------------------- +*/ +static time_t _new_ic35dtime; /* to vCard,vCal records and IC35 */ + +void +clr_newic35dt( void ) +{ + _new_ic35dtime = 0; +} +time_t +newic35dt( void ) +{ + if ( _new_ic35dtime == 0 ) + _new_ic35dtime = time( NULL ); + return _new_ic35dtime; +} + +/* new revised date+time to string for IC35 +* ---------------------------------------- +*/ +void +get_newic35dt( char * dtbuf ) +{ + time_t ic35dt; + struct tm * ptm; + + ic35dt = newic35dt(); + ptm = localtime( &ic35dt ); + strftime( dtbuf, 2+2+4+2+2+2+1, "%m%d%Y%H%M%S", ptm ); +} + +/* string from IC35 to old revised date+time +* ----------------------------------------- +* the ugly format [mmddyyyyhhmmss] (instead of pretty [yyyymmddhhmmss]) +* is used for reasons of compatibility with IC35sync/Windows. +* if sysinfo string from IC35 lacks plausible year,month,day, assume +* sysinfo was never written to IC35 and use old reference date+time +* far in the past. +*/ +static time_t _old_ic35dtime; /* from IC35, reference for vCard */ + +void +set_oldic35dt( char * dtime ) +{ + struct tm ic35tm; + + if ( dtime == NULL ) + return; + memset( &ic35tm, 0, sizeof(ic35tm) ); + sscanf( dtime, "%2d%2d%4d%2d%2d%2d", + &ic35tm.tm_mon, &ic35tm.tm_mday, &ic35tm.tm_year, + &ic35tm.tm_hour, &ic35tm.tm_min, &ic35tm.tm_sec ); + if ( 1970 <= ic35tm.tm_year + && 1 <= ic35tm.tm_mon && ic35tm.tm_mon <= 12 + && 1 <= ic35tm.tm_mday && ic35tm.tm_mday <= 31 ) { + ic35tm.tm_year -= 1900; + ic35tm.tm_mon -= 1; + ic35tm.tm_isdst = -1; + _old_ic35dtime = mktime( &ic35tm ); + } else + _old_ic35dtime = 1; +} +time_t +oldic35dt( void ) +{ + return _old_ic35dtime; +} + + +/* ============================================ */ +/* generic PIM access */ +/* ============================================ */ + +/* set, get format of input/output file(s) +* --------------------------------------- +*/ +static int pimfmt; +static int inpfmt; + +static int +pimfmt2bin( char * format ) +{ + if ( format ) { + if ( strcmp( format, "txt" ) == 0 ) return PIM_TXT; + else if ( strcmp( format, "bin" ) == 0 ) return PIM_BIN; + else if ( strcmp( format, "vca" ) == 0 ) return PIM_VCA; + } + return 0; +} +int +set_pim_format( char * format ) +{ + int newfmt; + + if ( (newfmt = pimfmt2bin( format )) <= 0 ) + return ERR; /* unknown format */ + pimfmt = inpfmt = newfmt; + return OK; +} +int +pim_format( void ) +{ + return pimfmt; +} + +/* +* pim_open() +* pim_close() +* pim_rewind() +* +* pim_getrec( int fileid ) -> void* / NULL +* pim_getrec_byID( ulong recid ) -> void* / NULL +* pim_cmpic35rec( IC35REC*, void* ) -> 0:same 1:differ +* pim_putic35rec( IC35REC* ) IC35REC to PIMrec +* pim_updic35rec( IC35REC*, void* ) PIMrec to IC35REC +* pim_delrec( void* ) +* +* pim_recid( void* ) +* pim_set_recid( void*, ulong ) +* pim_recstat( void* ) +* pim_set_recstat( void*, int ) +*/ + +/* dummy null operations +* --------------------- +*/ +static struct pim_oper nul_oper = { + NULL, /* NO open */ + NULL, /* NO close */ + NULL, /* NO rewind */ + NULL, /* NO getrec */ + NULL, /* NO getrec_byID */ + NULL, /* NO cmpic35rec */ + NULL, /* NO updic35rec */ + NULL, /* NO putic35rec */ + NULL, /* NO delrec */ + NULL, /* NO recid */ + NULL, /* NO set_recid */ + NULL, /* NO recstat */ + NULL, /* NO set_recstat */ + }; + +/* table of supported PIM-formats +* ------------------------------ +*/ +extern struct pim_oper txt_oper; /* text output IC35 record */ +extern struct pim_oper bin_oper; /* binary IC35 record format */ +extern struct pim_oper vca_oper; /* vCard,vCalendar format */ + +static struct pim_oper *pimop[] = { + &nul_oper, + &txt_oper, + &bin_oper, + &vca_oper + }; + +/* open, close, rewind output file(s) +* ---------------------------------- +*/ +int +pim_open( char * addrfname, char * vcalfname, char * memofname ) +{ + LPRINTF(( L_INFO, "pim_open(%s,%s,%s) pimfmt=%d (%s)", + addrfname ? addrfname : "NULL", + vcalfname ? vcalfname : "NULL", + memofname ? memofname : "NULL", + pimfmt, pimfmt == PIM_TXT ? "txt" : + pimfmt == PIM_BIN ? "bin" : + pimfmt == PIM_VCA ? "vca" : "unknown" )); +/*??? check file(s) creatable in dirname(a_xxxxfname) ???*/ + if ( pimop[pimfmt]->open ) + return (*pimop[pimfmt]->open)( "r+", addrfname, vcalfname, memofname ); + return ERR; +} +int +pim_openinp( char * format, char * fname ) +{ + int ifmt; + + if ( (ifmt = pimfmt2bin( format )) <= 0 + || pimop[ifmt]->open == NULL ) + return ERR; + inpfmt = ifmt; + return (*pimop[inpfmt]->open)( "r", fname, NULL, NULL ); +} +int +pim_openout( char * format, char * fname ) +{ + int ofmt; + + if ( (ofmt = pimfmt2bin( format )) <= 0 + || pimop[ofmt]->open == NULL + || !( fname && *fname ) ) + return ERR; + pimfmt = ofmt; +/*??? check file creatable in dirname(fname) ???*/ + remove( fname ); + return (*pimop[pimfmt]->open)( "w", fname, NULL, NULL ); +} +void +pim_close( void ) +{ + LPRINTF(( L_INFO, "pim_close: pimfmt=%d (%s) inpfmt=%d (%s)", + pimfmt, pimfmt == PIM_TXT ? "txt" : + pimfmt == PIM_BIN ? "bin" : + pimfmt == PIM_VCA ? "vca" : "unknown", + inpfmt, inpfmt == PIM_TXT ? "txt" : + inpfmt == PIM_BIN ? "bin" : + inpfmt == PIM_VCA ? "vca" : "unknown" )); + if ( pimop[pimfmt]->close ) /* write output file and .. */ + (*pimop[pimfmt]->close)(); /* release output record list */ + if ( inpfmt != pimfmt /* input format is different .. */ + && *pimop[inpfmt]->close ) /* release input record list */ + (*pimop[inpfmt]->close)(); +} +void +pim_rewind( void ) +{ + if ( pimop[inpfmt]->rewind ) + (*pimop[inpfmt]->rewind)(); +} + +/* get next PIM-record for fileid +* ------------------------------ +*/ +void * +pim_getrec( int fileid ) +{ + if ( pimop[inpfmt]->getrec ) + return (*pimop[inpfmt]->getrec)( fileid ); + return NULL; +} +/* get PIM-record by rec-ID for fileid +* ----------------------------------- +*/ +void * +pim_getrec_byID( ulong recid ) +{ + if ( pimop[inpfmt]->getrec_byID ) + return (*pimop[inpfmt]->getrec_byID)( recid ); + return NULL; +} + +/* update IC35 record with PIM record +* ---------------------------------- +*/ +IC35REC * +pim_updic35rec( IC35REC * ic35rec, void * pimrec ) +{ + if ( pimop[inpfmt]->updic35rec ) + return (*pimop[inpfmt]->updic35rec)( ic35rec, pimrec ); + return NULL; +} + +/* compare IC35 record with vCard,vCal record +* ------------------------------------------ +*/ +int +pim_cmpic35rec( IC35REC * ic35rec, void * pimrec ) +{ + if ( pimop[pimfmt]->cmpic35rec ) + return (*pimop[pimfmt]->cmpic35rec)( ic35rec, pimrec ); + return -1; +} + +/* put IC35-record to (new) PIM-record +* ----------------------------------- +*/ +void * +pim_putic35rec( IC35REC * ic35rec ) +{ + if ( pimop[pimfmt]->putic35rec ) + return (*pimop[pimfmt]->putic35rec)( ic35rec ); + return NULL; +} +void +pim_putrec( void * pimrec ) +{ + IC35REC * ic35rec; + + if ( pimop[inpfmt]->updic35rec != NULL + && pimop[pimfmt]->putic35rec != NULL + && (ic35rec = new_ic35rec()) != NULL ) { + (*pimop[inpfmt]->updic35rec)( ic35rec, pimrec ); + (*pimop[pimfmt]->putic35rec)( ic35rec ); + del_ic35rec( ic35rec ); + } +} + +/* delete PIM-record +* ----------------- +*/ +void +pim_delrec( void * pimrec ) +{ + if ( pimop[pimfmt]->delrec ) + (*pimop[pimfmt]->delrec)( pimrec ); +} + +/* get,set IC35-record-ID +* ---------------------- +*/ +ulong +pim_recid( void * pimrec ) +{ + if ( pimop[pimfmt]->recid ) + return (*pimop[pimfmt]->recid)( pimrec ); + return 0; +} +void +pim_set_recid( void * pimrec, ulong recid ) +{ + if ( pimop[pimfmt]->set_recid ) + (*pimop[pimfmt]->set_recid)( pimrec, recid ); +} + +/* get,set record-changeflag +* ------------------------- +*/ +int +pim_recstat( void * pimrec ) +{ + if ( pimop[pimfmt]->recstat ) + return (*pimop[pimfmt]->recstat)( pimrec ); + return PIM_DIRTY; +} +void +pim_set_recstat( void * pimrec, int stat ) +{ + if ( pimop[pimfmt]->set_recstat ) + (*pimop[pimfmt]->set_recstat)( pimrec, stat ); +} diff --git a/src/dataio.h b/src/dataio.h new file mode 100644 index 0000000..7e05893 --- /dev/null +++ b/src/dataio.h @@ -0,0 +1,77 @@ +/************************************************************************ +* Copyright (C) 2000 Thomas Schulz * +* * +* $Id: dataio.h,v 1.12 2000/12/26 01:25:22 tsch Rel $ * +* * +* header for IC35 data import/export * +* * +************************************************************************/ +#ifndef _DATAIO_H +#define _DATAIO_H 1 + +#include /* FILE */ +#include /* size_t */ + +#include "vcc.h" /* VObject, .. */ +#include "ic35frec.h" /* IC35REC */ +#include "util.h" /* uchar, .. */ + + +/* PIM file format ids */ +#define PIM_TXT 1 /* plain text */ +#define PIM_BIN 2 /* raw binary data */ +#define PIM_VCA 3 /* vCard,vCalendar,Memo */ +/* PIM record status */ +#define PIM_CLEAN 0 +#define PIM_DIRTY 1 +#define PIM_DEL 3 + + +struct pim_oper { /* table of record format specific functions */ + int (*open)( char *, char *, char *, char * ); + void (*close)( void ); + void (*rewind)( void ); + void * (*getrec)( int ); + void * (*getrec_byID)( ulong ); + int (*cmpic35rec)( IC35REC *, void * ); /* IC35 == PIM */ + IC35REC * (*updic35rec)( IC35REC *, void * ); /* IC35 <- PIM */ + void * (*putic35rec)( IC35REC * ); /* IC35 -> PIM */ + void (*delrec)( void * ); + ulong (*recid)( void * ); + void (*set_recid)( void *, ulong ); + int (*recstat)( void * ); + void (*set_recstat)( void *, int ); +}; + + +FILE * backup_and_openwr( char * fname ); + +int set_pim_format( char * format ); +int pim_format( void ); + +void clr_newic35dt( void ); +void get_newic35dt( char * dtbuf ); +time_t newic35dt( void ); +void set_oldic35dt( char * dtime ); +time_t oldic35dt( void ); + +int pim_open( char * addrfname, char * vcalfname, char * memofname ); +int pim_openinp( char * format, char * fname ); +int pim_openout( char * format, char * fname ); +void pim_close( void ); +void pim_rewind( void ); + +void * pim_getrec( int fileid ); +void * pim_getrec_byID( ulong recid ); +void * pim_putic35rec( IC35REC * ic35rec ); +IC35REC * pim_updic35rec( IC35REC * ic35rec, void * pimrec ); +int pim_cmpic35rec( IC35REC * ic35rec, void * pimrec ); +void pim_putrec( void * pimrec ); +void pim_delrec( void * pimrec ); + +ulong pim_recid( void * pimrec ); +void pim_set_recid( void * pimrec, ulong recid ); +int pim_recstat( void * pimrec ); +void pim_set_recstat( void * pimrec, int stat ); + +#endif /*_DATAIO_H*/ diff --git a/src/datatxt.c b/src/datatxt.c new file mode 100644 index 0000000..8aa242f --- /dev/null +++ b/src/datatxt.c @@ -0,0 +1,113 @@ +/************************************************************************ +* Copyright (C) 2000 Thomas Schulz * +* */ + static char rcsid[] = + "$Id: datatxt.c,v 1.32 2000/12/26 01:36:32 tsch Rel $"; /* +* * +* IC35 synchronize data import/export: text format output * +* * +************************************************************************* +* * +* ??? is "fixme" mark: sections of code needing fixes * +* * +************************************************************************/ + +#include /* fprintf(), .. */ +#include /* isprint(), .. */ +#include /* size_t, .. */ + +#include "util.h" /* ERR, uchar, .. */ +#include "ic35frec.h" /* IC35 record fields.. */ +#include "dataio.h" /* struct pim_oper */ +NOTUSED(rcsid); + + +/* open, close text file +* --------------------- +*/ +static FILE * outfp; + +static int +txt_open( char * mode, char * addrfname, char * vcalfname, char * memofname ) +{ + if ( !(mode[0] == 'w' || mode[1] == '+') + || (outfp = backup_and_openwr( addrfname )) == NULL ) + return ERR; + return OK; +} +static void +txt_close( void ) +{ + if ( outfp && outfp != stdout ) + fclose( outfp ); + outfp = NULL; +} + +/* output IC35 record as text +* -------------------------- +*/ +static IC35REC * +txt_putic35rec( IC35REC * rec ) +{ + static int lastfileid = -1; + static int idx; + int fileid; + int fi, i; + uchar * fld; + size_t len; + + fileid = FileId( ic35recid( rec ) ); + if ( ic35fname( fileid ) == NULL ) + return NULL; /* unknown file id */ + if ( fileid != lastfileid ) { /* first/next IC35 file */ + fprintf( outfp, "\nFile \"%s\"\n", ic35fname( fileid ) ); + lastfileid = fileid; + idx = 0; /* reset record index */ + } else { /* same IC35 file */ + ++idx; /* increment rec.index */ + } + get_ic35recdata( rec, NULL, &len ); + fprintf(outfp,"File \"%s\" Record %3d IC35id=%08lX cflag=%02X length=%d\n", + ic35fname(fileid), idx, ic35recid(rec), ic35recchg(rec), len); + for ( fi = 0; fi < ic35fnflds( fileid ); ++fi ) { + get_ic35recfld( rec, FILEfld(fileid,fi), &fld, &len ); + fprintf( outfp, "f%d(%d)\t", fi, len ); + if ( len != 0 ) { + fprintf( outfp, "\"" ); + for ( i = 0; i < len; ++i ) { + if ( isprint( fld[i] ) ) + fprintf( outfp, "%c", fld[i] ); + else + fprintf( outfp, "\\x%02X", (uchar)fld[i] ); + if ( 0 < i && i < len - 1 + && fld[i-1] == '\r' && fld[i] == '\n' ) + fprintf( outfp, "\"\n\t\"" ); + } + fprintf( outfp, "\"\n" ); + } else { + fprintf( outfp, "\n" ); + } + } + return rec; +} + + +/* text format operations +* ---------------------- +*/ +struct pim_oper txt_oper = { + txt_open, + txt_close, + NULL, /* NO rewind */ + NULL, /* NO getrec */ + NULL, /* NO getrec_byID */ + NULL, /* NO cmpic35rec */ + NULL, /* NO updic35rec */ + (void*(*)(IC35REC*))txt_putic35rec, + NULL, /* NO delrec */ + NULL, /* NO recid */ + NULL, /* NO set_recid */ + NULL, /* NO recstat */ + NULL, /* NO set_recstat */ + }; + diff --git a/src/datavca.c b/src/datavca.c new file mode 100644 index 0000000..1f19e4f --- /dev/null +++ b/src/datavca.c @@ -0,0 +1,1355 @@ +/************************************************************************ +* Copyright (C) 2000 Thomas Schulz * +* */ + static char rcsid[] = + "$Id: datavca.c,v 1.46 2001/03/02 02:09:59 tsch Rel $"; /* +* * +* IC35 synchronize data import/export: vCard,vCalendar format * +* * +************************************************************************* +* * +* ??? is "fixme" mark: sections of code needing fixes * +* * +* rev_dtime yyyy-mm-ddThh:mm:ss for VCARRD * +* mod_dtime yyyymmddThhmmss for VEVENT,VTODO,memo * +* IC35 records to/from vCard,vCalendar * +* ic35addr_to_vcard +* vcard_to_ic35addr +* ic35sched_to_vevent +* vevent_to_ic35sched +* ic35todo_to_vtodo +* vtodo_to_ic35todo +* ic35memo_to_vmemo +* vmemo_to_ic35memo +* vCard,vCal access functions * +* _vca_prodid local: vCal program korganizer,gnomecal +* _vca_rewind local: rewind record list using iterator +* _vca_getnext local: get next record using iterator +* vca_open +* vca_close +* vca_rewind +* vca_getrec +* vca_getrec_byID +* vca_updic35rec +* vca_cmpic35rec +* vca_putic35rec +* vca_delrec +* vca_recid +* vca_set_recid +* vca_recstat +* vca_set_recstat +* * +************************************************************************/ + +#include /* sprintf(), .. */ +#include /* strcpy(), .. */ +#include /* isdigit(), .. */ +#include /* access() */ +#include /* size_t, .. */ +#include /* struct tm, time() .. */ + +#include "util.h" /* ERR, uchar, .. */ +#include "vcc.h" /* VObject, .. */ +#include "vcutil.h" /* vCard,vCal utilities */ +#include "ic35frec.h" /* IC35 record fields.. */ +#include "dataio.h" +NOTUSED(rcsid); + + +/* record status values for X-PILOTSTAT */ +#define VCA_CLEAN PIM_CLEAN +#define VCA_DIRTY PIM_DIRTY +/* vCalendar program ids */ +#define VCAL_GNOME 1 +#define VCAL_KDE 2 + + +static int _vca_prodid( void ); +static void vca_set_recid( VObject * vobj, ulong recid ); +static void vca_set_recstat( VObject * vobj, int stat ); + + +/* ============================================ */ +/* IC35 Address record to/from vCard */ +/* ============================================ */ +/* +* field mapping: +* VCNameProp preferred +* VCGivenNameProp A_FirstName +* VCFamilyNameProp A_LastName +* VCFullNameProp only if VCNameProp absent +* first field A_FirstName +* last field A_LastName +* VCBirthDateProp A_BirthDate +* VCAdrProp +* VCStreetAddressProp A_Street +* VCCityProp A_City +* VCRegionProp A_Region +* VCPostalCodeProp A_ZIP +* VCCountryNameProp A_Country +* VCTelephoneProp +* VCHomeProp A_TelHome +* VCTelephoneProp +* VCWorkProp A_TelWork +* VCTelephoneProp +* VCCellularProp A_TelMobile +* VCTelephoneProp +* VCFaxProp A_TelFax +* VCEmailAddressProp +* VCInternetProp A_Email1 +* VCEmailAddressProp +* VCInternetProp A_Email2 +* VCOrgProp +* VCOrgNameProp A_Company +* VCURLProp A_URL +* VCNoteProp A_Notes +* "(def1):\n" A_Def1 +* "(def2):\n" A_Def2 +* -/- A_CategoryID default: 0x00 +* VCCategoriesProp A_Category default: "Unfiled" +* use first known standard from multiple (Business,Personal,Unfiled) +* if single use it, even if non-standard +*/ + +/* convert IC35 Address record to vCard +* ------------------------------------ +*/ +static void +ic35addr_to_vcard( IC35REC * rec, VObject * vcard ) +{ + VObject * vprop; + char * fullname, *oldstr, *ic35first, *ic35last; + + ic35first = ic35recfld( rec, A_FirstName ); + ic35last = ic35recfld( rec, A_LastName ); + vprop = SetProp( vcard, VCNameProp); + if ( (fullname = dupStringValue( vcard, VCFullNameProp )) && *fullname ) { + /* + * if old vCard (is present and) has fullname VCFullNameProp "FN:" + * substitute old VCFamilyNameProp in its value with A_FirstName + * and old VCGivenNameProp in it with A_LastName. no changes to + * VCFullNameProp will be made if VCGivenNameProp,VCFamilyNameProp + * are not substrings of it. + */ + fullname = dupSubst( oldstr = fullname, + StringValue( vprop, VCFamilyNameProp ), ic35last ); + deleteStr( oldstr ); + fullname = dupSubst( oldstr = fullname, + StringValue( vprop, VCGivenNameProp ), ic35first ); + deleteStr( oldstr ); + } else { + /* + * otherwise (no old vCard or no VCFullNameProp in it) construct + * VCFullNameProp from A_FirstName,A_LastName separated by space. + * if either A_FirstName or A_LastName is missing construct from + * the other one if present. + */ + if ( ic35first && *ic35first && ic35last && *ic35last ) { + fullname = dupStr( ic35first, strlen(ic35first)+1+strlen(ic35last) ); + strcat( strcat( fullname, " " ), ic35last ); + } else + fullname = dupStr( ic35first && *ic35first ? ic35first : + ic35last && *ic35last ? ic35last : "", 0 ); + } + SetString( vcard, VCFullNameProp, fullname ); + deleteStr( fullname ); + SetString( vprop, VCGivenNameProp, ic35first ); + SetString( vprop, VCFamilyNameProp, ic35last ); + + SetString( vcard, VCBirthDateProp, ic35recfld( rec, A_BirthDate ) ); + + if ( strlen( ic35recfld( rec, A_Street ) ) != 0 + || strlen( ic35recfld( rec, A_City ) ) != 0 + || strlen( ic35recfld( rec, A_Region ) ) != 0 + || strlen( ic35recfld( rec, A_ZIP ) ) != 0 + || strlen( ic35recfld( rec, A_Country ) ) != 0 ) { + vprop = SetProp( vcard, VCAdrProp ); + SetString( vprop, VCPostalBoxProp, "" ); + SetString( vprop, VCExtAddressProp, "" ); + SetString( vprop, VCStreetAddressProp,ic35recfld( rec, A_Street ) ); + SetString( vprop, VCCityProp, ic35recfld( rec, A_City ) ); + SetString( vprop, VCRegionProp, ic35recfld( rec, A_Region ) ); + SetString( vprop, VCPostalCodeProp, ic35recfld( rec, A_ZIP ) ); + SetString( vprop, VCCountryNameProp, ic35recfld( rec, A_Country ) ); + } else + DelProp( vcard, VCAdrProp ); + + SetTel( vcard, VCHomeProp, ic35recfld( rec, A_TelHome ) ); + SetTel( vcard, VCWorkProp, ic35recfld( rec, A_TelWork ) ); + SetTel( vcard, VCCellularProp, ic35recfld( rec, A_TelMobile ) ); + SetTel( vcard, VCFaxProp, ic35recfld( rec, A_TelFax ) ); + + SetEmail( vcard, 1, ic35recfld( rec, A_Email1 ) ); + SetEmail( vcard, 2, ic35recfld( rec, A_Email2 ) ); + + if ( strlen( ic35recfld( rec, A_Company ) ) != 0 ) { + vprop = SetProp( vcard, VCOrgProp ); + SetString( vprop, VCOrgNameProp, ic35recfld( rec, A_Company ) ); + } else + DelProp( vcard, VCOrgProp ); + + SetString( vcard, VCURLProp, ic35recfld( rec, A_URL ) ); + + SetNoteProp( vcard, DEF1PROP, ic35recfld( rec, A_Def1 ) ); + SetNoteProp( vcard, DEF2PROP, ic35recfld( rec, A_Def2 ) ); + SetNotes( vcard, ic35recfld( rec, A_Notes ) ); + + SetCategory( vcard, ic35recfld( rec, A_Category ) ); +} + +/* convert vCard to IC35 address record +* ------------------------------------ +*/ +static void +vcard_to_ic35addr( VObject * vcard, IC35REC * rec ) +{ + VObject * vprop; + VObjectIterator iter; + char * strval; + char * pstr; + const char * name; + int emailnum; + + if ( (vprop = isAPropertyOf( vcard, VCNameProp )) != NULL ) { + set_ic35recfld( rec, A_LastName, + StringValue( vprop, VCFamilyNameProp ) ); + set_ic35recfld( rec, A_FirstName, + StringValue( vprop, VCGivenNameProp ) ); + } else if ( (strval = StringValue( vcard, VCNameProp )) && *strval ) { + /* no Name property, parse first,last field from FormattedName */ + if ( (pstr = strrchr( strval, ' ' )) != NULL ) { + set_ic35recfld( rec, A_LastName, pstr+1 ); + pstr = strchr( strval, ' ' ); + *pstr = '\0'; + set_ic35recfld( rec, A_FirstName, strval ); + } else + set_ic35recfld( rec, A_LastName, strval ); + } else { + set_ic35recfld( rec, A_LastName, "NoName" ); /* IC35 needs name */ + } + vprop = isAPropertyOf( vcard, VCAdrProp ); + set_ic35recfld( rec, A_City, + StringValue( vprop, VCCityProp ) ); + set_ic35recfld( rec, A_Street, + StringValue( vprop, VCStreetAddressProp ) ); + set_ic35recfld( rec, A_Region, + StringValue( vprop, VCRegionProp ) ); + set_ic35recfld( rec, A_ZIP, + StringValue( vprop, VCPostalCodeProp ) ); + set_ic35recfld( rec, A_Country, + StringValue( vprop, VCCountryNameProp ) ); + vprop = isAPropertyOf( vcard, VCOrgProp ); + set_ic35recfld( rec, A_Company, + StringValue( vprop, VCOrgNameProp ) ); + + emailnum = 0; + set_ic35recfld( rec, A_TelHome, "" ); + set_ic35recfld( rec, A_TelWork, "" ); + set_ic35recfld( rec, A_TelMobile, "" ); + set_ic35recfld( rec, A_TelFax, "" ); + set_ic35recfld( rec, A_Email1, "" ); + set_ic35recfld( rec, A_Email2, "" ); + initPropIterator( &iter, vcard ); + while ( moreIteration( &iter ) ) { + vprop = nextVObject( &iter ); + name = vObjectName( vprop ); + if ( strcasecmp( name, VCTelephoneProp ) == 0 ) { + if ( isAPropertyOf( vprop, VCHomeProp ) ) + set_ic35recfld( rec, A_TelHome, StringValue( vprop, NULL ) ); + else if ( isAPropertyOf( vprop, VCWorkProp ) ) + set_ic35recfld( rec, A_TelWork, StringValue( vprop, NULL ) ); + else if ( isAPropertyOf( vprop, VCCellularProp ) ) + set_ic35recfld( rec, A_TelMobile, StringValue( vprop, NULL ) ); + else if ( isAPropertyOf( vprop, VCFaxProp ) ) + set_ic35recfld( rec, A_TelFax, StringValue( vprop, NULL ) ); + else if ( strcasecmp( "Business", CategoryValue( vcard ) ) == 0 ) + set_ic35recfld( rec, A_TelWork, StringValue( vprop, NULL ) ); + else + set_ic35recfld( rec, A_TelHome, StringValue( vprop, NULL ) ); + } + if ( strcasecmp( name, VCEmailAddressProp ) == 0 ) + switch ( ++emailnum ) { + case 1: + set_ic35recfld( rec, A_Email1, + StringValue( vprop, NULL ) ); + break; + case 2: + set_ic35recfld( rec, A_Email2, + StringValue( vprop, NULL ) ); + break; + } + } + + set_ic35recfld( rec, A_URL, StringValue( vcard, VCURLProp ) ); + + set_ic35recfld( rec, A_BirthDate, + isodtime_to_ymd( vcard, VCBirthDateProp ) ); + + set_ic35recfld( rec, A_Def1, NotePropValue( vcard, DEF1PROP ) ); + set_ic35recfld( rec, A_Def2, NotePropValue( vcard, DEF2PROP ) ); + set_ic35recfld( rec, A_Notes, NotesValue( vcard ) ); + + set_ic35recfld( rec, A_CategoryID, "\x00" ); + set_ic35recfld( rec, A_Category, CategoryValue( vcard ) ); +} + + +/* ==================================================== */ +/* IC35 Schedule record to/from vCal:vEvent */ +/* ==================================================== */ +/* +* field mapping: +* VCSummaryProp S_Subject +* VCDTstartProp +* date S_StartDate +* time S_StartTime +* VCDTendProp +* date S_EndDate +* time S_EndTime +* VCRRuleProp +* type D,W,MP,MD,YD S_Alarm_Repeat & RepeatMASK +* D daily RepeatDay +* W weekly RepeatWeek +* MP monthly RepeatMonWday +* MD monthly RepeatMonMday +* YD yearly RepeatYear +* count n S_RepCount +* end-isodtime S_RepEndDate only yyyymmdd, max 20311231 +* VCAAlarmProp S_Alarm_Repeat & AlarmNoBeep +* VCRunTimeProp S_AlarmBefore default: AlarmBefNone +* VCDAlarmProp S_Alarm_Repeat & AlarmNoLED +* VCRunTimeProp S_AlarmBefore default: AlarmBefNone +* VCRunTimeProp - VCDTstartProp +* < 60 AlarmBefNow +* >= 60 * 1 AlarmBef1min +* >= 60 * 5 AlarmBef5min +* >= 60 * 10 AlarmBef10min +* >= 60 * 30 AlarmBef30min +* >= 60 * 60 * 1 AlarmBef1hour +* >= 60 * 60 * 2 AlarmBef2hour +* >= 60 * 60 * 10 AlarmBef10hour +* >= 60 * 60 * 24 * 1 AlarmBef1day +* >= 60 * 60 * 24 * 2 AlarmBef2day +* VCDescriptionProp S_Notes +* -/- S_CategoryID default: 0x00 +* VCCategoriesProp S_Category default: "Unfiled" +* use first known standard from multiple (Business,Personal,Unfiled) +* if single use it, even if non-standard +* special for korganizer: +* korganizer supports only DALARM +* special for gnomecal: +* VCSummaryProp +* first line S_Subject +* subsequent lines S_Notes +* gnomecal does not support VEVENT.DESCRIPTION, but multiple lines +* in VEVENT.SUMMARY, which in turn the IC35 does not support. +* as workaround the first line of VEVENT.SUMMARY is mapped to S_Subject, +* all subsequent lines are mapped to S_Notes in IC35. +* VCClassProp PUBLIC +* gnomecal crashes on edit if VCClassProp is not in VEVENT record +* gnomecal,korganizer automagically add: +* CLASS:PUBLIC gnomecal need korganizer +* PRIORITY:0 gnomecal korganizer +* RELATED-TO:0 - korganizer +* SEQUENCE:0 gnomecal korganizer +* STATUS:NEEDS ACTION gnomecal korganizer +* TRANSP:0 gnomecal korganizer +* X-ORGANIZER:MAILTO:tsch@shs5.ind-hh korganizer +*/ + +/* convert IC35 Schedule record to vCal:vEvent +* ------------------------------------------- +*/ +static void +ic35sched_to_vevent( IC35REC * rec, VObject * vevent ) +{ + VObject * vprop; + uchar alarm_repeat; + char isodtime[20]; + char * notes; + + sprintf( isodtime, "%sT%s", /* yyyymmddThhmmss */ + ic35recfld( rec, S_StartDate ), ic35recfld( rec, S_StartTime ) ); + if ( isodtime[13] == ':' ) isodtime[13] = isodtime[14] = '0'; + SetString( vevent, VCDTstartProp, isodtime ); + sprintf( isodtime, "%sT%s", /* yyyymmddThhmmss */ + ic35recfld( rec, S_EndDate ), ic35recfld( rec, S_EndTime ) ); + if ( isodtime[13] == '<' ) isodtime[13] = isodtime[14] = '0'; + SetString( vevent, VCDTendProp, isodtime ); + + if ( _vca_prodid() == VCAL_GNOME ) { + if ( (notes = ic35recfld( rec, S_Notes )) && *notes ) { + char * subj = ic35recfld( rec, S_Subject ); + char * buff = dupStr( "", strlen(subj)+2+strlen(notes) ); + sprintf( buff, "%s\r\n%s", subj, notes ); + subj = dupSubstCRLF( buff ); + SetString( vevent, VCSummaryProp, subj ); + deleteStr( subj ); + deleteStr( buff ); + } else + SetString( vevent, VCSummaryProp, ic35recfld( rec, S_Subject ) ); + if ( ! isAPropertyOf( vevent, VCClassProp ) ) + SetString( vevent, VCClassProp, "PUBLIC" ); + } else { + SetString( vevent, VCSummaryProp, ic35recfld( rec, S_Subject ) ); + SetNotes( vevent, ic35recfld( rec, S_Notes ) ); + } + + alarm_repeat = *ic35recfld( rec, S_Alarm_Repeat ); + if ( alarm_repeat & RepeatMASK ) { + uchar repcount = *ic35recfld( rec, S_RepCount ); + char * repend = ic35recfld( rec, S_RepEndDate ); + char reprule[24]; + struct tm stm; + static const char * wdays[] = { + "SU", "MO", "TU", "WE", "TH", "FR", "SA", + }; + + memset( &stm, 0, sizeof(stm) ); + sscanf( ic35recfld( rec, S_StartDate ), "%4d%2d%2d", + &stm.tm_year, &stm.tm_mon, &stm.tm_mday ); + sscanf( ic35recfld( rec, S_StartTime ), "%2d%2d", + &stm.tm_hour, &stm.tm_min ); + stm.tm_year -= 1900; + stm.tm_mon -= 1; + stm.tm_sec = 0; + stm.tm_isdst = -1; + (void)mktime( &stm ); + repcount = *ic35recfld( rec, S_RepCount ); + repend = ic35recfld( rec, S_RepEndDate ); + *reprule = '\0'; + switch ( alarm_repeat & RepeatMASK ) { + case RepeatDay: + sprintf( reprule, "D%u %sT000000", repcount, repend ); + break; + case RepeatWeek: + sprintf( reprule, "W%u %s %sT000000", + repcount, wdays[stm.tm_wday], repend ); + break; + case RepeatMonWday: + sprintf( reprule, "MP%u %sT000000", repcount, repend ); + break; + case RepeatMonMday: + sprintf( reprule, "MD%u %sT000000", repcount, repend ); + break; + case RepeatYear: + sprintf( reprule, "YD%u %sT000000", repcount, repend ); + break; + } + SetString( vevent, VCRRuleProp, reprule ); + } else + DelProp( vevent, VCRRuleProp ); + if ( *ic35recfld( rec, S_AlarmBefore ) + && (alarm_repeat & (AlarmNoBeep|AlarmNoLED)) + != (AlarmNoBeep|AlarmNoLED) ) { + time_t atime; + struct tm * ptm; + struct tm atm; + char atimestr[16]; + + memset( &atm, 0, sizeof(atm) ); + sscanf( ic35recfld( rec, S_StartDate ), "%4d%2d%2d", + &atm.tm_year, &atm.tm_mon, &atm.tm_mday ); + sscanf( ic35recfld( rec, S_StartTime ), "%2d%2d", + &atm.tm_hour, &atm.tm_min ); + atm.tm_year -= 1900; + atm.tm_mon -= 1; + atm.tm_isdst = -1; + atime = mktime( &atm ); + switch ( *ic35recfld( rec, S_AlarmBefore ) ) { + case AlarmBefNone: + case AlarmBefNow: break; + case AlarmBef1min: atime -= 60; break; + case AlarmBef5min: atime -= 5 * 60; break; + case AlarmBef10min: atime -= 10 * 60; break; + case AlarmBef30min: atime -= 30 * 60; break; + case AlarmBef1hour: atime -= 60 * 60; break; + case AlarmBef2hour: atime -= 2 * 60 * 60; break; + case AlarmBef10hour: atime -= 10 * 60 * 60; break; + case AlarmBef1day: atime -= 24 * 60 * 60; break; + case AlarmBef2day: atime -= 2 * 24 * 60 * 60; break; + } + ptm = localtime( &atime ); + strftime( atimestr, sizeof(atimestr), "%Y%m%dT%H%M%S", ptm ); + if ( ! (alarm_repeat & AlarmNoBeep) && _vca_prodid() != VCAL_KDE ) { + vprop = SetProp( vevent, VCAAlarmProp ); + SetString( vprop, VCRunTimeProp, atimestr ); + SetString( vprop, VCSnoozeTimeProp, "" ); + SetString( vprop, VCRepeatCountProp, "1" ); + } else + DelProp( vevent, VCAAlarmProp ); + if ( ! (alarm_repeat & AlarmNoLED) + || (!(alarm_repeat & AlarmNoBeep) && _vca_prodid() == VCAL_KDE) ) { + vprop = SetProp( vevent, VCDAlarmProp ); + SetString( vprop, VCRunTimeProp, atimestr ); + SetString( vprop, VCSnoozeTimeProp, "" ); + SetString( vprop, VCRepeatCountProp, "1" ); + } else + DelProp( vevent, VCDAlarmProp ); + } else { + DelProp( vevent, VCAAlarmProp ); + DelProp( vevent, VCDAlarmProp ); + } +} + +/* convert IC35 schedule record to vCal +* ------------------------------------ +*/ +static void +vevent_to_ic35sched( VObject * vevent, IC35REC * rec ) +{ + VObject * vprop; + char * strval; + char * pstr; + char alarm_repeat[2]; + char repeat_count[2]; + char alarm_before[2]; + time_t abefsec; + + strval = StringValue( vevent, VCSummaryProp ); + if ( !( strval && *strval ) ) strval = "NoSubject"; /*IC35 needs subject*/ + if ( _vca_prodid() == VCAL_GNOME + && (pstr = strchr( strval, '\n' )) != NULL ) { + strval = dupStr( strval, pstr - strval ); /* first line */ + set_ic35recfld( rec, S_Subject, strval ); /* to Subject field */ + deleteStr( strval ); + pstr = dupSubstNL( pstr+1 ); /* subsequent lines */ + set_ic35recfld( rec, S_Notes, pstr ); /* to Notes field */ + deleteStr( pstr ); + } else { + set_ic35recfld( rec, S_Subject, strval ); + set_ic35recfld( rec, S_Notes, NotesValue( vevent ) ); + } + + set_ic35recfld( rec, S_StartDate, + isodtime_to_ymd_or_today( vevent, VCDTstartProp ) ); + set_ic35recfld( rec, S_StartTime, + isodtime_to_hms_or_now( vevent, VCDTstartProp ) ); + set_ic35recfld( rec, S_EndDate, + isodtime_to_ymd_or_today( vevent, VCDTendProp ) ); + set_ic35recfld( rec, S_EndTime, + isodtime_to_hms_or_now( vevent, VCDTendProp ) ); + + alarm_repeat[0] = 0; + alarm_repeat[1] = '\0'; + repeat_count[0] = 0; + repeat_count[1] = '\0'; + if ( (strval = StringValue( vevent, VCRRuleProp )) && *strval ) { + if ( strncasecmp( strval, "YD", 2 ) == 0 ) { + alarm_repeat[0] = (alarm_repeat[0] & RepeatMASK) | RepeatYear; + repeat_count[0] = (uchar)atoi( strval+2 ); + } else if ( strncasecmp( strval, "MD", 2 ) == 0 ) { + alarm_repeat[0] = (alarm_repeat[0] & RepeatMASK) | RepeatMonMday; + repeat_count[0] = (uchar)atoi( strval+2 ); + } else if ( strncasecmp( strval, "MP", 2 ) == 0 ) { + alarm_repeat[0] = (alarm_repeat[0] & RepeatMASK) | RepeatMonWday; + repeat_count[0] = (uchar)atoi( strval+2 ); + } else if ( strncasecmp( strval, "W", 1 ) == 0 ) { + alarm_repeat[0] = (alarm_repeat[0] & RepeatMASK) | RepeatWeek; + repeat_count[0] = (uchar)atoi( strval+1 ); + } else if ( strncasecmp( strval, "D", 1 ) == 0 ) { + alarm_repeat[0] = (alarm_repeat[0] & RepeatMASK) | RepeatDay; + repeat_count[0] = (uchar)atoi( strval+1 ); + } + if ( (pstr = strrchr( strval, ' ' )) != NULL + && strlen( pstr+1 ) >= 8 /* yyyymmdd */ + && isdigit( pstr[1] ) ) + set_ic35recfld( rec, S_RepEndDate, + isodtstr_to_ymd_or_today( pstr+1 ) ); + } + + alarm_repeat[0] |= (AlarmNoBeep|AlarmNoLED); + alarm_before[0] = AlarmBefNone; + alarm_before[1] = '\0'; + abefsec = 0; + if ( (vprop = isAPropertyOf( vevent, VCAAlarmProp )) != NULL ) { + alarm_repeat[0] &= ~AlarmNoBeep; + abefsec = isodtime_to_unixtime( vevent, VCDTstartProp ) + - isodtime_to_unixtime( vprop, VCRunTimeProp ); + } + if ( (vprop = isAPropertyOf( vevent, VCDAlarmProp )) != NULL ) { + alarm_repeat[0] &= ~AlarmNoLED; + if ( _vca_prodid() == VCAL_KDE ) /* korganizer has only DALARM */ + alarm_repeat[0] &= ~AlarmNoBeep; + abefsec = isodtime_to_unixtime( vevent, VCDTstartProp ) + - isodtime_to_unixtime( vprop, VCRunTimeProp ); + } + if ( (alarm_repeat[0] & (AlarmNoBeep|AlarmNoLED)) + != (AlarmNoBeep|AlarmNoLED) ) { + if ( abefsec < 60 ) alarm_before[0] = AlarmBefNow; + if ( abefsec >= 60 * 1 ) alarm_before[0] = AlarmBef1min; + if ( abefsec >= 60 * 5 ) alarm_before[0] = AlarmBef5min; + if ( abefsec >= 60 * 10 ) alarm_before[0] = AlarmBef10min; + if ( abefsec >= 60 * 30 ) alarm_before[0] = AlarmBef30min; + if ( abefsec >= 60 * 60 * 1 ) alarm_before[0] = AlarmBef1hour; + if ( abefsec >= 60 * 60 * 2 ) alarm_before[0] = AlarmBef2hour; + if ( abefsec >= 60 * 60 * 10 ) alarm_before[0] = AlarmBef10hour; + if ( abefsec >= 60 * 60 * 24 * 1 ) alarm_before[0] = AlarmBef1day; + if ( abefsec >= 60 * 60 * 24 * 2 ) alarm_before[0] = AlarmBef2day; + } + set_ic35recfld( rec, S_Alarm_Repeat, alarm_repeat ); + set_ic35recfld( rec, S_RepCount, repeat_count ); + set_ic35recfld( rec, S_AlarmBefore, alarm_before ); + + /* IC35 Schedule does not have Category */ +} + + +/* ==================================================== */ +/* IC35 ToDoList record to/from vCal:vTodo */ +/* ==================================================== */ +/* +* field mapping: +* VCSummaryProp T_Subject default: "ToDo" +* VCDTstartProp T_StartDate yyyymmdd default: today +* VCDueProp T_EndDate yyyymmdd default: today +* VCStatusProp T_Completed 1 byte default: 0x00 +* NEEDS ACTION 0x00 +* COMPLETED 0x01 +* other 0x00 "STATUS:\r\n" to T_Notes +* VCPriorityProp T_Priority 1 byte default: 0x01 +* 1,2 0x02 highest +* 3,4,0 0x01 normal +* 5.. 0x00 lowest +* VCDescriptionProp T_Notes default: +* -/- T_CategoryID default: 0x00 +* VCCategoriesProp T_Category default: "Unfiled" +* use first known standard from multiple (Business,Personal,Unfiled) +* if single use it, even if non-standard +* special for KOrganizer: +* VCDescriptionProp T_Notes +* "DTSTART:\r\n" T_StartDate +* "DUE:\r\n" T_EndDate +* "CATEGORIES:\r\n" T_Category +* KOrganizer does not support VTODO.DTSTART,DUE,CATEGORIES, +* as workaround these fields are put into VTODO.DESCRIPTION. +* special for gnomecal: +* VCDescriptionProp T_Notes +* "DTSTART:\r\n" T_StartDate +* gnomecal does not support VTODO.DTSTART, put into VTODO.DESCRIPTION. +* VCClassProp PUBLIC +* gnomecal crashes on edit if VCClassProp is not in VTODO record +* gnomecal,korganizer automagically add: +* CLASS:PUBLIC gnomecal need - +* SEQUENCE:0 gnomecal korganizer +* TRANSP:0 gnomecal - +* X-ORGANIZER:MAILTO:tsch@shs5.ind-hh korganizer +*/ + +/* convert IC35 ToDoList record to vCal:vTodo +* ------------------------------------------ +*/ +static void +ic35todo_to_vtodo( IC35REC * rec, VObject * vtodo ) +{ + char * prio; + char isodtime[20]; + + SetString( vtodo, VCSummaryProp, ic35recfld( rec, T_Subject ) ); + SetNotes( vtodo, ic35recfld( rec, T_Notes ) ); + + switch ( _vca_prodid() ) { + case VCAL_KDE: /* does not support DTSTART,DUE,CATEGORY */ + SetNoteProp( vtodo, VCDueProp, ic35recfld( rec, T_EndDate ) ); + SetNoteProp( vtodo, VCDTstartProp, ic35recfld( rec, T_StartDate ) ); + SetNoteProp( vtodo, VCCategoriesProp, ic35recfld( rec, T_Category ) ); + break; + case VCAL_GNOME: /* does not support DTSTART, needs CLASS */ + if ( ! isAPropertyOf( vtodo, VCClassProp ) ) + SetString( vtodo, VCClassProp, "PUBLIC" ); + SetNoteProp( vtodo, VCDTstartProp, ic35recfld( rec, T_StartDate ) ); + goto due_category; + default: + sprintf( isodtime, "%sT000000", ic35recfld( rec, T_StartDate ) ); + SetString( vtodo, VCDTstartProp, isodtime ); + due_category: + sprintf( isodtime, "%sT235900", ic35recfld( rec, T_EndDate ) ); + SetString( vtodo, VCDueProp, isodtime ); + SetCategory( vtodo, ic35recfld( rec, T_Category ) ); + } + + SetString( vtodo, VCStatusProp, *ic35recfld( rec, T_Completed ) == 0 + ? "NEEDS ACTION" : "COMPLETED" ); + + switch ( *ic35recfld( rec, T_Priority ) ) { + case 0x02: prio = "1"; break; /* highest */ + default: + case 0x01: prio = "3"; break; /* normal */ + case 0x00: prio = "5"; break; /* lowest */ + } + SetString( vtodo, VCPriorityProp, prio ); +} + +/* convert IC35 ToDoList record to vCal +* ------------------------------------ +*/ +static void +vtodo_to_ic35todo( VObject * vtodo, IC35REC * rec ) +{ + char *binstr, *strval; + int intval; + char * statusnote = NULL; + char * dtbeg; + char * dtend; + + strval = StringValue( vtodo, VCSummaryProp ); + if ( !( strval && *strval ) ) strval = "NoSubject"; /*IC35 needs subject*/ + set_ic35recfld( rec, T_Subject, strval ); + + switch ( _vca_prodid() ) { + case VCAL_KDE: /* does not support DTSTART,DUE,CATEGORY */ + dtbeg = dupStr( NotePropValue( vtodo, VCDTstartProp ), 0 ); + dtend = dupStr( NotePropValue( vtodo, VCDueProp ), 0 ); + set_ic35recfld( rec, T_Category, + NotePropValue( vtodo, VCCategoriesProp ) ); + break; + case VCAL_GNOME: /* does not support DTSTART */ + dtbeg = dupStr( NotePropValue( vtodo, VCDTstartProp ), 0 ); + goto due_category; + default: + dtbeg = dupStr( StringValue( vtodo, VCDTstartProp ), 0 ); + due_category: + dtend = dupStr( StringValue( vtodo, VCDueProp ), 0 ); + set_ic35recfld( rec, T_Category, + CategoryValue( vtodo ) ); + } + set_ic35recfld( rec, T_CategoryID, "\x00" ); + set_ic35recfld( rec, T_StartDate, isodtstr_to_ymd_or_today( dtbeg ) ); + set_ic35recfld( rec, T_EndDate, isodtstr_to_ymd_or_today( dtend ) ); + deleteStr( dtbeg ); + deleteStr( dtend ); + + binstr = "\x00"; + if ( (strval = StringValue( vtodo, VCStatusProp )) && *strval ) { + if ( strcasecmp( strval, "COMPLETED" ) == 0 ) + binstr = "\x01"; + else if ( strcasecmp( strval, "NEEDS ACTION" ) != 0 + && (statusnote = malloc( 255+1 )) != NULL ) { + strcpy( statusnote, "STATUS:" ); + strncat( strval, strval, 255-7-2 ); + strcat( strval, "\r\n" ); + } + } + set_ic35recfld( rec, T_Completed, binstr ); + + intval = atoi( StringValue( vtodo, VCPriorityProp ) ); + if ( intval >= 5 ) binstr = "\x00"; /* lowest */ + else if ( intval >= 3 ) binstr = "\x01"; /* normal */ + else if ( intval >= 1 ) binstr = "\x02"; /* highest */ + else /* intval == 0 */ binstr = "\x01"; /* default: normal */ + set_ic35recfld( rec, T_Priority, binstr ); + + if ( statusnote ) { + strval = NotesValue( vtodo ); + strncat( statusnote, strval, 255 - strlen( statusnote ) ); + set_ic35recfld( rec, T_Notes, statusnote ); + free( statusnote ); + } else + set_ic35recfld( rec, T_Notes, NotesValue( vtodo ) ); +} + + +/* ==================================================== */ +/* IC35 Memo record to/from vMemo (proprietary) */ +/* ==================================================== */ +/* +* field mapping: +* VCSummaryProp M_Subject default: "Memo" +* VCNoteProp M_Notes +* -/- M_CategoryID default: 0x00 +* VCCategoriesProp M_Category default: "Unfiled" +* use first known standard from multiple (Business,Personal,Unfiled) +* if single use it, even if non-standard +*/ + +/* convert IC35 Memo record to (proprietary) vMemo +* ----------------------------------------------- +*/ +static void +ic35memo_to_vmemo( IC35REC * rec, VObject * vmemo ) +{ + SetString( vmemo, VCSummaryProp, ic35recfld( rec, M_Subject ) ); + SetNotes( vmemo, ic35recfld( rec, M_Notes ) ); + SetCategory( vmemo, ic35recfld( rec, M_Category ) ); +} + +/* convert IC35 Memo record to (proprietary) vMemo +* ----------------------------------------------- +*/ +static void +vmemo_to_ic35memo( VObject * vmemo, IC35REC * rec ) +{ + char *strval; + + strval = StringValue( vmemo, VCSummaryProp ); + if ( !( strval && *strval ) ) strval = "NoSubject"; /*IC35 needs subject*/ + set_ic35recfld( rec, M_Subject, strval ); + + set_ic35recfld( rec, M_Notes, NotesValue( vmemo ) ); + + set_ic35recfld( rec, M_CategoryID, "\x00" ); + set_ic35recfld( rec, M_Category, CategoryValue( vmemo ) ); +} + + +/* ============================================ */ +/* vCard,vCal access functions */ +/* ============================================ */ +/* +* vca_open() +* vca_close() +* vca_rewind() +* +* vca_getrec( int fileid ) -> VObject* / NULL +* vca_getrec_byID( ulong recid ) -> VObject* / NULL +* vca_cmpic35rec( IC35REC*, VObject* ) -> 0:same 1:differ +* vca_updic35rec( IC35REC*, VObject* ) VObject to IC35REC +* vca_putic35rec( IC35REC* ) IC35REC to VObject +* vca_delrec( VObject* ) +* +* vca_recid( VObject* ) +* vca_set_recid( VObject*, ulong ) +* vca_recstat( VObject* ) +* vca_set_recstat( VObject*, int ) +*/ + +struct vcaget { /* current position in vcalist */ + VObject * vobj; /* current vCard,vCal,vMemo */ + bool in_vcal; /* flag: vcal_iterator in use */ + VObjectIterator vcal_iter; /* iterator within vCal object */ +}; + +static VObject * vcalist; /* list of vCard,vCal,vMemo objects */ +static VObject * vcal; /* vCalendar contains vEvent,vTodo */ +static struct vcaget vcaget; /* _vca_getrec position in vcalist */ + +static ulong vca_recid( VObject * vobj ); + +/* internal: determine vCalender program from PRODID +* ------------------------------------------------- +* inspect vCalender PRODID for creating program: +* PRODID:-//GNOME//NONSGML GnomeCalendar//EN +* PRODID:-//K Desktop Environment//NONSGML KOrganizer//EN +* some IC35 to/from vCalendar conversions are handled special +* depending on program. +*/ +static int +_vca_prodid( void ) +{ + char * prodid; + int rval; + + rval = 0; + if ( vcal != NULL + && (prodid = dupStringValue( vcal, VCProdIdProp )) != NULL ) { + if ( strstr( prodid, "GnomeCalendar//" ) != NULL ) + rval = VCAL_GNOME; + if ( strstr( prodid, "KOrganizer//" ) != NULL ) + rval = VCAL_KDE; + deleteStr( prodid ); + } + return rval; +} + +/* internal: rewind vCard,vCal list +* -------------------------------- +*/ +static void +_vca_rewind( struct vcaget * vcaget ) +{ + vcaget->vobj = NULL; + vcaget->in_vcal = FALSE; +} +/* internal: get next vCard,vCal record +* ------------------------------------ +*/ +static VObject * +_vca_getnext( struct vcaget * vcaget ) +{ + VObject * vcal; + + if ( vcaget == NULL ) + return NULL; + if ( vcaget->vobj == NULL ) + vcaget->vobj = vcalist; + else if ( ! vcaget->in_vcal ) + vcaget->vobj = nextVObjectInList( vcaget->vobj ); + while ( vcaget->vobj != NULL ) { + switch ( vca_type( vcaget->vobj ) ) { + case VCARD: + case VMEMO: + return vcaget->vobj; + case VCAL: + if ( ! vcaget->in_vcal ) { + initPropIterator( &vcaget->vcal_iter, vcaget->vobj ); + vcaget->in_vcal = TRUE; + } + while ( moreIteration( &vcaget->vcal_iter ) ) { + vcal = nextVObject( &vcaget->vcal_iter ); + switch ( vca_type( vcal ) ) { + case VEVENT: + case VTODO: + return vcal; + } + } + vcaget->in_vcal = FALSE; + break; + } + vcaget->vobj = nextVObjectInList( vcaget->vobj ); + } + return NULL; +} + +/* open vCard,vCal file(s) +* ----------------------- +* if file(s) exist, read into internal vObject-tree 'vcalist' +* complain if open file(s) failed. +* ??? check if output file creatable in dirname(filename) +* rewind so that _vca_getrec() gets first record in 'vcalist' +*/ +static char * iomode; +static char * addr_fname; +static char * vcal_fname; +static char * memo_fname; +static int addr_CRLF; /* CRLF / NL mode per PIMfile */ +static int vcal_CRLF; /* from Parse_MIME_FromFile() */ +static int memo_CRLF; /* calls in vca_open() */ + +/* mimeErrorHandler for Parse_MIME_FromFile() +*/ +static int _vca_errok; +static char * _vca_fname; + +static void +_vca_errmsg( char * msg ) +{ + error( "vCard/vCal \"%s\": %s", _vca_fname ? _vca_fname : "", msg ); + _vca_errok = ERR; +} + +/* check and read vCard/vCal from filepointer +*/ +static int +_vca_readfp( FILE * fp, char * fname, VObject ** pvlist, int * pcrlf ) +{ + int chr; + + LPRINTF(( L_INFO, "vca_open: read %s ..", fname )); + *pvlist = NULL; + *pcrlf = 1; + do { + if ( (chr = fgetc( fp )) == EOF ) /* empty file or only CR/LF */ + return OK; /* is accepted OK */ + } while ( chr == '\r' || chr == '\n' ); + rewind( fp ); + + registerMimeErrorHandler( _vca_errmsg ); + _vca_errok = OK; + _vca_fname = fname; + *pvlist = Parse_MIME_FromFile( fp ); + *pcrlf = CRLFmode(); /* CRLF/NL input from Parse_MIME */ + return _vca_errok; /* ok / error from _vca_errmsg() */ +} + +/* open,check+read,close vCard/vCal file +*/ +static int +_vca_readfile( char * fname, VObject ** pvlist, int * pcrlf ) +{ + FILE * fp; + int rval; + + *pvlist = NULL; + *pcrlf = 1; + if ( !( fname && *fname ) ) /* no filename specified */ + return OK; + + if ( (fp = fopen( fname, "r" )) == NULL ) { + if ( access( fname, F_OK ) == 0 ) + return ERR; /* file exists, but not readable */ + else + return OK; /* non-existing file is OK */ + } + rval = _vca_readfp( fp, fname, pvlist, pcrlf ); + fclose( fp ); + return rval; +} + +/* read-in vCard,vCal file(s) +*/ +static int +vca_open( char * mode, char * addrfname, char * vcalfname, char * memofname ) +{ + VObject * vlist; + VObject * vobj; + int rval; + + LPRINTF(( L_INFO, "vca_open(%s,%s,%s)", + addrfname ? addrfname : "NULL", + vcalfname ? addrfname : "NULL", + vcalfname ? addrfname : "NULL" )); + iomode = mode; /* note input/output for close */ + addr_fname = addrfname; /* note filenames for close */ + vcal_fname = vcalfname; + memo_fname = memofname; + vcalist = vcal = NULL; /* init vCard,vCal record lists */ + addr_CRLF = vcal_CRLF = memo_CRLF = 1; /* init CRLF mode default ON */ + if ( iomode[0] != 'r' ) + return OK; /* open for output only */ + + if ( addrfname && strcmp( addrfname, "-" ) == 0 /* using stdin */ + && !( vcalfname && *vcalfname ) /* is allowed if */ + && !( memofname && *memofname ) ) /* only one filename */ + rval = _vca_readfp( stdin, "(stdin)", &vcalist, &addr_CRLF ); + else + rval = _vca_readfile( addrfname, &vcalist, &addr_CRLF ); + if ( rval != OK ) /* open/parse vCard file failed */ + return ERR; + + rval = _vca_readfile( vcalfname, &vcal, &vcal_CRLF ); + if ( rval != OK ) /* open/parse vCal file failed */ + return ERR; + if ( vcal != NULL ) /* vCal object parsed from file */ + addList( &vcalist, vcal ); /* add vCal object to vcalist */ + else /* vCal object in addr file */ + for ( vobj = vcalist; vobj != NULL; vobj = nextVObjectInList( vobj ) ) + if ( vca_type( vobj ) == VCAL ) { + vcal = vobj; /* setup pointer to vCal obj */ + break; + } + + rval = _vca_readfile( memofname, &vlist, &memo_CRLF ); + if ( rval != OK ) /* open/parse VMEMO file failed */ + return ERR; + while ( vlist != NULL ) { + vobj = vlist; + vlist = nextVObjectInList( vlist ); + addList( &vcalist, vobj ); /* add memo records to vcalist */ + } + + _vca_rewind( &vcaget ); /* rewind for _vca_getrec() get first record */ + LPRINTF(( L_INFO, "vca_open: done" )); + return OK; +} + +/* close vCard,vCal file(s) +* ------------------------ +* flush internal vObject-tree to vCard,vCal file(s) +* if file(s) exist, rename file(s) for backup +* static filename(s) were setup by pim_open() +*/ +static void +vca_close( void ) +{ + FILE * addrfp = NULL; + FILE * vcalfp = NULL; + FILE * memofp = NULL; + VObject * vobj; + + if ( iomode[0] == 'w' || iomode[1] == '+' ) { + addrfp = backup_and_openwr( addr_fname ); + vcalfp = backup_and_openwr( vcal_fname ); + if ( vcalfp == NULL ) { vcalfp = addrfp; vcal_CRLF = addr_CRLF; } + memofp = backup_and_openwr( memo_fname ); + if ( memofp == NULL ) { memofp = vcalfp; memo_CRLF = memo_CRLF; } + } + LPRINTF(( L_INFO, "vca_close: %s", + addrfp && vcalist ? "write .." : "no output" )); + for ( vobj = vcalist; vobj != NULL; vobj = nextVObjectInList( vobj ) ) { + FILE * fp = NULL; + int crlf = 0; + + switch ( vca_type( vobj ) ) { + case VCARD: fp = addrfp; crlf = addr_CRLF; break; + case VCAL: fp = vcalfp; crlf = vcal_CRLF; break; + case VMEMO: fp = memofp; crlf = memo_CRLF; break; + } + if ( fp != NULL ) { + setCRLFmode( crlf ); + writeVObject( fp, vobj ); + } + } + cleanVObjects( vcalist ); + vcalist = vcal = NULL; /* all vCard,vCal,vMemo objects were removed */ + _vca_rewind( &vcaget ); /* sanity for vca_getrec() */ + + if ( addrfp != NULL && addrfp != stdout ) + fclose( addrfp ); + if ( vcalfp && vcalfp != addrfp && vcalfp != stdout ) + fclose( vcalfp ); + if ( memofp && memofp != vcalfp && memofp != stdout ) + fclose( memofp ); + LPRINTF(( L_INFO, "vca_close: done" )); +} + +/* rewind vCard,vCal list +* ---------------------- +*/ +static void +vca_rewind( void ) +{ + _vca_rewind( &vcaget ); +} + +/* get vCard,vCal record for fileid +* -------------------------------- +* fileid specifies which type of record to get: +* FILEADDR VCARD +* FILESCHED VCAL:VEVENT +* FILETODO VCAL:VTODO +* FILEMEMO VMEMO +* returns: +* VObject* next record +* NULL no more records +*/ +static VObject * +vca_getrec( int fileid ) +{ + VObject * vobj; + int wanttype; + + switch ( fileid ) { + case FILEADDR: wanttype = VCARD; break; + case FILESCHED: wanttype = VEVENT; break; + case FILETODO: wanttype = VTODO; break; + case FILEMEMO: wanttype = VMEMO; break; + case FILE_ANY: wanttype = 0; break; + default: + return NULL; /* unknown fileid */ + } + while ( (vobj = _vca_getnext( &vcaget )) ) { + if ( wanttype == 0 /* next record with any fileid */ + || vca_type( vobj ) == wanttype ) + return vobj; + } + return NULL; +} +/* get vCard,vCal record by record-ID +* ---------------------------------- +*/ +static VObject * +vca_getrec_byID( ulong recid ) +{ + VObject * vobj; + struct vcaget tmpget; + + _vca_rewind( &tmpget ); + while ( (vobj = _vca_getnext( &tmpget )) ) + if ( vca_recid( vobj ) == recid ) + return vobj; + return NULL; +} + +/* update IC35 record with vCard,vCal record +* ----------------------------------------- +*/ +static IC35REC * +vca_updic35rec( IC35REC * ic35rec, VObject * vobj ) +{ + int fid; + void (* vobj_to_ic35 )(VObject*,IC35REC*); + + switch ( vca_type( vobj ) ) { + case VCARD: vobj_to_ic35 = vcard_to_ic35addr; fid = FILEADDR; break; + case VEVENT: vobj_to_ic35 = vevent_to_ic35sched; fid = FILESCHED; break; + case VTODO: vobj_to_ic35 = vtodo_to_ic35todo; fid = FILETODO; break; + case VMEMO: vobj_to_ic35 = vmemo_to_ic35memo; fid = FILEMEMO; break; + default: + return NULL; + } + if ( ic35rec == NULL + && (ic35rec = new_ic35rec()) == NULL ) + return NULL; + /* must set fileid,recid first to specify number of fields */ + set_ic35recid( ic35rec, FileRecId( fid, RecId(vca_recid( vobj )) ) ); + (*vobj_to_ic35)( vobj, ic35rec ); + return ic35rec; +} + +/* compare IC35 record with vCard,vCal record +* ------------------------------------------ +*/ +static int +vca_cmpic35rec( IC35REC * ic35rec, VObject * vobj ) +{ + IC35REC * pimic35; + int rval; + + if ( ic35rec == NULL && vobj == NULL ) + return 0; + if ( ic35rec == NULL || vobj == NULL ) + return -1; + + if ( (pimic35 = new_ic35rec()) == NULL ) + return -1; + vca_updic35rec( pimic35, vobj ); + rval = cmp_ic35rec( ic35rec, pimic35 ); + del_ic35rec( pimic35 ); + return rval; +} + +/* put IC35 record to (new) vCard,vCal record +* ------------------------------------------ +*/ +static VObject * +vca_putic35rec( IC35REC * ic35rec ) +{ + VObject * oldvobj; + VObject * vobj = NULL; + VObject * locvcal; + const char *id; + void (* ic35_to_vobj )(IC35REC*,VObject*); + + switch ( FileId( ic35recid( ic35rec ) ) ) { + case FILEADDR: ic35_to_vobj = ic35addr_to_vcard; id = VCCardProp; break; + case FILEMEMO: ic35_to_vobj = ic35memo_to_vmemo; id = VCMemoProp; break; + case FILESCHED: ic35_to_vobj = ic35sched_to_vevent; id = VCEventProp; break; + case FILETODO: ic35_to_vobj = ic35todo_to_vtodo; id = VCTodoProp; break; + default: + return NULL; + } + if ( (vobj = oldvobj = vca_getrec_byID( ic35recid( ic35rec ) )) == NULL + && (vobj = newVObject( id )) == NULL ) + return NULL; + clr_vobjdirty(); + (*ic35_to_vobj)( ic35rec, vobj ); + SetModtimeIfdirty( vobj ); + vca_set_recid( vobj, ic35recid( ic35rec ) ); + vca_set_recstat( vobj, VCA_CLEAN ); + if ( oldvobj != NULL ) + return vobj; + + switch ( vca_type( vobj ) ) { + case VCARD: + case VMEMO: + addList( &vcalist, vobj ); + break; + case VEVENT: + case VTODO: + /* create vCalendar header of not yet present */ + if ( (locvcal = vcal) == NULL ) + locvcal = newVObject( VCCalProp ); + if ( isAPropertyOf( locvcal, VCProdIdProp ) == NULL ) + addPropValue( locvcal, VCProdIdProp, + "-//IC35//NONSGML IC35Calendar//EN" ); + /* -//GNOME//NONSGML GnomeCalendar//EN */ + /* -//K Desktop Environment//NONSGML KOrganizer//EN */ + if ( isAPropertyOf( locvcal, VCVersionProp ) == NULL ) + addPropValue( locvcal, VCVersionProp, "1.0" ); + if ( isAPropertyOf( locvcal, VCTimeZoneProp ) == NULL + || isAPropertyOf( locvcal, VCDCreatedProp ) == NULL ) { + extern long timezone; + time_t tnow; + struct tm * ptm; + char isodtime[24]; + char tzoffset[8]; + long tzmineastUTC; + tnow = time( NULL ); ptm = localtime( &tnow ); + tzmineastUTC = -timezone / 60; + strftime( isodtime, sizeof(isodtime), "%Y-%m-%dT%T", ptm ); + sprintf( tzoffset, "%+03ld:%02ld", + tzmineastUTC/60, tzmineastUTC%60 ); + if ( isAPropertyOf( locvcal, VCTimeZoneProp ) == NULL ) + addPropValue( locvcal, VCTimeZoneProp, tzoffset ); + if ( isAPropertyOf( locvcal, VCDCreatedProp ) == NULL ) + addPropValue( locvcal, VCDCreatedProp, isodtime ); + } + if ( vcal == NULL ) + addList( &vcalist, vcal = locvcal ); + addVObjectProp( vcal, vobj ); + break; + } + return vobj; +} + +/* delete vCard,vCal record +* ------------------------ +*/ +static void +vca_delrec( VObject * vobj ) +{ + switch ( vca_type( vobj ) ) { + case VCARD: + case VMEMO: + cleanVObject( delList( &vcalist, vobj ) ); + break; + case VEVENT: + case VTODO: + cleanVObject( delProp( vcal, vobj ) ); + break; + } +} + +/* get,set record-ID +* ----------------- +*/ +static ulong +vca_recid( VObject * vobj ) +{ + long recid; + + if ( (recid = LongValue( vobj, XPilotIdProp )) == -1 ) + return 0; + return (ulong)recid; +} +static void +vca_set_recid( VObject * vobj, ulong recid ) +{ + char * IC35id = "IC35id"; + char * ouid; + char nuid[7+8+1]; + + ouid = StringValue( vobj, VCUniqueStringProp ); + if ( !( ouid && *ouid ) /* not present yet */ + || strncmp( ouid, IC35id, strlen( IC35id ) ) == 0 ) { /* or IC35 recID */ + sprintf( nuid, "IC35id-%08lX", recid ); + SetString( vobj, VCUniqueStringProp, nuid ); /* korganizer wants UID */ + } + SetLong( vobj, XPilotIdProp, recid ); +} +/* get,set record-changeflag +* ------------------------- +* gnomecard (from gnome-pim-1.2.0) does not maintain X-PILOTSTAT ! +* as workaround the last-revised-time VCARD.REV is compared against +* the previous time of sync with IC35 'oldic35dt()' to obtain the +* record-status CLEAN or DIRTY. +*/ +static int +vca_recstat( VObject * vobj ) +{ + long stat; + + if ( (stat = LongValue( vobj, XPilotStatusProp )) == -1 ) + return VCA_DIRTY; + if ( stat <= 0 + && vca_type( vobj ) == VCARD + && oldic35dt() != 0 + && oldic35dt() < isodtime_to_unixtime( vobj, VCLastRevisedProp ) ) + return VCA_DIRTY; + return (int)stat; +} +static void +vca_set_recstat( VObject * vobj, int stat ) +{ + SetLong( vobj, XPilotStatusProp, (long)stat ); +} + +/* vCard,vCal format operations +* ---------------------------- +*/ +struct pim_oper vca_oper = { + vca_open, + vca_close, + vca_rewind, + (void*(*)(int))vca_getrec, + (void*(*)(ulong))vca_getrec_byID, + (int(*)(IC35REC*,void*))vca_cmpic35rec, + (IC35REC*(*)(IC35REC*,void*))vca_updic35rec, + (void*(*)(IC35REC*))vca_putic35rec, + (void(*)(void*))vca_delrec, + (ulong(*)(void*))vca_recid, + (void(*)(void*,ulong))vca_set_recid, + (int(*)(void*))vca_recstat, + (void(*)(void*,int))vca_set_recstat, + }; + diff --git a/src/genproto.c b/src/genproto.c new file mode 100644 index 0000000..9dcecf1 --- /dev/null +++ b/src/genproto.c @@ -0,0 +1,199 @@ +/************************************************************************ +* Copyright (C) 2001 Thomas Schulz * +* */ + static char rcsid[] = + "$Id: genproto.c,v 1.2 2001/01/21 19:54:52 tsch Rel $"; /* +* * +* general IC35 protocol support * +* * +************************************************************************* +* * +* ??? is "fixme" mark: sections of code needing fixes * +* * +* utilities for PDU encode/decode * +* putbyte encode one byte * +* putword encode 2byte-word * +* putdword encode 4byte-doubleword * +* putbtxt encode binary text * +* puttext encode text string * +* puttxt0 encode text string plus trailing NUL-byte * +* getbyte decode one byte * +* getword decode 2byte-word * +* getdword decode 4byte-doubleword * +* getbtxt decode binary text * +* gettext decode text string * +* chksum calculate arithmetic checksum * +* general communication * +* welcome initial welcome handshake * +* * +************************************************************************/ + +#include /* memcpy(), .. */ +#include /* usleep() */ +#include /* SIG_INT, signal() */ + +#include "util.h" /* ERR,OK, uchar, .. */ +#include "comio.h" /* com_send(), .. */ +#include "genproto.h" +NOTUSED(rcsid) + + +/* ============================================ */ +/* utilities for PDU encode/decode */ +/* ============================================ */ + +/* encode data items to PDU +* ------------------------ +*/ +void +putbyte( uchar * pduptr, uchar byte ) +{ + *pduptr = byte; +} +void +putword( uchar * pduptr, ushort word ) +{ + putbyte( pduptr+0, word & 0x00FF ); /* LSB first .. */ + putbyte( pduptr+1, (word & 0xFF00) >> 8 ); /* .. then MSB */ +} +void +putdword( uchar * pduptr, ulong dword ) +{ + putword( pduptr+0, dword & 0x0000FFFF ); + putword( pduptr+2, (dword & 0xFFFF0000) >> 16 ); +} +void +putbtxt( uchar * pduptr, uchar * text, size_t tlen ) +{ + memcpy( pduptr, text, tlen ); +} +void +puttext( uchar * pduptr, char * text ) +{ + putbtxt( pduptr, text, strlen( text ) ); +} +void +puttxt0( uchar * pduptr, char * text ) +{ + size_t len; + + putbtxt( pduptr, text, len = strlen( text ) ); + pduptr[len] = '\0'; +} + +/* decode data items from PDU +* -------------------------- +*/ +uchar +getbyte( uchar * pduptr ) +{ + return *pduptr; +} +ushort +getword( uchar * pduptr ) +{ + return (getbyte( pduptr+1 ) << 8) | getbyte( pduptr+0 ); +} +ulong +getdword( uchar * pduptr ) +{ + return (getword( pduptr+2 ) << 16) | getword( pduptr+0 ); +} +void +getbtxt( uchar * pduptr, uchar * text, size_t tlen ) +{ + memcpy( text, pduptr, tlen ); +} +void +gettext( uchar * pduptr, char * text, size_t tlen ) +{ + getbtxt( pduptr, text, tlen ); + text[tlen] = '\0'; +} + +/* calculate arithmetic checksum +* ----------------------------- +*/ +ushort +chksum( uchar * data, size_t dlen ) +{ + ushort checksum; + size_t i; + + for ( checksum = 0x0000, i = 0; i < dlen; ++i ) + checksum += data[i]; + return checksum; +} + + +/* ==================================== */ +/* general communication */ +/* ==================================== */ + +/* welcome handshake +* ----------------- +* send 'cmd' every 1.15 sec until "WELCOME" received +* send 'cmd', wait for '\x80' +* command byte 'cmd' is for: +* - synchronize protocol '\x41'='A' +* - manager protocol '\x40'='@' +*/ +#define WELCOME_TIMEOUT 1150 +#define WELCOME_RETRY 20 + +#define RSP_READY (uchar)0x80 +#define GOT_NONE 0 +#define GOT_WELCOME 1 +#define GOT_READY 2 + +static int hadsignal; + +static void +_sig_hdlr( int signum ) +{ + hadsignal = signum; +} + +int +welcome( uchar cmd ) +{ + char * welcome = "WELCOME"; + int old_tmo; + int retry, rlen; + int state; + uchar rbuff[7]; + + hadsignal = 0; + signal( SIGINT, _sig_hdlr ); + old_tmo = com_settimeout( WELCOME_TIMEOUT ); + for ( retry = 0, state = GOT_NONE; + retry < WELCOME_RETRY && state != GOT_READY && hadsignal == 0; + ++retry ) { + com_send( &cmd, 1 ); + switch ( state ) { + case GOT_NONE: + rlen = com_recv( rbuff, sizeof(rbuff) ); + if ( rlen < strlen(welcome) /* receive error / timeout */ + || memcmp( rbuff, welcome, strlen(welcome) ) != 0 ) + continue; /* not enough or miss "WELCOME" */ + state = GOT_WELCOME; + continue; + case GOT_WELCOME: + rlen = com_recv( rbuff, 1 ); + if ( rlen < 1 /* receive error / timeout */ + || rbuff[0] != RSP_READY ) + continue; /* missing RSP_READY */ + state = GOT_READY; + break; + } + break; + } + signal( SIGINT, SIG_DFL ); + com_settimeout( old_tmo ); + if ( hadsignal ) + return ERR_intr; + if ( state != GOT_READY ) + return ERR; + usleep( 50000 ); /* give IC35 50ms to get ready */ + return OK; +} diff --git a/src/genproto.h b/src/genproto.h new file mode 100644 index 0000000..ca079fc --- /dev/null +++ b/src/genproto.h @@ -0,0 +1,39 @@ +/************************************************************************ +* Copyright (C) 2001 Thomas Schulz * +* * +* $Id: genproto.h,v 1.2 2001/01/21 23:38:37 tsch Rel $ * +* * +* header for general IC35 protocol support * +* * +************************************************************************/ +#ifndef _GENPROTO_H +#define _GENPROTO_H 1 + +#include "util.h" /* ushort, .. */ + + +/* communication error codes */ +#define ERR_intr -2 /* user interrupt with Ctl-C */ +#define ERR_recv -3 /* receive error */ +#define ERR_chksum -4 /* block checksum mismatch */ +#define ERR_acknak -5 /* ack/nak handshake failed */ + + +void putbyte( uchar * pduptr, uchar byte ); +void putword( uchar * pduptr, ushort word ); +void putdword( uchar * pduptr, ulong dword ); +void putbtxt( uchar * pduptr, uchar * text, size_t tlen ); +void puttext( uchar * pduptr, char * text ); +void puttxt0( uchar * pduptr, char * text ); + +uchar getbyte( uchar * pduptr ); +ushort getword( uchar * pduptr ); +ulong getdword( uchar * pduptr ); +void getbtxt( uchar * pduptr, uchar * text, size_t tlen ); +void gettext( uchar * pduptr, char * text, size_t tlen ); + +ushort chksum( uchar * data, size_t dlen ); + +int welcome( uchar cmd ); + +#endif /*_GENPROTO_H*/ diff --git a/src/ic35frec.c b/src/ic35frec.c new file mode 100644 index 0000000..f7422ab --- /dev/null +++ b/src/ic35frec.c @@ -0,0 +1,384 @@ +/************************************************************************ +* Copyright (C) 2000 Thomas Schulz * +* */ + static char rcsid[] = + "$Id: ic35frec.c,v 1.8 2001/03/02 02:09:59 tsch Rel $"; /* +* * +* IC35 record access * +* * +************************************************************************* +* * +* ??? is "fixme" mark: sections of code needing fixes * +* * +************************************************************************/ + +#include /* calloc(), .. */ +#include /* strcmp(), .. */ + +#include "util.h" /* ERR,OK, uchar, .. */ +#include "ic35frec.h" +NOTUSED(rcsid); + + +struct ic35file { /* IC35 file description */ + int id; /* id on IC35 */ + int nfld; /* number of fields in record */ + char * name; /* filename */ +}; +struct ic35rec { /* internal IC35 record for field access */ + ulong id; /* record-id on IC35: recid[3],fileid[1]*/ + uchar chg; /* change-flag on IC35 */ + size_t dlen; /* length of IC35 record data buffer */ + uchar * data; /* record data buffer: flens, fdata */ + int nflds; /* number of fields in record */ + struct { + size_t len; /* length of field */ + uchar * ptr; /* pointer to field data */ + } flds[MAXFLDS]; /* vector of record fields */ + size_t fblen; /* length of field data buffer */ + uchar * fbuff; /* field data buffer */ +}; + + +/* lookup IC35 fileid +* ------------------ +*/ +static struct ic35file ic35files[] = { + { FILEADDR, 21, "Addresses" }, + { FILEMEMO, 4, "Memo" }, + { FILESCHED, 10, "Schedule" }, + { FILETODO, 8, "To Do List" }, + { 0, 0, NULL } +}; + +static struct ic35file * +_ic35fdesc( int fileid ) +{ + struct ic35file * pfile; + + for ( pfile = ic35files; pfile->name != NULL; ++pfile ) + if ( pfile->id == fileid ) + return pfile; + return NULL; +} + +/* get number of record fields acc.to file-id +* ------------------------------------------ +*/ +int +ic35fnflds( int fileid ) +{ + struct ic35file * pfile; + + pfile = _ic35fdesc( fileid ); + if ( pfile == NULL ) /* unknown file id */ + return 0; + return pfile->nfld; +} +static int +_nrecflds( int fldid ) +{ + int nflds; + + if ( (nflds = ic35fnflds( FldFile(fldid) )) == 0 /* unkwown file id */ + || FldIdx(fldid) >= nflds /* bad field index */ + || FldIdx(fldid) >= alenof(((IC35REC*)0)->flds) ) + return 0; + return nflds; +} + +/* get filename acc.to file-id +* --------------------------- +*/ +char * +ic35fname( int fileid ) +{ + struct ic35file * pfile; + + pfile = _ic35fdesc( fileid ); + if ( pfile == NULL ) /* unknown file id */ + return NULL; + return pfile->name; +} + + +/* constructur: create new IC35 record +* ----------------------------------- +*/ +IC35REC * +new_ic35rec( void ) +{ + return calloc( 1, sizeof(IC35REC) ); +} + +/* destructor: delete IC35 record +* ------------------------------ +*/ +static void +_del_ic35recbuffs( IC35REC * rec ) +{ + int fi; + + for ( fi = 0; fi < alenof(rec->flds); ++fi ) { + if ( !( rec->fbuff <= rec->flds[fi].ptr + && rec->flds[fi].ptr < rec->fbuff+rec->fblen ) ) + free( rec->flds[fi].ptr ); + rec->flds[fi].ptr = NULL; + rec->flds[fi].len = 0; + } + rec->nflds = 0; + if ( rec->fbuff != NULL ) { + free( rec->fbuff ); + rec->fbuff = NULL; + rec->fblen = 0; + } + if ( rec->data != NULL ) { + free( rec->data ); + rec->data = NULL; + rec->dlen = 0; + } +} +void +del_ic35rec( IC35REC * rec ) +{ + _del_ic35recbuffs( rec ); + free( rec ); +} + +/* set,get special field in IC35 record +* ------------------------------------ +*/ +void +set_ic35recid( IC35REC * rec, ulong recid ) +{ + int nflds; + + rec->id = recid; + if ( (nflds = ic35fnflds( FileId(recid) )) != 0 ) + rec->nflds = nflds; +} +ulong +ic35recid( IC35REC * rec ) +{ + return rec->id; +} +void +set_ic35recchg( IC35REC * rec, uchar chgflag ) +{ + rec->chg = chgflag; +} +uchar +ic35recchg( IC35REC * rec ) +{ + return rec->chg; +} + +/* set,get IC35 record raw data +* ---------------------------- +*/ +void +set_ic35recdata( IC35REC * rec, uchar * data, size_t dlen ) +{ + uchar * fptr; + uchar * fbptr; + int fi; + size_t nflds, flensum; + + _del_ic35recbuffs( rec ); + if ( data == NULL || dlen == 0 ) + return; + if ( (rec->data = malloc( dlen )) == NULL ) + return; + memcpy( rec->data, data, rec->dlen = dlen ); + memset( rec->flds, 0, sizeof(rec->flds) ); + for ( nflds = flensum = 0; nflds < alenof(rec->flds); ++nflds ) { + flensum += rec->data[nflds] + 1; + if ( flensum > dlen ) + break; + } + if ( (rec->fbuff = malloc( dlen )) == NULL ) + return; + rec->fblen = dlen; + rec->nflds = nflds; + fbptr = rec->fbuff; + fptr = rec->data + nflds; + for ( fi = 0; fi < nflds; ++fi ) { + rec->flds[fi].len = rec->data[fi]; + rec->flds[fi].ptr = fbptr; + memcpy( rec->flds[fi].ptr, fptr, rec->flds[fi].len ); + fbptr += rec->flds[fi].len; + *fbptr++ = '\0'; /* append end-of-string */ + fptr += rec->data[fi]; + } +} +void +get_ic35recdata( IC35REC * rec, uchar ** pdata, size_t * pdlen ) +{ + uchar * fptr; + int fi; + size_t dlen; + + if ( rec->data == NULL ) { /* rebuild record data */ + for ( fi = 0, dlen = 0; fi < rec->nflds; ++fi ) + dlen += rec->flds[fi].len + 1; + if ( (rec->data = malloc( dlen )) != NULL ) { + rec->dlen = dlen; + fptr = rec->data + rec->nflds; + for ( fi = 0; fi < rec->nflds; ++fi ) { + if ( rec->flds[fi].ptr != NULL ) + memcpy( fptr, rec->flds[fi].ptr, rec->flds[fi].len ); + else + rec->flds[fi].len = 0; + fptr += (rec->data[fi] = rec->flds[fi].len); + } + } + } + if ( pdata ) + *pdata = rec->data; + if ( pdlen ) + *pdlen = rec->dlen; +} + +/* set,get string field from IC35 record +* ------------------------------------- +*/ +void +set_ic35recfld( IC35REC * rec, int fldid, char * data ) +{ + int fi, nflds; + size_t flen; + char * fbuff; + + if ( (nflds = _nrecflds( fldid )) <= 0 ) + return; /* bad field id */ + if ( rec->nflds == 0 ) + rec->nflds = nflds; + if ( (fi = FldIdx(fldid)) >= rec->nflds ) + return; + if ( data == NULL ) + data = ""; + switch ( fldid ) { + case A_CategoryID: + case S_AlarmBefore: + case S_Alarm_Repeat: + case S_RepCount: + case T_Completed: + case T_Priority: + case T_CategoryID: + case M_CategoryID: /* 1-byte binary fields */ + flen = 1; + break; + default: + flen = strlen( data ); + } + if ( flen > 255 ) flen = 255; /* truncate to max.field length */ + if ( flen > rec->flds[fi].len ) { /* new field bigger old content */ + if ( (fbuff = malloc( flen + 1 )) == NULL ) + return; /* no memory */ + if ( !( rec->fbuff <= rec->flds[fi].ptr + && rec->flds[fi].ptr < rec->fbuff+rec->fblen ) ) + free( rec->flds[fi].ptr ); /* old field outside buffer */ + rec->flds[fi].ptr = fbuff; + } + if ( flen ) { + memcpy( rec->flds[fi].ptr, data, flen ); + rec->flds[fi].ptr[flen] = '\0'; + } + rec->flds[fi].len = flen; + if ( rec->data != NULL ) { + free( rec->data ); + rec->data = NULL; /* record data must be rebuilt */ + rec->dlen = 0; + } +} +void +get_ic35recfld( IC35REC * rec, int fldid, uchar ** pfld, size_t * plen ) +{ + if ( _nrecflds( fldid ) <= 0 /* bad field id */ + || rec->flds[FldIdx(fldid)].ptr == NULL ) { /* empty field */ + if ( pfld ) *pfld = ""; + if ( plen ) *plen = 0; + } else { + if ( pfld ) *pfld = rec->flds[FldIdx(fldid)].ptr; + if ( plen ) *plen = rec->flds[FldIdx(fldid)].len; + } +} +char * +ic35recfld( IC35REC * rec, int fldid ) +{ + uchar * fld; + + get_ic35recfld( rec, fldid, &fld, NULL ); + return fld; +} + +/* compare 2 IC35 records +* ---------------------- +* records are compared field by field, because some fields need +* special handling, i.e. difference ignored. +* returns +* 0 both IC35 records are (regarded) same +* 1 IC35 records differ +* -1 IC35 records differ strangely: +* either one is NULL or fileids mismatch +*/ +int +cmp_ic35rec( IC35REC * rec1, IC35REC * rec2 ) +{ + int fileid; + int fi, nflds, fldid; + uchar * ic35fld; + uchar * binfld; + char ic35tfld[6+1]; + + if ( rec1 == NULL && rec2 == NULL ) + return 0; + if ( rec1 == NULL || rec2 == NULL ) + return -1; + if ( (fileid = FileId( ic35recid( rec1 ) )) + != FileId( ic35recid( rec2 ) ) ) + return -1; + + nflds = ic35fnflds( fileid ); + for ( fi = 0; fi < nflds; ++fi ) { + fldid = FILEfld(fileid,fi); + ic35fld = ic35recfld( rec1, fldid ); + binfld = ic35recfld( rec2, fldid ); + if ( strcmp( binfld, ic35fld ) == 0 ) + continue; + switch ( fldid ) { + case A_CategoryID: + case M_CategoryID: + case T_CategoryID: + continue; /* ignore category-ID difference */ + case S_StartTime: + case S_EndTime: + ic35fld = strncat( strcpy( ic35tfld, "" ), + ic35fld, sizeof(ic35tfld)-1 ); + if ( ( fldid == S_StartTime && ic35fld[4] == ':' ) + || ( fldid == S_EndTime && ic35fld[4] == '<' ) ) + ic35fld[4] = ic35fld[5] = '0'; + if ( strcmp( ic35fld, binfld ) == 0 ) + continue; /* ignore strange IC35 Start/EndTime */ + break; + case S_RepEndDate: + case S_RepCount: + if ( (*ic35recfld( rec2, S_Alarm_Repeat ) & RepeatMASK) == 0 ) + continue; /* ignore difference if not repeat */ + break; + case S_Alarm_Repeat: + if ( ((*binfld ^ *ic35fld) & ~(AlarmNoLED|AlarmNoBeep)) == 0 + && *ic35recfld( rec2, S_AlarmBefore ) == AlarmBefNone ) + continue; /* ignore LED,Beep diff if no alarm */ + break; + } + LPRINTF(( L_DEBUG, "cmp_ic35rec: rid=%08lX fi=%d diff", + ic35recid(rec1), fi )); + LDUMP(( L_DEBUG, binfld, strlen(binfld), + "cmp_ic35rec: rec2 len=%d", strlen(binfld) )); + LDUMP(( L_DEBUG, ic35fld, strlen(ic35fld), + "cmp_ic35rec: rec1 len=%d", strlen(ic35fld) )); + return 1; + } + return 0; +} diff --git a/src/ic35frec.h b/src/ic35frec.h new file mode 100644 index 0000000..49c40c4 --- /dev/null +++ b/src/ic35frec.h @@ -0,0 +1,138 @@ +/************************************************************************ +* Copyright (C) 2000 Thomas Schulz * +* * +* $Id: ic35frec.h,v 1.5 2000/12/23 01:09:26 tsch Rel $ * +* * +* header for IC35 record access * +* * +************************************************************************/ +#ifndef _IC35REC_H +#define _IC35REC_H 1 + +#include "util.h" /* uchar, .. */ + + +/* IC35 database files */ +#define FILEADDR 0x05 +#define FILEMEMO 0x06 +#define FILETODO 0x07 +#define FILESCHED 0x08 +#define FILE_ANY 0 + +#define MAXFLDS 21 /* max.number of fields in IC35 record */ +#define MAXRLEN (MAXFLDS * (1+255)) /* #fields [Addresses] * (len + fieldlen) */ + +/* record-ids consist of file-id and record-id */ +#define FileId(frid) ( ((ulong)(frid) & 0xFF000000) >> 24 ) +#define RecId(frid) ( ((ulong)(frid) & 0x00FFFFFF) ) +#define FileRecId(fid,rid) ( (((ulong)(fid) << 24) & 0xFF000000) \ + | ( (ulong)(rid) & 0x00FFFFFF) ) +/* field-ids consist of file-id and field-index */ +#define FldFile(fldid) ( ((fldid) & 0xFF00) >> 8 ) +#define FldIdx(fldid) ( (fldid) & 0x00FF ) + +/* IC35 record fields */ +#define FILEfld(fileid,index) ( (fileid)<<8 | (index) ) +/* record fields "Addresses" */ +#define ADDRfld(index) FILEfld( FILEADDR, index ) +#define A_LastName ADDRfld( 0 ) /* max. 50 chars */ +#define A_FirstName ADDRfld( 1 ) /* max. 50 chars */ +#define A_Company ADDRfld( 2 ) /* max. 128 chars */ +#define A_TelHome ADDRfld( 3 ) /* max. 48 chars */ +#define A_TelWork ADDRfld( 4 ) /* max. 48 chars */ +#define A_TelMobile ADDRfld( 5 ) /* max. 48 chars */ +#define A_TelFax ADDRfld( 6 ) /* max. 48 chars */ +#define A_Street ADDRfld( 7 ) /* max. 128 chars */ +#define A_City ADDRfld( 8 ) /* max. 60 chars */ +#define A_ZIP ADDRfld( 9 ) /* max. 10 chars */ +#define A_Region ADDRfld( 10 ) /* max. 40 chars */ +#define A_Country ADDRfld( 11 ) /* max. 15 chars */ +#define A_Email1 ADDRfld( 12 ) /* max. 80 chars */ +#define A_Email2 ADDRfld( 13 ) /* max. 80 chars */ +#define A_URL ADDRfld( 14 ) /* max. 128 chars */ +#define A_BirthDate ADDRfld( 15 ) /* max. 10 chars */ +#define A_Notes ADDRfld( 16 ) /* max. 255 chars */ +#define A_CategoryID ADDRfld( 17 ) /* 1 Byte */ +#define A_Def1 ADDRfld( 18 ) /* max. 128 chars */ +#define A_Def2 ADDRfld( 19 ) /* max. 128 chars */ +#define A_Category ADDRfld( 20 ) /* max. 8 chars */ +/* record fields "Schedule" */ +#define SCHEDfld(index) FILEfld( FILESCHED, index ) +#define S_Subject SCHEDfld( 0 ) /* max. 60 chars */ +#define S_StartDate SCHEDfld( 1 ) /* 8 yyyymmdd */ +#define S_StartTime SCHEDfld( 2 ) /* 6 hhmmss */ +#define S_EndTime SCHEDfld( 3 ) /* 6 hhmmss */ +#define S_AlarmBefore SCHEDfld( 4 ) /* 1 Byte */ +#define AlarmBefNone 0x00 +#define AlarmBefNow 0x01 +#define AlarmBef1min 0x02 +#define AlarmBef5min 0x03 +#define AlarmBef10min 0x04 +#define AlarmBef30min 0x05 +#define AlarmBef1hour 0x06 +#define AlarmBef2hour 0x07 +#define AlarmBef10hour 0x08 +#define AlarmBef1day 0x09 +#define AlarmBef2day 0x0A +#define S_Notes SCHEDfld( 5 ) /* max. 255 chars */ +#define S_Alarm_Repeat SCHEDfld( 6 ) /* 1 Byte */ +#define AlarmNoLED 0x80 +#define AlarmNoBeep 0x40 +#define RepeatMASK 0x0F +#define RepeatNone 0x00 +#define RepeatDay 0x01 +#define RepeatWeek 0x02 +#define RepeatMonWday 0x03 +#define RepeatYear 0x04 +#define RepeatMonMday 0x05 +#define S_EndDate SCHEDfld( 7 ) /* 8 yyyymmdd */ +#define S_RepEndDate SCHEDfld( 8 ) /* 6 hhmmss */ +#define S_RepCount SCHEDfld( 9 ) /* 1 Byte */ +/* record fields "To Do List" */ +#define TODOfld(index) FILEfld( FILETODO, index ) +#define T_StartDate TODOfld( 0 ) /* 8 yyyymmdd */ +#define T_EndDate TODOfld( 1 ) /* 8 yyyymmdd */ +#define T_Completed TODOfld( 2 ) /* 1 Byte */ +#define T_Priority TODOfld( 3 ) /* 1 Byte */ +#define T_Subject TODOfld( 4 ) /* max. 60 chars */ +#define T_Notes TODOfld( 5 ) /* max. 255 chars */ +#define T_CategoryID TODOfld( 6 ) /* 1 Byte */ +#define T_Category TODOfld( 7 ) /* max. 8 chars */ +/* record fields "Memo" */ +#define MEMOfld(index) FILEfld( FILEMEMO, index ) +#define M_Subject MEMOfld( 0 ) /* max. 60 chars */ +#define M_Notes MEMOfld( 1 ) /* max. 255 chars */ +#define M_CategoryID MEMOfld( 2 ) /* 1 Byte */ +#define M_Category MEMOfld( 3 ) /* max. 8 chars */ + +/* IC35 record change flags */ +#define IC35_NEW 0x80 /* new record */ +#define IC35_MOD 0x40 /* modified */ +#define IC35_DEL 0x20 /* deleted (no record data) */ +#define IC35_CLEAN 0x00 /* unchanged */ + + +typedef struct ic35rec IC35REC; + + +char * ic35fname( int fileid ); +int ic35fnflds( int fileid ); + +IC35REC * new_ic35rec( void ); +void del_ic35rec( IC35REC * rec ); + +void set_ic35recid( IC35REC * rec, ulong recid ); +ulong ic35recid( IC35REC * rec ); +void set_ic35recchg( IC35REC * rec, uchar chgflag ); +uchar ic35recchg( IC35REC * rec ); + +void set_ic35recdata( IC35REC * rec, uchar * data, size_t dlen ); +void get_ic35recdata( IC35REC * rec, uchar ** pdata, size_t * pdlen ); + +void set_ic35recfld( IC35REC * rec, int fldid, char * data ); +void get_ic35recfld( IC35REC * rec, int fldid, uchar** pfld, size_t * plen ); +char * ic35recfld( IC35REC * rec, int fldid ); + +int cmp_ic35rec( IC35REC * rec1, IC35REC * rec2 ); + +#endif /*_IC35REC_H*/ diff --git a/src/ic35log.sh b/src/ic35log.sh new file mode 100644 index 0000000..1c118e8 --- /dev/null +++ b/src/ic35log.sh @@ -0,0 +1,807 @@ +#!/bin/sh +# +# $Id: ic35log.sh,v 1.6 2001/01/14 00:09:46 tsch Rel $ +# +# process logs of IC35 communication +# + +# setup parameters and defaults +PAGER="less -S" +PROGNAME=`basename $0` +TMPDIR=/tmp/dpkgmirr$$ + +usage() +{ + echo "\ +usage: $PROGNAME [options] [tarfile] logfile +process IC35 communication 'logfile', which may be gzip'd or contained +in optionally gzip'd 'tarfile'. options: + -l level level of output: + all show all communication (default) + syn2 suppress Level-1 of IC35sync protocol + syn4 decode Level-4 of IC35sync protocol + -s n prepend output with 1:linenumber 2:timestamp + -a show communication also as ASCII characters + -D debug/detail: output diagnostic data \ +" + exit 2 +} + +# add ASCII chars to hex_logfile +# +showascii() # hex_ascii_logfile +{ + awk ' +BEGIN { + # create table chr[] for hex to char conversion + ascii = ascii " !\"#$%&\047()*+,-./" + ascii = ascii "0123456789:;<=>?" + ascii = ascii "@ABCDEFGHIJKLMNO" + ascii = ascii "PQRSTUVWXYZ[\\]^_" + ascii = ascii "`abcdefghijklmno" + ascii = ascii "pqrstuvwxyz{|}~" + for ( c = 0; c <= 255; ++c ) + if ( 32 <= c && c <= 126 ) # 32=space 126=tilda + chr[ sprintf("%02X", c) ] = substr( ascii, c - 32+1, 1 ) + else + chr[ sprintf("%02X", c) ] = "." +} +/WRx/ || /RDx/ { + if ( $1 == "WRx" || $1 == "RDx" ) { + if ( $1 == "WRx" ) dir = "WRa" + else dir = "RDa" + beghex = 3 + } else { + if ( $2 == "WRx" ) dir = "WRa" + else dir = "RDa" + beghex = 4 + } + ascblk = chr[$(beghex)] + for ( i = beghex+1; i <= NF; ++i ) + ascblk = ascblk " " chr[$i] + print + if ( beghex == 3 ) + printf( "%s %2d\t %s\n", dir, $2, ascblk ) + else + printf( "%s %s %2d\t %s\n", $1, dir, $3, ascblk ) +}' - +} + +# convert 'portmon' logfile +# expects 'portmon' logfiles in Unix-format i.e. NL terminated lines +# (DOS-format CRLF terminated lines may break the awk script) +# for 'portmon' see http://www.sysinternals.com +# +ic35prot() # [-vskip=3] [-vDEBUG=1] hex_logfile +{ + awk $* ' +# parameters +# skip number of leading bytes to skip from transmitted blocks +# blocks up to this much bytes are not printed, default 0 +# stamp if non-zero/non-empty stamp transmit blocks with id (first +# field) from portmon log, to ease finding portmon log lines +# default: 0 / "" (disabled) +# MAXBPL maximum number of bytes to print per output line +# default: 128 +# DEBUG if non-zero/non-empty print verbose debugging output +# default: 0 / "" (disabled) + +# extract transmitted data bytes +# returns: +# -1 no data bytes: miss "Length" field +# 0 no data bytes: length value == 0 +# > 0 length value +# xdlen number of logged bytes, maybe < length value +# xdata logged transmitted data bytes +# +function getxdata() +{ + xlen = 0 + xdata = "" + # search "Length" field + for ( i = 1; i <= NF; ++i ) + if ( $i ~ "^Length" ) + break + if ( i > NF ) { # "Length" field not found + # check if log from "ic35sync" + for ( i = 1; i <= NF; ++i ) + if ( $i ~ "^[0-9A-F][0-9A-F][0-9A-F][0-9A-F]:$" ) + break; + if ( i > NF ) + return -1 + # process log from "ic35sync" + xdata = "" + for ( i = i+1; i <= NF; ++i ) { + if ( $i !~ "^[0-9A-F][0-9A-F]$" ) + break; + xdata = xdata $i " " + if ( --hadxmit == 0 ) + break; + } + xdlen = length( xdata ) / 3 + return xdlen + } + # get length value from next field, remove trail ":" + split( $(i+1), flds, ":" ) + len = int( flds[1] ) + if ( len == 0 ) + return 0 # no data transmitted + # extract get data bytes after "Length" and + lentxt = $(i) " " $(i+1) + xdata = substr( $0, index($0, lentxt) + length(lentxt)+1 ) + # make string of 2dig-hex with trail " " for append + sub( "^[^0-9A-Fa-f]*", "", xdata ); # remove leading garbage + sub( "[^0-9A-Fa-f]*$", "", xdata ); # remove trailing garbage + xdata = xdata " " + xdlen = length( xdata ) / 3 + # return length value, maybe not all bytes logged + return len +} + +function prxdata( dir, xbuf, xlen, + _offs, _i, _hex, _pbuf, _plen ) +{ + if ( xlen <= skip ) + return + _plen = length( xbuf ) + _offs = skip * 3 + if ( skip >= 3 ) + _plen = length( xbuf ) - _offs - 2 * 3 # skip checksum also + printf( "%s%sx %2d\t%s\n", xstamp, dir, xlen, substr(xbuf,_offs,_plen) ) +} + +BEGIN { + # max.number of bytes per output line + if ( ! MAXBPL ) MAXBPL = 128 +} + +NF >= 2 { + if ( $2 == "ic35sync" || $2 == "ic35mgr" ) + tsecs = $1 + else + tsecs += $2 +} +# Windows-98: +# 35 0.00034960 Ic35 syn VCOMM_ReadComm COM1 SUCCESS Length: 1: 57 +/VCOMM_ReadComm/ && /SUCCESS/ { + if ( oldxdir == "WR" ) do_print = 1 + xdir = "RD" + if ( DEBUG ) print "DBG: xdir=" xdir, $0 +} +# 36 0.00005040 Ic35 syn VCOMM_GetCommQueueStatus COM1 SUCCESS RX: 6 TX: 0 +/VCOMM_GetCommQueueStatus/ { + if ( DEBUG ) print "DBG: next ", $0 + next +} +# 58 0.00063840 Ic35 syn VCOMM_WriteComm COM1 SUCCESS Length: 1: 41 +/VCOMM_WriteComm/ { + if ( oldxdir == "RD" ) do_print = 1 + xdir = "WR" + if ( DEBUG ) print "DBG: xdir=" xdir, $0 +} + +# Windows-NT: +# 22 0.00106488 IC35Mgr.exe IRP_MJ_READ Serial1 SUCCESS Length 1: 80 +# 16 0.00000000 IC35 Sync.exe IRP_MJ_READ Serial0 Length 1 +# 16 0.00000953 SUCCESS Length 1: 57 +/IRP_MJ_READ/ { + if ( oldxdir == "WR" ) do_print = 1 + if ( $0 ~ "SUCCESS" ) { + xdir = "RD" + if ( DEBUG ) print "DBG: xdir=" xdir, $0 + } else if ( $0 !~ "TIMEOUT" ) { + hadread = 1 + if ( DEBUG ) print "DBG: hadread", $0 + } else { + do_print = 1 + } +} +# 16 0.00000953 SUCCESS Length 1: 57 +/SUCCESS/ && hadread { + xdir = "RD" + hadread = 0 + if ( DEBUG ) print "DBG: dlyd RD", $0 +} +# 15 0.07746540 TIMEOUT Length 0: +/TIMEOUT/ && hadread { + do_print = 1 +} +# 419 0.00000000 IC35 Sync.exe IOCTL_SERIAL_CLR_DTR Serial0 +/IOCTL_SERIAL_CLR_DTR/ { + do_print = 1 +} +# 3 0.00000000 IC35 Sync.exe IRP_MJ_WRITE Serial0 Length 1: 41 +/IRP_MJ_WRITE/ { + if ( oldxdir == "RD" ) do_print = 1 + hadread = 0 + xdir = "WR" + if ( DEBUG ) print "DBG: xdir=" xdir, $0 +} + +# ic35sync/Linux +# 01:11:48.3206 ic35sync 6 com_send(0x8055180,19) = 19 +# 01:11:48.3206 ic35sync 6 0000: 02 13 00 83 0E 00 49 0B 00 01 07 03 00 00 00 00 +# 01:11:48.3206 ic35sync 6 0010: 00 05 01 +/ com_send\(/ { + if ( oldxdir == "RD" ) do_print = 1 + hadxmit = $NF + xmitdir = "WR" +} +# 01:11:48.3703 ic35sync 6 com_recv(0xbffff89b,1) = 1 +# 01:11:48.3703 ic35sync 6 0000: F2 +# 01:11:48.3706 ic35sync 6 com_recv(0x8055181,2) = 2 +# 01:11:48.3706 ic35sync 6 0000: 10 00 +# 01:11:48.3800 ic35sync 6 com_recv(0x8055183,13) = 13 +# 01:11:48.3800 ic35sync 6 0000: A0 0B 00 49 08 00 0A 00 00 06 20 2E 02 +/ com_recv\(/ { + if ( oldxdir == "WR" ) do_print = 1 + hadxmit = $NF + xmitdir = "RD" +} +/ [0-9A-F][0-9A-F][0-9A-F][0-9A-F]: / && hadxmit { + xdir = xmitdir +} + +DEBUG { + print "DBG1: old,xdir=" oldxdir "," xdir, "xbuff=" xblen, "\047" xbuff "\047" +} +do_print { + prxdata( oldxdir, xbuff, xblen ) + if ( txlen > xblen ) { + txdir = (oldxdir == "WR" ? "Wx " : "Rx ") + print txdir txlen + } + xbuff = "" + xblen = txlen = 0 + do_print = 0 +} +xdir { + if ( DEBUG ) print "DBG: do_x=" xdir, $0 + xlen = getxdata() # get transmit data to xdata,xdlen + if ( xlen <= 0 ) + next # no transmit / timeout + if ( xblen == 0 && stamp ) { + if ( stamp == 2 ) + if ( tsecs ~ ":" ) + xstamp = tsecs " " + else + xstamp = sprintf( "%010.4f ", tsecs ) + else + xstamp = sprintf( "%05d\t", $1 ) + } + xbuff = xbuff xdata # buffer transmit data + xblen += xlen + txlen += xlen + if ( xdlen != xlen || xblen >= MAXBPL ) { # not all logged or too much + prxdata( xdir, xbuff, xblen ) + xbuff = "" + xblen = 0 + } + oldxdir = xdir + xdir = "" +} +DEBUG { + print "DBG2: old,xdir=" oldxdir "," xdir, "xbuff=" xblen, "\047" xbuff "\047" +} +' - +} + +# decode IC35sync Level-4 protocol +# expects output from ic35prot() +# +ic35L4prot() # [-vDEBUG=1] L4prot_logfile +{ + awk $* ' +# parameters +# DEBUG if non-zero/non-empty print verbose debugging output +# default: 0 / "" (disabled) + +BEGIN { + # create table chr[] for hex to char conversion + ascii = ascii " !\"#$%&\047()*+,-./" + ascii = ascii "0123456789:;<=>?" + ascii = ascii "@ABCDEFGHIJKLMNO" + ascii = ascii "PQRSTUVWXYZ[\\]^_" + ascii = ascii "`abcdefghijklmno" + ascii = ascii "pqrstuvwxyz{|}~" + for ( c = 0; c <= 255; ++c ) { + xc = sprintf("%02X", c) + if ( 32 <= c && c <= 126 ) # 32=space 126=tilda + chr[ xc ] = substr( ascii, c - 32+1, 1 ) + else + chr[ xc ] = "\\x" xc + } +} + +# get block of bytes from hex transmit data +# convert to ASCII and enclose in " quotes +# +function ascblk( beg, len, _end, _blk, _i ) +{ + if ( (_end = beg + len-1) > NF ) + _end = NF # truncate to end of input line + _blk = "\"" + for ( _i = beg; _i <= _end; ++_i ) + _blk = _blk chr[ $_i ] + _blk = _blk "\"" + return _blk +} + +# get byte block of bytes from hex transmit data +# +function hexblk( beg, len, _end, _blk, _i ) +{ + if ( (_end = beg + len-1) > NF ) + _end = NF # truncate to end of input line + _blk = $beg + for ( _i = beg+1; _i <= _end; ++_i ) + _blk = _blk " " $_i + return _blk +} + +# convert hex strings to decimal +# +function hex2dec( hexstr, _xval, _xdig, _i ) +{ + if ( hexstr ~ "^0[Xx]" ) + hexstr = substr(hexstr, 3) + _xval = 0 + for ( _i = 1; _i <= length(hexstr); ++_i ) { + _xdig = index( "0123456789ABCDEFabcdef", substr(hexstr, _i, 1) ) - 1 + if ( _xdig < 0 ) + break + if ( _xdig >= 16 ) + _xdig -= 16 - 10 + _xval = _xval * 16 + _xdig + } + return _xval +} + +# decode field data from IC35 record +function recdata( beg, fid, _i, _recblk ) +{ + if ( fid != "" ) { # file-id specified: this the header fragment + nflens = 0 + if ( fid == "05" ) nflens = 21 # Addresses + else if ( fid == "06" ) nflens = 4 # Memo + else if ( fid == "07" ) nflens = 8 # ToDoList + else if ( fid == "08" ) nflens = 10 # Schedule + if ( nflens == 0 ) { + printf( "ERR: recdata(beg=%s,fid=%s) unkown fid\n", beg, fid ) + return "" + } + delete flens + for ( _i = 1; _i <= nflens; ++_i ) + flens[_i] = hex2dec( $(beg+_i-1) ) + fidx = 1 + fldrest = 0 + } + if ( fldrest ) { # partial last field from previous fragment + _recblk = " f" fidx-1 "=" ascblk( beg, fldrest ) + sub( "=\"", "=<", _recblk ) + _i = beg + fldrest + fldrest = 0 + } else { # record data on field boundary + _recblk = "" + if ( fid != "" ) _i = beg + nflens # header fragment + else _i = beg # 2nd ff. fragment + } + while ( _i <= NF && fidx <= nflens ) { + _recblk = _recblk " f" fidx "=" ascblk( _i, flens[fidx] ) + _i += flens[fidx++] + } + if ( _i != NF+1 ) { # partial last field + fldrest = _i - (NF+1) + sub( "\"$", ">", _recblk ) + } + return _recblk +} + +# WRx 43 80 26 00 10 00 64 00 4A 1F 00 49 4E 56 45 4E 54 45 43 ... +# WRx 14 83 09 00 49 06 00 02 00 00 +# WRx 8 83 00 03 +# RDx 46 A0 29 00 10 00 D0 07 4A 22 00 49 4E 56 45 4E 54 45 43 ... +# RDx 13 A0 08 00 49 05 00 03 01 +# RDx 8 A0 03 00 +# variables: +# type indicates command "Cmd" "C??" or response "Rsp" "R??" +# "C??","R??" flag PDU contents not matching current knowledge +# pi parameter index: PDU paramters begin at pi-th input field +# pdu PDU identification for output, e.g. "identify", "openfile" +# pars decoded parameters from PDU for output +# l2l3 concatenation of Level-2 and Level-3 id-bytes +# l4 Level-4 command +# l2l3l4 concatenation of l2l3 and l4 +# printhex flag to also output the hex input (for debugging) +# may be set by some decoder, reset on every input block +# DEBUG global debug flag to also output the hex input +# +/^WRx/ || /^RDx/ { + if ( $1 == "WRx" ) { + type = "Cmd" + pi = 2+3+3+2 + 1 # command parameter index + } else { # "RDx" + type = "Rsp" + pi = 2+3+3 + 1 # response parameter index + } + printhex = 0 + pdu = pars = l2l3 = l4 = l2l3l4 = "" + if ( NF >= 2+3+3 ) { # have L2- and L3 id-bytes + l2l3 = $3 $6 + if ( NF >= 2+3+3+2 ) { # have also L4 command + l4 = $9 $10 + l2l3l4 = l2l3 l4 + } +# identify + if ( l2l3 == "8010" ) { +# AdsGetDeviceVersion ? + pdu = "identify" + } else if ( l2l3 == "A010" ) { + pdu = "identify" + pars = ascblk(pi+25, 99) # "DCS15" version + +# power, passwd + } else if ( l2l3l4 == "8249" "0301" ) { + pdu = "power" + pars = ascblk(pi, 5) # "Power" + pars = pars " " hexblk(pi+5, 99) # 00 00 00 + if ( pars != "\"Power\" 00 00 00" ) + type = "C??" + } else if ( l2l3 == "A049" && lastcmd == "power" ) { + pdu = "power" + pars = $(pi) $(pi+1) # 0301 + if ( pars != "0301" || NF+1 - pi != 2 ) + type = "R??" + } else if ( l2l3l4 == "8249" "0300" ) { + pdu = "passwd" + pars = ascblk(pi, 8) # [password] + if ( NF+1 - pi != 8 ) + type = "C??" + } else if ( l2l3 == "A049" && lastcmd == "passwd" ) { + pdu = "passwd" + pars = $(pi) $(pi+1) # 0101 + if ( pars != "0101" || NF+1 - pi != 2 ) + type = "R??" + +# getdtime, setdtime + } else if ( l2l3l4 == "8349" "0200" ) { +# AdsReadSysInfo ? + pdu = "getdtime" + pars = hexblk(pi, 99) # 00 + if ( pars != "00" ) + type = "C??" + } else if ( l2l3 == "A049" && lastcmd == "getdtime" ) { + pdu = "dtime" + pars = ascblk(pi, 14) # [mmddyyyyhhmmss] + tail = hexblk(pi+14, 99) # 00 00 + if ( tail != "00 00" ) { + pars = pars " " tail + type = "R??" + } + } else if ( l2l3l4 == "8249" "0201" ) { +# AdsWriteSysInfo ? + pdu = "setdtime" + head = hexblk(pi, 1) # 00 + pars = ascblk(pi+1, 14) # [mmddyyyyhhmmss] + tail = hexblk(pi+1+14, 99) # 00 00 + if ( head != "00" && tail != "00 00" ) { + pars = head " " pars " " tail + type = "C??" + } + # response "A0" + # pdu = "done" below + +# category + } else if ( l2l3l4 == "8249" "0302" ) { +# AdsReadCategoryData ? + pdu = "category" + pars = ascblk(pi, 8) # [category] + if ( NF+1 - pi != 8 ) + type = "C??" + } else if ( l2l3 == "A049" && lastcmd == "category" ) { + pdu = "category" + pars = $(pi) $(pi+1) + if ( NF+1 - pi != 2 ) + type = "R??" + +# openfile, closefile + } else if ( l2l3l4 == "8249" "0002" ) { +# AdsOpenDataBase ? + pdu = "openfile" + head = hexblk( pi, 7 ) + lf2 = hex2dec( $(pi+10) $(pi+9) $(pi+8) $(pi+7) ) + lf = hex2dec( $(pi+11) ) + fn = ascblk(pi+12, lf) + tail = hexblk( pi+12+lf, 99 ) + if ( length( fn ) == lf + 2 \ + && lf2 == lf + 2 \ + && head == "00 00 00 00 00 00 00" \ + && tail == "02" ) + pars = fn + else { + pars = head " lf2=" lf2 " lf=" lf " " fn " " tail + type = "C??" + } + # set current file-id, to determine num.of record fields + if ( fn ~ "Addresses" ) currfid = "05" + else if ( fn ~ "Memo" ) currfid = "06" + else if ( fn ~ "To Do List" ) currfid = "07" + else if ( fn ~ "Schedule" ) currfid = "08" + + } else if ( l2l3 == "A049" && lastcmd == "openfile" ) { + pdu = "openfile" + pars = "fd=" $(pi+1) $(pi) + if ( NF+1 - pi != 2 ) + type = "R??" + } else if ( l2l3l4 == "8249" "0003" ) { +# AdsCloseDataBase ? AdsCloseHandle ? + pdu = "closefile" + pars = "fd=" $(pi+1) $(pi) + tail = hexblk( pi+2, 99 ) + if ( tail != "00 00 00 00" ) { + pars = pars " " tail + type = "C??" + } + # unset current file-id on close + currfid = "" + # response "A0" + # pdu = "done" below + +# getflen + } else if ( l2l3l4 == "8349" "0103" ) { +# AdsGetRecordCount + pdu = "getflen" + pars = "fd=" $(pi+1) $(pi) + tail = hexblk( pi+2, 99 ) + if ( tail != "00 00 00 00" ) { + pars = pars " " tail + type = "C??" + } + } else if ( l2l3l4 == "8349" "0104" ) { +# AdsGetModifiedRecordCount + pdu = "getflenmod" + pars = "fd=" $(pi+1) $(pi) + tail = hexblk( pi+2, 99 ) + if ( tail != "00 00 00 00" ) { + pars = pars " " tail + type = "C??" + } + } else if ( l2l3 == "A049" && (lastcmd == "getflen" \ + || lastcmd == "getflenmod") ) { + pdu = lastcmd + pars = "n=" hex2dec( $(pi+1) ($pi) ) + if ( NF+1 - pi != 2 ) + type = "R??" + +# read record + } else if ( l2l3l4 == "8349" "0105" ) { +# AdsReadRecordByID + pdu = "readrecrid" + pars = "fd=" $(pi+1) $(pi) + pars = pars " ri=" $(pi+3) $(pi+2) + pars = pars " " $(pi+4) + pars = pars " fi=" $(pi+5) + reqrid = hex2dec( $(pi+5) $(pi+4) $(pi+3) $(pi+2) ) + if ( NF+1 - pi != 6 ) + type = "C??" + } else if ( l2l3l4 == "8349" "0106" ) { +# AdsReadRecordByIndex + pdu = "readrec" + reqrid = "" + pars = "fd=" $(pi+1) $(pi) + pars = pars " ix=" hex2dec( $(pi+3) $(pi+2) ) + tail = hexblk( pi+4, 99 ) + if ( tail != "00 00" ) { + pars = pars " " tail + type = "C??" + } + } else if ( l2l3l4 == "8349" "0107" ) { +# AdsReadNextModifiedRecord + pdu = "readrecmod" + reqrid = "" + pars = "fd=" $(pi+1) $(pi) + tail = hexblk(pi+2, 99) + if ( NF+1 - pi != 6 || tail != "00 00 00 00" ) { + pars = pars " " tail + type = "C??" + } + } else if ( (l2l3 == "A049" \ + || l2l3 == "2048") && (lastcmd == "readrec" \ + || lastcmd == "readrecmod" \ + || lastcmd == "readrecrid") ) { + pdu = "rechdr" (l2l3 == "2048" ? "(more)" : "(last)") + rsprid = hex2dec( $(pi+3) $(pi+2) $(pi+1) $(pi) ) + pars = "ri=" $(pi+1) $(pi) + pars = pars " " $(pi+2) + pars = pars " fi=" $(pi+3) + pars = pars " ch=" $(pi+4) + if ( NF+1 - pi == 5 ) + pars = pars " (nodata)" + else if ( lastcmd == "readrecrid" && rsprid != reqrid ) + pars = pars " notfound (" NF+1 - (pi+5) " bytes garbage)" + else + pars = pars " " recdata( pi+5, $(pi+3) ) + reqrid = "" + } else if ( (l2l3 == "A049" \ + || l2l3 == "2048") && lastcmd == "readmore" ) { + pdu = "recdat" (l2l3 == "2048" ? "(more)" : "(last)") + pars = " " + pars = pars " " recdata( pi, "" ) + +# write record + } else if ( l2l3l4 == "0248" "0108" \ + || l2l3l4 == "8249" "0108" ) { +# AdsWriteRecord + pdu = "writerec" (l2l3 == "0248" ? "(more)" : "(last)") + fdesc = $(pi+1) $(pi) + zero1 = hexblk(pi+2, 4) + p678 = hexblk(pi+6, 3) + zero2 = hexblk(pi+9, 2) + lrec = hex2dec( $(pi+14) $(pi+13) $(pi+12) $(pi+11) ) + if ( zero1 == "00 00 00 00" && zero2 == "00 00" ) { + pars = "fd=" fdesc " " p678 " lr=" lrec + pars = pars " " recdata( pi+15, currfid ) + } else { + pars = "fd=" fdesc " " zero1 " " p678 " " zero2 " lr=" lrec + pars = pars " " recdata( pi+15, currfid ) + type = "C??" + } +# update record + } else if ( l2l3l4 == "0248" "0109" \ + || l2l3l4 == "8249" "0109" ) { +# AdsUpdateRecord + pdu = "updatrec" (l2l3 == "0248" ? "(more)" : "(last)") + pars1 = "fd=" $(pi+1) $(pi) + pars1 = pars1 " ri=" $(pi+3) $(pi+2) + pars1 = pars1 " " $(pi+4) + pars1 = pars1 " fi=" $(pi+5) + pars1 = pars1 " " hexblk(pi+6, 3) + zero2 = hexblk(pi+9, 2) + pars2 = " lr=" hex2dec( $(pi+14) $(pi+13) $(pi+12) $(pi+11) ) + if ( zero2 == "00 00" ) { + pars = pars1 pars2 + pars = pars " " recdata( pi+15, currfid ) + } else { + pars = pars1 " " zero2 pars2 + pars = pars " " recdata( pi+15, currfid ) + type = "C??" + } + } else if ( (l2l3 == "0248" \ + || l2l3 == "8249") && (lastcmd == "writerec(more)" \ + || lastcmd == "updatrec(more)") ) { + pdu = substr(lastcmd, 1, 8) + pdu = pdu (l2l3 == "0248" ? "(more)" : "(last)") + pars = " " + pars = pars " " recdata( pi-2, "" ) + # response l2 == "90" + # pdu = "writemore" below + } else if ( l2l3 == "A049" && (lastcmd == "writerec(last)" \ + || lastcmd == "updatrec(last)") ) { + pdu = substr(lastcmd, 1, 8) + pars = "ri=" $(pi+1) $(pi) + pars = pars " " $(pi+2) + pars = pars " fi=" $(pi+3) + if ( NF+1 - pi != 4 ) + type = "R??" + +# delete record + } else if ( l2l3l4 == "8249" "0102" ) { +# AdsDeleteRecord + pdu = "deleterec" + pars = "fd=" $(pi+1) $(pi) + pars = pars " ri=" $(pi+3) $(pi+2) + pars = pars " " $(pi+4) + pars = pars " fi=" $(pi+5) + if ( NF+1 - pi != 6 || $(pi+4) != "00" ) + type = "C??" + # response l2 == "A0" + # pdu = "done" below + +# reset changeflag + } else if ( l2l3l4 == "8249" "010A" ) { +# AdsCommitRecord + pdu = "resetchg" + pars = "fd=" $(pi+1) $(pi) + pars = pars " ri=" $(pi+3) $(pi+2) + pars = pars " " $(pi+4) + pars = pars " fi=" $(pi+5) + if ( $(pi+4) != "00" || NF+1 - pi != 6 ) + type = "C??" + # response l2 == "A0" + # pdu = "done" below + + } + +# short PDUs with Level-2 id-byte only + } else if ( NF == 2+3 ) { + if ( $3 == "83" ) { + pdu = "readmore" + } else if ( $3 == "90" ) { + pdu = "writemore" + } else if ( $3 == "81" ) { + pdu = "disconn" + } else if ( $3 == "A0" ) { + pdu = "done" + } + } + + if ( $1 == "WRx" ) { + lastcmd = pdu + if ( pdu == "" ) type = "C??" # unknown command PDU + if ( type !~ "^C..$" ) + print "INTERNAL ERROR" + } else { # "RDx" + lastrsp = pdu + if ( pdu == "" ) type = "R??" # unknown response PDU + if ( type !~ "^R..$" ) + print "INTERNAL ERROR" + } + + if ( type == "C??" || type == "R??" \ + || DEBUG || printhex ) + print # show hex data for unknown PDU / params + printf( "%s %2d\t", type, $2 ) + if ( pdu == "" ) { + for ( i = 3; i <= NF; ++i ) printf( " %s", $i ) + printf( "\n" ) + } else { + printf( " %-14s %s\n", pdu, pars ) + } +} +' - +} + +# process options +# +LEVEL=all +while getopts "l:s:aDh" OPT +do case $OPT in + l ) LEVEL=$OPTARG ;;# level of output + s ) STAMP="-vstamp=$OPTARG" ;;# prepend lineno/time stamp + a ) ASCII=1 ;;# also ASCII chars + D ) DEBUG="-vDEBUG=1" ; KEEPTMP=1 ;;# debug/diagnostic + h|? ) usage ;;# for other options give help and exit + esac +done +shift `expr $OPTIND - 1` +[ $# -eq 1 -o $# -eq 2 ] || usage + +# the real work .. +# +case "$LEVEL" in +all) + PASS1="ic35prot $STAMP $DEBUG" + if [ -n "$ASCII" ] + then PASS2=showascii + else PASS2=cat + fi + ;; +syn2) + PASS1="ic35prot -vskip=3 $DEBUG" + if [ -n "$ASCII" ] + then PASS2=showascii + else PASS2=cat + fi + ;; +syn4) + PASS1="ic35prot -vskip=3" + PASS2="ic35L4prot $DEBUG" + ;; +*) + echo "$PROGNAME -- unsupported output level: $LEVEL" + exit 1 +esac + +if [ $# -eq 2 ] ; then + if [ "$1" != "-" ] && expr "`file $1`" : '.*\/dev/null ; then + tar -xOzf $1 $2 | tr -d '\015' | $PASS1 | $PASS2 | $PAGER + else + tar -xOf $1 $2 | tr -d '\015' | $PASS1 | $PASS2 | $PAGER + fi +else + if [ "$1" != "-" ] && expr "`file $1`" : '.*\/dev/null ; then + zcat $1 | tr -d '\015' | $PASS1 | $PASS2 | $PAGER + else + cat $1 | tr -d '\015' | $PASS1 | $PASS2 | $PAGER + fi +fi diff --git a/src/ic35mgr.c b/src/ic35mgr.c new file mode 100644 index 0000000..938db4b --- /dev/null +++ b/src/ic35mgr.c @@ -0,0 +1,991 @@ +/************************************************************************ +* Copyright (C) 2001 Thomas Schulz * +* */ + static char rcsid[] = + "$Id: ic35mgr.c,v 1.20 2001/11/20 23:08:35 thosch Exp $"; /* +* * +* IC35 manager * +* * +************************************************************************* +* * +* ??? is "fixme" mark: sections of code needing fixes * +* * +* conditional compile on NO_LOGSIM: if #defined, logging of the * +* IC35 communications as well as simulated communication are NOT * +* supported, default WITH logging and com-simulation support. * +* * +* for usage run ic35mgr -h or see function usage() below. * +* * +************************************************************************/ + +#include /* printf(), .. */ +#include /* malloc(), free() */ +#include /* memcpy(), strcpy() ..*/ +#include /* islower(), .. */ +#include /* getopt(), optarg, .. */ +#include /* struct tm, time() .. */ +#include /* struct utimbuf, .. */ +#include +#include +#include +#include + +#include "util.h" /* FALSE, .. */ +#include "comio.h" /* COM_SIMINIT(), .. */ +#include "mgrtrans.h" /* IC35mgr transactions */ +NOTUSED(rcsid) + + +extern char * pkgvers; /* these are all */ +extern char * pkgdate; /* in versinfo.c, which is */ +extern char * bldinfo; /* auto-generated by Makefile */ + + +/* ==================================== */ +/* IC35 manager commands */ +/* ==================================== */ + +/* report IC35 status +* ------------------ +*/ +static int +ic35mgrstat( char * devname, char * statfname ) +{ + int mmcnum; + int rval; + char label[11+1]; + + if ( (rval = mconnect( devname, statfname )) != OK ) + return rval; + + for ( mmcnum = 1; mmcnum <= 2; ++mmcnum ) { + if ( (rval = mmc_status( mmcnum )) < 0 ) + break; + if ( rval != 0 ) { + if ( (rval = mmc_label( mmcnum, label )) < 0 ) + break; + message( "MMCard%d found, label: \"%s\"", mmcnum, label ); + } else { + message( "MMCard%d not present", mmcnum ); + } + } + + mdisconnect(); + return rval; +} + + +/* backup IC35 database to file +* ---------------------------- +*/ +static int +ic35backup( char * devname, char * fname ) +{ + int rval; + + if ( (rval = mconnect( devname, NULL )) != OK ) + return rval; + + rval = readdatabase( fname ); + + mdisconnect(); + return rval; +} + +/* restore IC35 database from file +* ------------------------------- +*/ +static int +ic35restore( char * devname, char * fname ) +{ + int rval; + + if ( (rval = mconnect( devname, NULL )) != OK ) + return rval; + + rval = writedatabase( fname ); + + mdisconnect(); + return rval; +} + + +/* list IC35 MMCard(s) directories +* ------------------------------- +* list looks like: +* MMCardn "Label" +* size filetimestamp attr filepath +* nnnnnnnnnn yyyy-mm-dd hh:mm:ss xxx MMCard1\subdir\filename.ext +*/ +static int +mmcdirlist( char * dirpath ) +{ + MMCDIR * dirp; + MMCDIRENT * dirent; + size_t pfxlen; + char * subdir; + char attrtext[4+1]; + int rval, rc; + + if ( (dirp = mmc_opendir( dirpath )) == NULL ) + return ERR; + pfxlen = strlen( dirpath ) + 1; + rval = OK; + while ( (dirent = mmc_readdir( dirp )) != NULL ) { + switch ( dirent->attr ) { + case MMCattrDirectory: strcpy( attrtext, "dir" ); break; + case MMCattrArchive: strcpy( attrtext, "arc" ); break; + default: sprintf( attrtext, "0x%02X", dirent->attr & 0xFF ); + } + printf( "%10ld %s %4s %s\\%s\n", + dirent->size, mmctstampstr( dirent->tstamp ), + attrtext, dirpath, dirent->name ); + fflush( stdout ); + if ( dirent->attr & MMCattrDirectory + && strcmp( dirent->name, ".." ) != 0 + && (subdir = malloc( pfxlen + strlen(dirent->name) + 1 )) != NULL ) { + sprintf( subdir, "%s\\%s", dirpath, dirent->name ); + rc = mmcdirlist( subdir ); + if ( rval == OK ) rval = rc; + free( subdir ); + } + } + rc = mmc_closedir( dirp ); + if ( rval == OK ) rval = rc; + return rval; /* errcode of first failure or success OK */ +} +static int +ic35mmcdir( char * devname ) +{ + int mmcnum; + int rval; + char label[11+1]; + char dirpath[7+1]; + + if ( (rval = mconnect( devname, NULL )) != OK ) + return rval; + + for ( mmcnum = 1; mmcnum <= 2; ++mmcnum ) { + if ( (rval = mmc_status( mmcnum )) < 0 ) + break; + if ( rval == 0 ) { + message( "MMCard%d not present", mmcnum ); + continue; + } + if ( (rval = mmc_label( 1, label )) < 0 ) + break; + printf( "MMCard%d \"%s\"\n", mmcnum, label ); + fflush( stdout ); + sprintf( dirpath, "MMCard%d", mmcnum ); + if ( (rval = mmcdirlist( dirpath )) < 0 ) + break; + } + + mdisconnect(); + return rval; +} + + + + +/* ==================================== */ +/* IC35 MMCard file commands */ +/* ==================================== */ + +#define MINBLKSZ 1 /* min.blocksize */ +#define MAXBLKSZ 16350 /* max.blocksize (found by experiments) */ + +#define ERR_mmcpfx -1 +#define ERR_mmcnum -2 +#define ERR_mmcslash -3 +#define ERR_mmcbadpath -4 + +static char * MMCard = "MMCard"; + +static int blocksize; /* read/write buffer size mmcget/mmcput */ + +/* check,convert MMCard path to IC35 format & get MMCard number +* ------------------------------------------------------ +* the ic35path will be checked (slash means / or \): +* - prefix must be "MMCard" in lowercase or uppercase or mixed +* - MMCard number after prefix must be '1' or '2' +* - slash after MMCard number (unless the path ends there) +* - no adjacent slashes +* - at most one dot between slashes +* - max. 8 characters after slash until dot or slash or end +* - max. 3 characters after dot until slash or end +* the ic35path will be translated to match IC35 requirements: +* translate '/' to '\' dir separators and lowercase to upper. +* the translation is done in place, i.e. the argument string +* is modified. +* returns: +* -1 ERR, missing "MMCard" prefix +* -2 ERR, bad MMCard number (not 1,2) +* -3 ERR, missing slash/backslash after "MMCard[12]" +* -4 ERR, bad path component (too long, >1 dot, adjacent slashes) +* +* parameter mode: if 1 create directories +*/ +static int +_ic35mmcpath( char * ic35path , int mode) +{ + MMCDIR * mdirp; + int mmcnum; + char * ptr; + char * pdot; + char * pslash; + + /* check/set "MMCard" prefix in ic35fpath */ + if ( strncasecmp( ic35path, MMCard, strlen(MMCard) ) != 0 + || ! isdigit( ic35path[strlen(MMCard)] ) ) + return ERR_mmcpfx; /* error: missing MMCard prefix */ + memcpy( ic35path, MMCard, strlen(MMCard) ); + /* check MMCard number and slash/backslash after prefix */ + ptr = ic35path + strlen(MMCard); + if ( *ptr != '1' && *ptr != '2' ) + return ERR_mmcnum; /* error: bad MMCard number */ + mmcnum = *ptr - '0'; + ++ptr; + if ( *ptr == 0 ) return mmcnum; /* allow just MMCard */ + if ( *ptr == '/' ) *ptr = '\\'; + if ( *ptr != '\\' ) + return ERR_mmcslash; /* error: miss slash/backslash */ + /* check path components for 8+3, too many dots, etc. */ + pslash = ptr++; pdot = NULL; + do { + switch ( *ptr ) { + case '/': + case '\\': + if (mode == 1) { + /* create directory if it does not yet exist */ + *ptr = 0; + if ((mdirp = mmc_opendir(ic35path)) == NULL) + mdirp = mmc_createdir(ic35path); + mmc_closedir(mdirp); + } + *ptr = '\\'; + /* fall through */ + case '\0': + if ( ptr - (pslash+1) == 0 ) { + if (*ptr == 0) *pslash = 0; /* remove trailing slash */ + else return ERR_mmcbadpath; /* error: adjacent slashes */ + } + if ( (pdot != NULL && ptr - (pdot+1) > 3) + || (pdot == NULL && ptr - (pslash+1) > 8) ) + return ERR_mmcbadpath; /* error: too long since slash */ + pslash = ptr; pdot = NULL; + break; + case '.': + if ( pdot != NULL ) + return ERR_mmcbadpath; /* error: too many dots */ + if ( ptr - (pslash+1) > 8 ) + return ERR_mmcbadpath; /* error: too long before dot */ + pdot = ptr; + break; + } + } while ( *ptr++ ); + /* convert ic35path '/' to '\\', lowercase to uppercase */ + for ( ptr = ic35path+strlen(MMCard); *ptr; ++ptr ) { + if ( islower( *ptr ) ) + *ptr = (char)toupper( *ptr ); + } + return mmcnum; +} + +/* output MMCard path error message +* -------------------------------- +*/ +static void +_mmcpatherror( int errnum, char * ic35path ) +{ + switch ( errnum ) { + case ERR_mmcpfx: + error( "missing \"%s\" prefix in IC35 filepath: %s", MMCard, ic35path ); + break; + case ERR_mmcnum: + error( "bad MMCard number in IC35 filepath: %s", ic35path ); + break; + case ERR_mmcslash: + error( "missing \\ or / in IC35 filepath: %s", ic35path ); + break; + case ERR_mmcbadpath: + default: + error( "bad path component in IC35 filepath: %s", ic35path ); + break; + } +} + +/* get filename from IC35 MMCard path +* ---------------------------------- +* derive local filename from base filename in ic35path, i.e. leading +* directory components removed, and translate to lowercase. +* the buffer for the local filename is strdup()'d and shall be freed +* by caller. +*/ +static char * +_dupic35fname( char * ic35path, int mode) +{ + char * ptr; + char * fname; + + /* create copy */ + if ( (fname = strdup( ic35path )) == NULL ) + return NULL; + + /* convert to unix filename, creating dirs on the way */ + ptr = fname; + while (*ptr) { + if (*ptr == '\\' || *ptr == '/') { + if ( mode == 1 ) { + /* create subdir */ + *ptr = 0; + if (mkdir(fname, 0700) < 0 + && errno != EEXIST ) { + perror(fname); + } + } + *ptr++ = '/'; + } else { + *ptr++ = tolower( *ptr ); + } + } + return fname; +} + +static void progress( int mode, char* ipath, char *upath, ulong curr, ulong total) +{ + fprintf( stderr, "\r%s %s %s %s %ld", + mode? "read": "write", ipath, + mode? "->" : "<-", upath, (curr+511)/1024); + if (total) fprintf( stderr, "/%ldk, %ld%% ", (total+511)/1024, + curr*100/total); + else fprintf( stderr, "k "); +} + +/* read file from IC35 MMCard +* -------------------------- +* the ic35path will be translated to match IC35 requirements: +* translate '/' to '\' dir separators and lowercase to upper. +* on absent local filename (NULL or empty string) derive it from +* base filename in ic35path, i.e. leading directory components +* removed, translated to lowercase. +* e.g. mmcard1/Ic35\App\Reversi.APP will be translated +* to MMCard1\IC35\APP\REVERSI.APP and local file reversi.app +* the local file's timestamp will be set to the IC35 file's. +* (but IC35 seems to errorneously set the IC35 file's timestamp +* to the time of read, although the IC35 file is not modified!?) +* +*/ + +static int +mmcget( char * ic35path, char * a_fname ) +{ + int rbuffsize = 5 * 1024; + char * fname; + MMCFILE * mmcfp; + MMCDIRENT * mmcfstat; + FILE * fp; + struct utimbuf ftime; + uchar * buff; + size_t blen; + int rlen; + ulong frlen; + int rval = OK; + ulong size; + + if ( MINBLKSZ <= blocksize && blocksize <= MAXBLKSZ ) + rbuffsize = blocksize; + + else if ( blocksize != 0 ) + error( "bad buffer size %d, using default %d", blocksize, rbuffsize ); + + /* create fname from basename(ic35path) if absent */ + if ( a_fname && *a_fname ) { + fname = a_fname; + } else + if ( (fname = _dupic35fname( ic35path, 1 )) == NULL ) { + error( "no memory for local filename from %s", ic35path ); + return ERR; + } + + if ( (mmcfp = mmc_openfile( ic35path, MMCopenexist )) != NULL ) { + if ( (mmcfstat = mmc_statfile( mmcfp )) != NULL ) { + size = mmcfstat->size; + if ( (buff = malloc( blen = rbuffsize )) != NULL ) { + if ( (fp = fopen( fname, "w" )) != NULL ) { + rval = OK; + frlen = 0; + progress( 1, ic35path, fname, frlen, size ); + while ( (rlen = mmc_readfile( mmcfp, buff, blen )) > 0 ) { + frlen += rlen; + progress( 1, ic35path, fname, frlen, size ); + if ( fwrite( buff, 1, rlen, fp ) != rlen ) { + error( "write error %s: %s (%d)", + fname, strerror(errno), errno ); + rval = ERR; + break; + } + } + fclose( fp ); + if ( rlen == 0 && rval == OK ) { + ftime.actime = time( NULL ); + ftime.modtime = mmctstampunixtime( mmcfstat->tstamp ); + utime( fname, &ftime ); /* set file modtime from IC35 */ + fprintf( stderr, "\n" ); + } else /* mmc_readfile() or fwrite() failed */ + rval = ERR; + } else /* (fp = fopen()) == NULL */ + error( "cannot open %s: %s (%d)", + fname, strerror(errno), errno ); + free( buff ); + } else /* (buff = malloc()) == NULL */ + error( "no memory for read buffer (%d)", rbuffsize ); + } else /* mmc_statfile() failed */ + rval = ERR; + mmc_closefile( mmcfp ); + } else /* mmc_openfile() failed */ + rval = ERR; + if ( fname != a_fname ) + free( fname ); + return rval; +} + +static int +ic35mmcget( char * devname, char * ic35path, char * a_fname ) +{ + int mmcnum; + int rval; + + /* check ic35path, check/set "MMCard" prefix and */ + /* convert '/' to '\\', lowercase to uppercase */ + if ( (mmcnum = _ic35mmcpath( ic35path, 0 )) < 0 ) { + _mmcpatherror( mmcnum, ic35path ); + return ERR; + } + /* connect to IC35 in SyncStation via serial device */ + if ( (rval = mconnect( devname, NULL )) != OK ) + return rval; + /* get MMCard status (else other MMCard ops would fail) */ + if ( (rval = mmc_status( mmcnum )) <= 0 ) { + if ( rval == 0 ) + message( "MMCard%d not present", mmcnum ); + mdisconnect(); + return ERR; + } + + /* read file from IC35 MMCard */ + rval = mmcget( ic35path, a_fname); + + /* cleanup and disconnect from IC35 */ + mdisconnect(); + return rval; +} + +/* get complete tree from IC35 MMCard(s) +* ------------------------------- +* dirpath = subdir from which to start +* +* +* +*/ +static int +mmcgettree( char * dirpath ) +{ + MMCDIR * dirp; + MMCDIRENT * dirent; + size_t pfxlen; + char * subdir; + char * ic35path; + int rval, rc; + + if ( (dirp = mmc_opendir( dirpath )) == NULL ) + return ERR; + pfxlen = strlen( dirpath ) + 1; + rval = OK; + while ( (dirent = mmc_readdir( dirp )) != NULL ) { + switch ( dirent->attr ) { + case MMCattrDirectory: + if (strcmp(dirent->name, ".." ) != 0 + && (subdir = malloc( pfxlen + strlen(dirent->name) + 1 )) != NULL ) { + sprintf(subdir, "%s\\%s", dirpath, dirent->name ); + rc = mmcgettree(subdir); + if ( rval == OK ) rval = rc; + free(subdir); + } + break; + case MMCattrArchive: + ic35path = malloc(pfxlen + strlen(dirent->name) + 1 ); + sprintf(ic35path, "%s\\%s", dirpath, dirent->name); + mmcget(ic35path, NULL); + free(ic35path); + break; + default: + } + } + rc = mmc_closedir( dirp ); + if ( rval == OK ) rval = rc; + return rval; /* errcode of first failure or success OK */ +} + +static int +ic35mmcgettree( char * devname, char * ic35path) +{ + int mmcnum; + int rval; + char dirpath[7+1]; + + if (ic35path) { + /* check ic35path, check/set "MMCard" prefix and */ + /* convert '/' to '\\', lowercase to uppercase */ + if ( (mmcnum = _ic35mmcpath( ic35path, 0 )) < 0 ) { + _mmcpatherror( mmcnum, ic35path ); + return ERR; + } + if ( (rval = mconnect( devname, NULL )) != OK ) + return rval; + if ( (rval = mmc_status( mmcnum )) == 0 ) { + message( "MMCard%d not present", mmcnum ); + rval = ERR; + } else if (rval > 0) + rval = mmcgettree( ic35path ); + } + else { + if ( (rval = mconnect( devname, NULL )) != OK ) + return rval; + for ( mmcnum = 1; mmcnum <= 2; ++mmcnum ) { + if ( (rval = mmc_status( mmcnum )) < 0 ) + break; + if ( rval == 0 ) { + message( "MMCard%d not present", mmcnum ); + continue; + } + sprintf( dirpath, "MMCard%d", mmcnum ); + if ( (rval = mmcgettree( dirpath )) < 0 ) + break; + } + } + mdisconnect(); + return rval; +} + +/* write file to IC35 MMCard +* ------------------------- +* see ic35mmcget() comment about ic35path conversions and deriving +* fname from it if a_fname is NULL or empty. +*/ + +static int +mmcput(char * ic35path, char * a_fname ) +{ + int wbuffsize = 2 * 1024; + MMCFILE * mmcfp; + FILE * fp; + uchar * buff; + size_t blen; + char * fname; + int rlen; + ulong fwlen; + ulong size; + int rval = OK; + struct stat statbuf; + + if ( MINBLKSZ <= blocksize && blocksize <= MAXBLKSZ ) + wbuffsize = blocksize; + else if ( blocksize != 0 ) + error( "bad buffer size %d, using default %d", blocksize, wbuffsize ); + + /* create fname from basename(ic35path) if absent */ + if ( a_fname && *a_fname ) { + fname = a_fname; + } else + if ( (fname = _dupic35fname( ic35path, 0 )) == NULL ) { + error( "no memory for local filename from %s", ic35path ); + return ERR; + } + + /* write file to IC35 MMCard */ + if ( (mmcfp = mmc_openfile( ic35path, MMCcreatrunc )) != NULL ) { + if ( (buff = malloc( blen = wbuffsize )) != NULL ) { + if ( (fp = fopen( fname, "r" )) != NULL ) { + rval = OK; + fwlen = 0; + stat(fname, &statbuf); + size = statbuf.st_size; + progress( 0, ic35path, fname, fwlen, size ); + while ( (rlen = fread( buff, 1, blen, fp )) > 0 ) { + if ( mmc_writefile( mmcfp, buff, rlen ) != rlen ) { + rval = ERR; + break; + } + fwlen += rlen; + progress( 0, ic35path, fname, fwlen, size ); + } + if ( rlen <= 0 && feof( fp ) && rval == OK ) { + fprintf( stderr, "\n" ); + } else { /* fread() or mmc_writefile() failed */ + if ( rval != ERR ) + error( "read error %s: %s (%d)", + fname, strerror(errno), errno ); + rval = ERR; + } + fclose( fp ); + } else /* (fp = fopen()) == NULL */ + error( "cannot open %s: %s (%d)", + fname, strerror(errno), errno ); + free( buff ); + } else /* (buff = malloc()) == NULL */ + error( "no memory for write buffer (%d)", wbuffsize ); + mmc_closefile( mmcfp ); + } else /* mmc_openfile() failed */ + rval = ERR; + if ( fname != a_fname ) + free( fname ); + return rval; +} + +static int +ic35mmcput( char * devname, char * ic35path, char * a_fname ) +{ + int mmcnum; + int rval; + + /* check ic35path, check/set "MMCard" prefix and */ + /* convert '/' to '\\', lowercase to uppercase */ + if ( (mmcnum = _ic35mmcpath( ic35path, 0 )) < 0 ) { + _mmcpatherror( mmcnum, ic35path ); + return ERR; + } + /* connect to IC35 in SyncStation via serial device */ + if ( (rval = mconnect( devname, NULL )) != OK ) + return rval; + /* get MMCard status (else other MMCard ops would fail) */ + if ( (rval = mmc_status( mmcnum )) <= 0 ) { + if ( rval == 0 ) + message( "MMCard%d not present", mmcnum ); + mdisconnect(); + return ERR; + } + rval = mmcput(ic35path, a_fname ); + + /* cleanup and disconnect from IC35 */ + mdisconnect(); + return rval; +} + +/* put complete tree to IC35 MMCard(s) +* ------------------------------- +* dirpath = subdir from which to start +* +* +* +*/ +static int +mmcputtree( char * dirpath ) +{ + DIR * dirp; + struct dirent * dirent; + size_t pfxlen; + char * path; + int rval, rc; + int mmcnum; + struct stat statbuf; + + if ( (dirp = opendir( dirpath )) == NULL ) + return ERR; + pfxlen = strlen( dirpath ) + 1; + rval = OK; + while ( (dirent = readdir( dirp )) != NULL ) { + if (strcmp(dirent->d_name, ".." ) == 0) continue; + if (strcmp(dirent->d_name, "." ) == 0) continue; + path = malloc(pfxlen + strlen(dirent->d_name) + 1 ); + if (path == NULL) { + rval = ERR; + break; + } + sprintf(path, "%s/%s", dirpath, dirent->d_name); + if (stat(path, &statbuf) == -1) { + rval = ERR; + break; + } + if (S_ISDIR(statbuf.st_mode)) { + rval = mmcputtree(path); + } else if (S_ISREG(statbuf.st_mode)) { + /* convert to IC35 name */ + if ( (mmcnum = _ic35mmcpath( path, 1)) < 0 ) { + _mmcpatherror( mmcnum, path ); + return ERR; + } + mmcput(path, NULL); + } + free(path); + } + rc = closedir( dirp ); + if ( rval == OK ) rval = rc; + return rval; /* errcode of first failure or success OK */ +} + +static int +ic35mmcputtree( char * devname, char * ic35path) +{ + int mmcnum; + int rval; + char * tmp; + + /* check ic35path, check/set "MMCard" prefix and */ + /* convert '/' to '\\', lowercase to uppercase */ + tmp = strdup(ic35path); + mmcnum = _ic35mmcpath( tmp, 0 ); + free(tmp); + if ( mmcnum < 0 ) { + _mmcpatherror( mmcnum, tmp ); + return ERR; + } + if ( (rval = mconnect( devname, NULL )) != OK ) + return rval; + if ( (rval = mmc_status( mmcnum )) == 0 ) { + message( "MMCard%d not present", mmcnum ); + rval = ERR; + } else if (rval > 0) + rval = mmcputtree( ic35path ); + mdisconnect(); + return rval; +} + +/* delete file on IC35 MMCard +* ------------------------- +*/ +static int +ic35mmcdel( char * devname, char * ic35path ) +{ + int mmcnum; + int rval; + + /* check ic35path, check/set "MMCard" prefix and */ + /* convert '/' to '\\', lowercase to uppercase */ + if ( (mmcnum = _ic35mmcpath( ic35path, 0 )) < 0 ) { + _mmcpatherror( mmcnum, ic35path ); + return ERR; + } + + /* connect to IC35 in SyncStation via serial device */ + if ( (rval = mconnect( devname, NULL )) != OK ) + return rval; + /* get MMCard status (else delete MMCard file fails) */ + if ( (rval = mmc_status( mmcnum )) > 0 ) { + message( "delete %s", ic35path ); + rval = mmc_delfile( ic35path ); /* delete IC35 MMCard file */ + } else if ( rval == 0 ) { + message( "MMCard%d not present", mmcnum ); + rval = ERR; + } + /* disconnect from IC35 */ + mdisconnect(); + return rval; +} + + +/* -------------------- M A I N - program ----------------------------- */ +static void +versinfo( void ) +{ + printf( "IC35manager for GNU/Linux %s (%s)\n", pkgvers, pkgdate ); + printf( "%s\n", bldinfo ); + printf( + "Copyright (C) 2001 Thomas Schulz\n" + "This is free software; see the GNU General Public Licence version 2 in\n" + "the file named COPYING for copying conditions. There is NO warranty.\n" + ); +} +static void +usage( void ) +{ + static char * usetext[] = { +"usage: ic35mgr [option..] command [argument..]", +"commands and arguments are:", +" status [FILE] report IC35 status (and write to FILE)", +" backup FILE backup IC35 database to FILE", +" restore FILE restore IC35 database from FILE", +" mmcdir list contents of IC35 MMCard(s)", +" mmcdel IC35PATH delete MMCard file IC35PATH", +" mmcget IC35PATH [FILE] read FILE from IC35PATH on IC35 MMCard", +" mmcgettree [IC35PATH] read file tree starting at IC35PATH on IC35 MMCard", +" mmcput IC35PATH [FILE] write FILE to IC35PATH on IC35 MMCard", +" mmcputtree IC35PATH write file tree starting at IC35PATH to IC35 MMCard", +" IC35PATH e.g. MMCard1/ic35/app/reversi.app will be", +" converted to MMCard1\\IC35\\APP\\REVERSI.APP on IC35", +" filename FILE is taken from IC35PATH if absent", +"options in short and long form are:", +" -d DEV --device=DEV serial device with IC35 connected, default /dev/ic35", +" -b SIZE --blocksize=SIZE block size for mmcget / mmcput, default 5120 / 2048", +" -n NINC --nice=NINC lower process priority for restore,mmcput, default 2", +#ifndef NO_LOGSIM +" -l FILE --logfile=FILE log communications to FILE, default no logging", +" -s FILE --simfile=FILE simulate communication with IC35 using FILE", +" (create simulation FILE from logfile with 'ic35log')", +#endif +" -V --version show version information and exit", +" -h --help show this help and exit", + NULL + }; + char ** lptr; + + for ( lptr = usetext; *lptr != NULL; ++lptr ) + printf( "%s\n", *lptr ); +} + +int +main( int argc, char *argv[] ) +{ + static struct option const long_opts[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { "device", required_argument, NULL, 'd' }, + { "blocksize", required_argument, NULL, 'b' }, + { "nice", required_argument, NULL, 'n' }, +#ifndef NO_LOGSIM + { "logfile", required_argument, NULL, 'l' }, + { "simfile", required_argument, NULL, 's' }, +#endif + { NULL, 0, NULL, 0 } + }; + char * devname = "/dev/ic35"; + char * nicearg = "2"; + char * command = NULL; +#ifndef NO_LOGSIM + char * logfname = NULL; + char * simfname = NULL; +#endif + int rval; + + /* + * parse command line + */ + for ( ; ; ) { + switch ( getopt_long( argc, argv, +#ifndef NO_LOGSIM + "l:s:" +#endif + "d:b:hV", long_opts, NULL ) ) { + default: /* invalid option */ + fprintf( stderr, "use 'ic35mgr --help' for more information\n" ); + return 1; + case 'h': /* show Help and exit */ + usage(); + return 0; + case 'V': /* show Version and exit */ + versinfo(); + return 0; + case 'd': /* serial Device where IC35 */ + devname = optarg; + continue; + case 'b': /* buffer size for mmcget,put */ + blocksize = atoi( optarg ); + continue; + case 'n': /* nice processs priority */ + nicearg = optarg; + continue; +#ifndef NO_LOGSIM + case 'l': /* communication logfile */ + logfname = optarg; + continue; + case 's': /* simulate: no communication */ + simfname = optarg; + continue; +#endif /*NO_LOGSIM*/ + case -1: /* end of options */ + break; + } + break; + } + if ( argc < optind+1 ) { + fatal( "missing command\n" + "use 'ic35mgr --help' for more information" ); + return 1; + } + if ( atoi( nicearg ) == 0 && !isdigit(*nicearg) ) { + fatal( "non-integer nice argument: %s", nicearg ); + return 1; + } + com_waitnice( atoi( nicearg ) ); + + /* + * initial info to logfile: date,time, version, command line + * if 'logfname' is NULL or empty, nothing will be logged. + */ + LOG_INIT(( logfname, "ic35mgr", 0 )); + LOG_PROGINFO(( "ic35mgr", pkgvers, pkgdate, bldinfo )); + LOG_ARGSINFO(( argc, argv )); + + /* + * setup simulated communication if requested + */ + COM_SIMINIT(( simfname )); + + /* + * process command + */ + command = argv[optind]; /* note command */ + argv += optind, argc -= optind; /* skip options */ + rval = 1; + if ( strcmp( command, "status" ) == 0 ) { + if ( argc <= 2 ) + rval = ic35mgrstat( devname, argc > 1 ? argv[1] /*???*/ + : "/tmp/ic35.stat" ); + else + error( "too many arguments" ); + } else if ( strcmp( command, "backup" ) == 0 ) { + if ( argc == 2 ) + rval = ic35backup( devname, argv[1] ); + else + error( argc < 2 ? "missing backup file" + : "too many arguments" ); + } else if ( strcmp( command, "restore" ) == 0 ) { + if ( argc == 2 ) + rval = ic35restore( devname, argv[1] ); + else + error( argc < 2 ? "missing restore file" + : "too many arguments" ); + } else if ( strcmp( command, "mmcdir" ) == 0 ) { + if ( argc == 1 ) + rval = ic35mmcdir( devname ) == OK ? 0 : 1; + else + error( "too many arguments" ); + } else if ( strcmp( command, "mmcdel" ) == 0 ) { + if ( argc == 2 ) + rval = ic35mmcdel( devname, argv[1] ) == OK ? 0 : 1; + else + error( argc < 2 ? "missing ic35path" + : "too many arguments" ); + } else if ( strcmp( command, "mmcget" ) == 0 ) { + if ( argc == 2 || argc == 3 ) + rval = ic35mmcget( devname, argv[1], argc == 3 ? argv[2] : NULL ); + else + error( argc < 2 ? "missing ic35path" + : "too many arguments" ); + } else if ( strcmp( command, "mmcgettree" ) == 0 ) { + if ( argc == 1 || argc == 2 ) + rval = ic35mmcgettree( devname, argc == 2 ? argv[1] : NULL ); + else + error( "too many arguments" ); + } else if ( strcmp( command, "mmcput" ) == 0 ) { + if ( argc == 2 || argc == 3 ) + rval = ic35mmcput( devname, argv[1], argc == 3 ? argv[2] : NULL ); + else + error( argc < 2 ? "missing ic35path,file" + : "too many arguments" ); + } else if ( strcmp( command, "mmcputtree" ) == 0 ) { + if ( argc == 2 ) + rval = ic35mmcputtree( devname, argv[1]); + else + error( argc < 2 ? "missing ic35path,file" + : "too many arguments" ); + } else { + error( "unsupported command: %s", command ); + rval = 1; + } + if ( rval != OK ) + rval = 1; + + LOG_CLOSE(()); + return rval; +} diff --git a/src/ic35sync.c b/src/ic35sync.c new file mode 100644 index 0000000..7d120d8 --- /dev/null +++ b/src/ic35sync.c @@ -0,0 +1,980 @@ +/************************************************************************ +* Copyright (C) 2000,2001 Thomas Schulz * +* */ + static char rcsid[] = + "$Id: ic35sync.c,v 1.19 2001/03/02 02:10:42 tsch Rel $"; /* +* * +* IC35 synchronize PIM data * +* * +************************************************************************* +* * +* ??? is "fixme" mark: sections of code needing fixes * +* * +* conditional compile on NO_LOGSIM: if #defined, logging of the * +* IC35 communications as well as simulated communication are NOT * +* supported, default WITH logging and com-simulation support. * +* * +* for usage run ic35sync -h or see function usage() below. * +* * +************************************************************************/ + +#include /* printf(), .. */ +#include /* strcmp(), .. */ +#include /* getopt(), optarg, .. */ +#include /* struct tm, time() .. */ + +#include "util.h" /* FALSE, .. */ +#include "comio.h" /* com_siminit() */ +#include "syntrans.h" /* IC35sync transactions */ +#include "ic35frec.h" /* FILEADDR, .. */ +#include "dataio.h" /* IC35sync import/export */ +NOTUSED(rcsid) + + +extern char * pkgvers; /* these are all */ +extern char * pkgdate; /* in versinfo.c, which is */ +extern char * bldinfo; /* auto-generated by Makefile */ + + +static const int fileids[4] = { + FILEADDR, + FILEMEMO, + FILESCHED, + FILETODO + }; + + +/* report IC35 status +* ------------------ +* show firmware version, sysinfo (date+time), total and modified +* number of records per IC35 file. +*/ +static int +ic35status( char * devname, char * passwd ) +{ + int i, fileid; + int fd; + char * fname; + int nrec, nmod, nspc; + int rval; + char ic35dtime[16+1]; + + if ( (rval = connect( devname, passwd, ic35dtime )) != OK ) + return rval; + + for ( i = 0; i < sizeof(fileids)/sizeof(fileids[0]); ++i ) { + fileid = fileids[i]; + rval = ERR; + if ( (fname = ic35fname( fileid )) == NULL ) { + error( "status failed: bad fileid %02X", fileid ); + break; + } + LPRINTF(( L_INFO, "status fid=%02X \"%s\" ..", fileid, fname )); + if ( (fd = open_file( ic35fname( fileid ) )) >= 0 ) { + if ( (nrec = get_flen( fd )) >= 0 ) { + if ( (nmod = get_mod_flen( fd )) >= 0 ) { + if ( (nspc = 10 - strlen( fname )) < 0 ) nspc = 0; + message( "status \"%s\"%*s total %2d modified %2d records", + fname, nspc, "", nrec, nmod ); + rval = OK; + } else + error( "status %s failed: get_mod_flen failed", fname ); + } else + error( "status %s failed: get_flen failed", fname ); + close_file( fd ); + } else + error( "status %s failed: open_file failed", fname ); + LPRINTF(( L_INFO, "status fid=%02X \"%s\" %s", fileid, fname, + rval == OK ? "OK" : "ERR" )); + if ( rval != OK ) + break; + } + + disconnect(); + return rval; +} + + +/* run IC35 export session +* ----------------------- +* read all IC35 records and export (i.e. add/update) to PIMfile: +* - set modified+created time in PIMrecord if added IC35 record +* - set modified time in PIMrecord if updated from IC35 record +* - do not change modified time in PIMrecord if same as IC35 record +* - set PIMrecord status to CLEAN if added/updated/same as IC35 +* - set IC35 record-ID to added/updated PIMfile records +* changeflags of records in IC35 will not be reset, +* records in IC35 will not be modified/deleted, and +* sysinfo (date+time) will not be written to IC35. +* IC35 overrides PIMfile, PIMfile records not in IC35 remain untouched. +*/ +static int +exportfile( int fileid ) +{ + char * fname; + int fd, nrec, irec; + IC35REC * rec; + int rval; + + if ( (fname = ic35fname( fileid )) == NULL ) { + error( "export failed: bad fileid %02X", fileid ); + return ERR; + } + LPRINTF(( L_INFO, "exportfile(fid=%02X) \"%s\" ..", fileid, fname )); + if ( (rec = new_ic35rec()) == NULL ) { + error( "export %s failed: no memory", fname ); + return ERR; + } + if ( (fd = open_file( ic35fname( fileid ) )) < 0 ) { + error( "export %s failed: open_file failed", fname ); + del_ic35rec( rec ); + return ERR; + } + if ( (nrec = get_flen( fd )) < 0 ) { + error( "export %s failed: get_flen failed", fname ); + close_file( fd ); + del_ic35rec( rec ); + return ERR; + } + message( "export \"%s\", %d records", fname, nrec ); + rval = OK; + for ( irec = 0; irec < nrec; ++irec ) { + if ( read_frec( fd, irec, rec ) < 0 ) { + rval = ERR; + break; + } + pim_putic35rec( rec ); + } + close_file( fd ); + del_ic35rec( rec ); + LPRINTF(( L_INFO, "exportfile(fid=%02X) \"%s\" %s", + fileid, fname, rval == OK ? "OK" : "ERR" )); + return rval; +} +static int +ic35export( char * devname, char * passwd ) +{ + int i, rval; + + if ( (rval = connect( devname, passwd, NULL )) != 0 ) + return rval; + + for ( i = 0; i < sizeof(fileids)/sizeof(fileids[0]); ++i ) + if ( (rval = exportfile( fileids[i] )) != OK ) + break; + + disconnect(); + return rval; +} + + +/* maintain IC35 record table for import,sync +* ------------------------------------------ +*/ +enum ic35op { /* IC35 record operation */ + IC35NOACT, /* 0 no action */ + IC35DELETE, /* 1 delete record */ + IC35WRITE, /* 2 write new record */ + IC35UPDATE, /* 3 re-write existing record */ + IC35COMMIT, /* 4 commit clears changeflag */ +}; +struct recop { /* IC35 record,op entry */ + IC35REC * rec; /* ptr to IC35 record */ + enum ic35op op; /* record operation to do */ + void * pim; /* ptr to PIM record */ +}; + +static struct recop * _ic35rectab; +static size_t _ic35rectablen; + +#define ADD_CHUNKS 64 + +/* put IC35 record and action into table +* ------------------------------------- +* if record already in table, just update action +* if free entry available, put record,action there +* if table full or not allocated yet, make it bigger +* in chunks of +*/ +static void +_put_ic35recs( IC35REC * ic35rec, enum ic35op action, void * pimrec ) +{ + int i; + struct recop * ntab; + size_t nlen; + + for ( i = 0; i < _ic35rectablen; ++i ) { + if ( _ic35rectab[i].rec == ic35rec ) { + _ic35rectab[i].op = action; + _ic35rectab[i].pim = pimrec; + return; + } + if ( _ic35rectab[i].rec == NULL ) + break; + } + if ( i >= _ic35rectablen ) { + nlen = _ic35rectablen + ADD_CHUNKS; + if ( (ntab = realloc( _ic35rectab, nlen * sizeof(ntab[0]) )) == NULL ) + return; + memset( ntab+_ic35rectablen, 0, ADD_CHUNKS * sizeof(ntab[0]) ); + _ic35rectab = ntab; + _ic35rectablen = nlen; + } + _ic35rectab[i].rec = ic35rec; + _ic35rectab[i].op = action; + _ic35rectab[i].pim = pimrec; +} +/* get IC35 record and action from table +* ------------------------------------- +* if non-zero 'recid' lookup IC35 record with that record-ID, +* otherwise just retrieve first non-NULL record in table. +* pass action associated with record to non-NULL 'paction'. +* the record will be removed from table ! +*/ +static IC35REC * +_get_ic35recs( ulong recid, enum ic35op * paction, void ** ppimrec ) +{ + int i; + IC35REC * rec; + + for ( i = 0; i < _ic35rectablen; ++i ) { + if ( (rec = _ic35rectab[i].rec) == NULL ) + continue; + if ( recid == 0 || ic35recid( rec ) == recid ) { + if ( paction ) *paction = _ic35rectab[i].op; + if ( ppimrec ) *ppimrec = _ic35rectab[i].pim; + _ic35rectab[i].op = IC35NOACT; + _ic35rectab[i].rec = NULL; + return rec; + } + } + return NULL; +} +/* delete IC35 record table +* ------------------------ +* release all non-NULL records in table, then release table +* return number of records yet in table, used by "import" to +* detect records in IC35 but not in PIMfile. +*/ +static int +_del_ic35recs( void ) +{ + int i; + int only_ic35; + + for ( i = only_ic35 = 0; i < _ic35rectablen; ++i ) + if ( _ic35rectab[i].rec != NULL ) { + ++only_ic35; /* IC35 record not in PIMfile */ + del_ic35rec( _ic35rectab[i].rec ); + } + free( _ic35rectab ); + _ic35rectab = NULL; + _ic35rectablen = 0; + return only_ic35; /* num.of records not in PIMfile */ +} + + +/* run IC35 import session +* ----------------------- +* read all records from IC35 and compare with PIMfile +* - update PIMrecord to IC35 if same record-ID on IC35 +* - write PIMrecord to IC35 if record not on IC35 +* and write back new IC35 record-ID to PIMrecord +* - reset IC35 record changeflag if same as PIMrecord +* - set PIMrecord status to CLEAN +* - owner address data must be written with update_frec() and recID 1 +* otherwise on IC35 in category "Address", but not as owner data +* do not delete records neither in IC35 nor in PIMfile +* write sysinfo (date+time) if all IC35 records match PIMfile +* PIMfile overrides IC35, IC35 records not in PIMfile remain untouched, +* sysinfo (date+time) is not written if any IC35 record not in PIMfile. +*/ +static int +importfile( int fileid, char * ic35dtime ) +{ + char * fname; + int fd, nrec, irec; + IC35REC * ic35rec; + void * pimrec; + ulong recid; + int n_write, n_update, n_commit; + int ic35_not_in_PIM; + enum ic35op action; + char * actext; + int rc, rval; + + if ( (fname = ic35fname( fileid )) == NULL ) { + error( "import failed: bad fileid %02X", fileid ); + return ERR; + } + LPRINTF(( L_INFO, "importfile(fid=%02X) \"%s\" ..", fileid, fname )); + if ( (fd = open_file( ic35fname( fileid ) )) < 0 ) { + error( "import %s failed: open_file failed", fname ); + return ERR; + } + if ( (nrec = get_flen( fd )) < 0 ) { + error( "import %s failed: get_flen failed", fname ); + close_file( fd ); + return ERR; + } + /* + * read all IC35-records and note them + */ + message( "import \"%s\", read %d records", fname, nrec ); + rval = OK; + for ( irec = 0; irec < nrec; ++irec ) { + if ( (ic35rec = new_ic35rec()) == NULL ) { + error( "import %s failed: no memory for ic35rec", fname ); + rval = ERR; + break; + } + if ( read_frec( fd, irec, ic35rec ) < 0 ) { + rval = ERR; + break; + } + _put_ic35recs( ic35rec, IC35NOACT, NULL ); + } + if ( rval != OK ) { + close_file( fd ); + LPRINTF(( L_INFO, "importfile(fid=%02X) \"%s\" rval=ERR", fileid,fname )); + return rval; + } + /* + * compare all PIM-records with IC35-records, and do: + * - if PIM-record not in IC35, write it to IC35 + * and write back record-ID from IC35 to PIM-record, + * - if different update IC35-record with PIM-record, + * - if same commit to IC35 if non-zero changeflag in IC35. + * mark all PIM-records CLEAN as they are now same as IC35. + * collect IC35 records with actions to do. + */ + n_write = n_update = n_commit = 0; + pim_rewind(); + while ( (pimrec = pim_getrec( fileid )) != NULL ) { + if ( (recid = pim_recid( pimrec )) != 0 /* PIM record-ID on IC35 */ + && (ic35rec = _get_ic35recs( recid, NULL, NULL )) != NULL ) { + if ( pim_cmpic35rec( ic35rec, pimrec ) == 0 ) { + if ( ic35recchg( ic35rec ) == IC35_CLEAN ) { + del_ic35rec( ic35rec ); /* same as PIM, unchanged */ + continue; /* no need for commit */ + } + action = IC35COMMIT; /* same as PIM, commit it */ + ++n_commit; + } else { + action = IC35UPDATE; /* different, update rec */ + ++n_update; + } + } else { /* PIMrecord not on IC35 */ + ic35rec = new_ic35rec(); + if ( recid == FileRecId(FILEADDR,1) ) { + action = IC35UPDATE; /* must update owner-addr */ + ++n_update; + } else { + action = IC35WRITE; /* write new IC35 record */ + ++n_write; + } + } + pim_updic35rec( ic35rec, pimrec ); /* PIM update/new to IC35 */ + _put_ic35recs( ic35rec, action, pimrec ); /* add action and PIMrec */ + } + /* + * execute actions on IC35 records collected above. + * count records with no action, i.e. only existing + * on IC35, but not in PIMfile. + */ + message( "import \"%s\", write %d update %d commit %d records", + fname, n_write, n_update, n_commit ); + ic35_not_in_PIM = 0; + while ( (ic35rec = _get_ic35recs( 0, &action, &pimrec )) != NULL ) { + rc = 0; actext = ""; + switch ( action ) { + case IC35COMMIT: + actext = "commit"; + rc = commit_frec( fd, ic35recid( ic35rec ) ); + break; + case IC35UPDATE: + actext = "update"; + rc = update_frec( fd, ic35rec ); + break; + case IC35WRITE: + actext = "write"; + rc = write_frec( fd, ic35rec ); + break; + default: + ++ic35_not_in_PIM; + continue; + } + if ( rc >= 0 ) { /* commit/update/write success */ + if ( pimrec != NULL ) { + pim_set_recid( pimrec, ic35recid( ic35rec ) ); + pim_set_recstat( pimrec, PIM_CLEAN ); + } + } else { /* commit/update/write failed */ + error( "import %s failed: %s_frec failed", fname, actext ); + rval = ERR; + } + del_ic35rec( ic35rec ); + } + _del_ic35recs(); + if ( rval == OK ) + rval = ic35_not_in_PIM; /* any IC35 records not in PIMfile */ + close_file( fd ); + LPRINTF(( L_INFO, "importfile(fid=%02X) \"%s\" rval=%d", + fileid, fname, rval )); + return rval; +} +static int +ic35import( char * devname, char * passwd ) +{ + int i, rc, rval; + bool ic35_same_as_PIM; + char ic35dtime[16+1]; + + if ( (rval = connect( devname, passwd, ic35dtime )) != 0 ) + return rval; + set_oldic35dt( ic35dtime ); + + rval = OK; + ic35_same_as_PIM = TRUE; + for ( i = 0; i < sizeof(fileids)/sizeof(fileids[0]); ++i ) + if ( (rc = importfile( fileids[i], ic35dtime )) != OK ) { + ic35_same_as_PIM = FALSE; + if ( rc < 0 ) { + rval = rc; + break; + } + } + + if ( ic35_same_as_PIM ) { + get_newic35dt( ic35dtime ); + WriteSysInfo( ic35dtime ); + } + + disconnect(); + return rval; +} + + +/* run IC35 sync session +* --------------------- +* update PIMfile with IC35 changes (modify/delete) and vice versa +* - phase-0: initial export if PIMfile does not contains any records +* otherwise phase-3 below would delete all unmodified records on IC35, +* as they are regarded deleted from PIMfile. +* - phase-1a: update records modified/deleted on IC35 to PIMfile +* read all modified records from IC35, if unchanged in PIMfile: +* - delete from PIMfile if deleted on IC35, +* - update from IC35 to PIMfile if modified on IC35. +* note IC35-recID for commit (commit now would disturb read_mod_frec) +* conflict if record changed in IC35 AND also in PIMfile, resolve: +* - PIMfile overrides IC35: update from PIMrecord to IC35record +* will be done in next phase below (now would disturb read_mod_frec), +* - IC35 overrides PIMfile: update from IC35record to PIMrecord +* ??? ? add changed record on IC35/PIM to PIM/IC35 with conflict mark +* - phase-1b: commit handled IC35 changes to IC35 +* commits noted above to avoid disturbing read_mod_frec, now do them. +* - phase-2: update records modified in PIMfile to IC35 +* - if record exists on IC35, i.e. has record-ID, +* read record by ID from IC35, update with PIMrecord +* write record to IC35 +* - if record is new in PIMfile (no record-ID) +* create IC35 record from PIMrecord, write to IC35 +* and writeback new IC35 record-ID to PIMrecord +* - phase-3a: records deleted from PIMfile are detected by reading +* all records from IC35 and check if record in PIMfile +* ??? sanity check IC35 changeflags clean +* - PIMfile overrides IC35: note IC35-recID for delete +* (delete now would disturb read_frec) +* - IC35 overrides PIMfile: re-create PIMrecord from IC35 record +* - phase-3b: delete records on IC35 +* deletes were noted above to avoid disturbing read_frec, now do them. +* now there should be no more pending changes neither in IC35 nor in PIM. +* write sysinfo (date+time) as now all IC35 records (shall) match PIMfile +*/ +static int +syncfile( int fileid, char * ic35dtime ) +{ + char * fname; + bool initexp; + int fd, nrec, irec; + IC35REC * ic35rec; + void * pimrec; + ulong recid; + enum ic35op action; + int n_commit, n_delete; + int rc, rval; + + if ( (fname = ic35fname( fileid )) == NULL ) { + error( "sync failed: bad fileid %02X", fileid ); + return ERR; + } + /* + * phase-0: initial export if PIMfile does not contains any records + * otherwise phase-3 below would delete all unmodified records on IC35 + */ + initexp = FALSE; + pim_rewind(); + if ( pim_getrec( fileid ) == NULL ) { + message( "sync \"%s\", no PIM records: initial export", fname ); + if ( (rval = exportfile( fileid )) != OK ) + return rval; + initexp = TRUE; + } + + LPRINTF(( L_INFO, "syncfile(fid=%02X) \"%s\" ..", fileid, fname )); + if ( (fd = open_file( ic35fname( fileid ) )) < 0 ) { + error( "sync %s failed: open_file failed", fname ); + return ERR; + } + /* + * phase-1a: update records modified/deleted on IC35 to PIMfile + * read all modified records from IC35, if unchanged in PIMfile: + * - delete from PIMfile if deleted on IC35, + * - update from IC35 to PIMfile if modified on IC35. + * note IC35-recID for commit (commit now would disturb read_mod_frec) + * conflict if record changed in IC35 AND also in PIMfile, resolve: + * - PIMfile overrides IC35: update from PIMrecord to IC35record + * will be done in next phase below (now would disturb read_mod_frec), + * - IC35 overrides PIMfile: update from IC35record to PIMrecord + * ? add changed record on IC35/PIM to PIM/IC35 with conflict mark ??? + */ + if ( (nrec = get_mod_flen( fd )) < 0 ) { + error( "sync %s failed: get_mod_flen failed", fname ); + close_file( fd ); + return ERR; + } + message( "sync \"%s\", read %d modified records%s", + fname, nrec, nrec > 0 ? ", update PIM" : "" ); + n_commit = 0; + rval = OK; + for ( irec = 0; irec < nrec; ++irec ) { + if ( (ic35rec = new_ic35rec()) == NULL ) { + error( "sync %s failed: no memory", fname ); + rval = ERR; + break; + } + if ( read_mod_frec( fd, ic35rec ) < 0 ) { + error( "sync %s failed: read_mod_frec failed", fname ); + rval = ERR; + break; + } + if ( (pimrec = pim_getrec_byID( ic35recid( ic35rec ) )) == NULL + || pim_recstat( pimrec ) == PIM_CLEAN ) { + switch ( ic35recchg( ic35rec ) ) { + case IC35_NEW: + case IC35_MOD: + pim_putic35rec( ic35rec ); + break; + case IC35_DEL: + if ( pimrec ) + pim_delrec( pimrec ); + break; + } + _put_ic35recs( ic35rec, IC35COMMIT, NULL ); + ++n_commit; + } else { + ; /*??? CONFLICT: changed on both sides */ + } + } + /* + * phase-1b: commit handled IC35 changes to IC35 + * commits noted above to avoid disturbing read_mod_frec, now do them. + */ + if ( rval == OK && n_commit > 0 ) { + message( "sync \"%s\", commit %d IC35 records", fname, n_commit ); + while ( (ic35rec = _get_ic35recs( 0, &action, NULL )) != NULL ) { + if ( action == IC35COMMIT + && commit_frec( fd, ic35recid( ic35rec ) ) < 0 ) { + error( "sync %s failed: commit_frec failed", fname ); + rval = ERR; + break; + } + del_ic35rec( ic35rec ); + } + } + _del_ic35recs(); + if ( rval != OK ) { + close_file( fd ); + LPRINTF(( L_INFO, "syncfile(fid=%02X) \"%s\" rval=ERR", fileid,fname )); + return rval; + } + /* + * if initial export was done, update from PIM to IC35 and + * check records removed from PIM and delete is not needed. + */ + if ( initexp ) { + close_file( fd ); + LPRINTF(( L_INFO, "syncfile(fid=%02X) \"%s\" rval=OK", fileid, fname )); + return rval; + } + /* + * phase-2: update records modified in PIMfile to IC35 + * - if record exists on IC35, i.e. has record-ID, + * read record by ID from IC35, update with PIMrecord + * write record to IC35 + * - if record is new in PIMfile (no record-ID) + * create IC35 record from PIMrecord, write to IC35 + * and writeback new IC35 record-ID to PIMrecord + */ + if ( (ic35rec = new_ic35rec()) == NULL ) { + close_file( fd ); + error( "sync %s failed: no memory", fname ); + return ERR; + } + message( "sync \"%s\", update IC35", fname ); + pim_rewind(); + while ( rval == OK && (pimrec = pim_getrec( fileid )) != NULL ) { + switch ( pim_recstat( pimrec ) ) { + case PIM_CLEAN: + continue; + case PIM_DEL: + if ( (recid = pim_recid( pimrec )) != 0 ) { + if ( delete_frec( fd, recid ) >= 0 ) { + pim_delrec( pimrec ); + } else { + error( "sync %s failed: delete_frec failed", fname ); + rval = ERR; + } + } + continue; + default: + error( "bad PIM record status: %d", pim_recstat( pimrec ) ); + /* full through and handle as PIM_DIRTY */ + case PIM_DIRTY: + if ( (recid = pim_recid( pimrec )) != 0 ) { /* PIMrec on IC35 */ + if ( (rc = read_id_frec( fd, recid, ic35rec )) < 0 ) { + error( "sync %s failed: read_id_frec failed", fname ); + rval = ERR; + break; + } + if ( rc > 0 ) { /* PIMrec found on IC35 */ + pim_updic35rec( ic35rec, pimrec ); + if ( update_frec( fd, ic35rec ) >= 0 ) { + pim_set_recstat( pimrec, PIM_CLEAN ); + } else { + error( "sync %s failed: update_frec failed", fname ); + rval = ERR; + } + } + break; + } /* PIMrecord not on IC35 */ + set_ic35recdata( ic35rec, NULL, 0 ); + pim_updic35rec( ic35rec, pimrec ); + if ( write_frec( fd, ic35rec ) >= 0 ) { + pim_set_recid( pimrec, ic35recid( ic35rec ) ); + pim_set_recstat( pimrec, PIM_CLEAN ); + } else { + error( "sync %s failed: write_frec failed", fname ); + rval = ERR; + } + break; + } + } + del_ic35rec( ic35rec ); ic35rec = NULL; + if ( rval != OK ) { + close_file( fd ); + LPRINTF(( L_INFO, "syncfile(fid=%02X) \"%s\" rval=ERR", fileid,fname )); + return rval; + } + /* + * phase-3a: records deleted from PIMfile are detected by reading + * all records from IC35 and check if record in PIMfile + * ? sanity check IC35 changeflags clean ??? + * - PIMfile overrides IC35: note IC35-recID for delete + * (delete now would disturb read_frec) + * - IC35 overrides PIMfile: re-create PIMrecord from IC35 record + */ + if ( (nrec = get_flen( fd )) < 0 ) { + error( "sync %s failed: get_flen failed", fname ); + close_file( fd ); + return ERR; + } + message( "sync \"%s\", read %d records, check PIM deleted", fname, nrec ); + n_delete = 0; + for ( irec = 0; irec < nrec; ++irec ) { + if ( (ic35rec = new_ic35rec()) == NULL ) { + error( "sync %s failed: no memory", fname ); + rval = ERR; + break; + } + if ( read_frec( fd, irec, ic35rec ) < 0 ) { + error( "sync %s failed: read_frec failed", fname ); + rval = ERR; + break; + } + if ( pim_getrec_byID( ic35recid( ic35rec ) ) ) { + del_ic35rec( ic35rec ); + continue; + } + /*??? sanity check ic35recchg() == 0 ? */ + /*??? if PIM overrides IC35 */ + _put_ic35recs( ic35rec, IC35DELETE, NULL ); + ++n_delete; + /*??? if IC35 overrides PIM */ + /*??? pim_putic35rec( ic35rec ); */ + } + /* + * phase-3b: delete records on IC35 + * deletes were noted above to avoid disturbing read_frec, now do them. + */ + if ( rval == OK && n_delete ) { + message( "sync \"%s\", delete %d IC35 records", fname, n_delete ); + while ( (ic35rec = _get_ic35recs( 0, &action, NULL )) != NULL ) { + if ( action == IC35DELETE + && delete_frec( fd, ic35recid( ic35rec ) ) < 0 ) { + error( "sync %s failed: delete_frec failed", fname ); + rval = ERR; + break; + } + del_ic35rec( ic35rec ); + } + } + _del_ic35recs(); + + close_file( fd ); + LPRINTF(( L_INFO, "syncfile(fid=%02X) \"%s\" rval=%d", + fileid, fname, rval )); + return rval; +} +static int +ic35sync( char * devname, char * passwd ) +{ + int i, rval; + char ic35dtime[16+1]; + + if ( (rval = connect( devname, passwd, ic35dtime )) != 0 ) + return rval; + set_oldic35dt( ic35dtime ); + + for ( i = 0; i < sizeof(fileids)/sizeof(fileids[0]); ++i ) { + if ( (rval = syncfile( fileids[i], ic35dtime )) != OK ) + break; + } + + if ( rval == OK ) { + get_newic35dt( ic35dtime ); + WriteSysInfo( ic35dtime ); + } + + disconnect(); + return rval; +} + + +/* -------------------- M A I N - program ----------------------------- */ +static void +versinfo( void ) +{ + printf( "IC35sync for GNU/Linux %s (%s)\n", pkgvers, pkgdate ); + printf( "%s\n", bldinfo ); + printf( + "Copyright (C) 2000,2001 Thomas Schulz\n" + "This is free software; see the GNU General Public Licence version 2 in\n" + "the file named COPYING for copying conditions. There is NO warranty.\n" + ); +} +static void +usage( void ) +{ + static char * usetext[] = { +"usage: ic35sync [option..] command [pimfile..]", +"commands are:", +" sync synchronize IC35 with pimfile(s) data", +" export read all data from IC35 and write to pimfile(s)", +" import write to IC35 all data read from pimfile(s)", +" status report IC35 status (no pimfile access)", +"pimfile contains local Personal Information Management data,", +"e.g. in vCard/vCalendar format.", +"options in short and long form are:", +" -d DEV --device=DEV serial device with IC35 connected, default /dev/ic35", +" -p TEXT --password=TEXT password TEXT on IC35, default none", +" -f FORM --format=FORM format of pimfile(s), FORM and default pimfile(s):", +" vca (default) vCard,vCalendar,Memo, ic35.vcard ic35.vcal ic35.memo", +" txt (export only) plain text, ic35.txt", +" bin raw binary data, ic35.bin", +#ifndef NO_LOGSIM +" -l FILE --logfile=FILE log communications to FILE, default no logging", +" -s FILE --simfile=FILE simulate communication with IC35 using FILE", +" (create simulation FILE from logfile with 'ic35log')", +#endif +" -V --version show version information and exit", +" -h --help show this help and exit", +NULL + }; + char ** lptr; + + for ( lptr = usetext; *lptr != NULL; ++lptr ) + printf( "%s\n", *lptr ); +} + +int +main( int argc, char *argv[] ) +{ + static struct option const long_opts[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { "device", required_argument, NULL, 'd' }, + { "password", required_argument, NULL, 'p' }, + { "format", required_argument, NULL, 'f' }, +#ifndef NO_LOGSIM + { "logfile", required_argument, NULL, 'l' }, + { "simfile", required_argument, NULL, 's' }, +#endif + { NULL, 0, NULL, 0 } + }; + char * passwd = ""; + char * devname = "/dev/ic35"; + char * pimfmtstr = "vca"; + char * command = NULL; + char * pimaddrfname = NULL; + char * pimvcalfname = NULL; + char * pimmemofname = NULL; +#ifndef NO_LOGSIM + char * logfname = NULL; + char * simfname = NULL; +#endif + int rval; + + /* + * parse command line + */ + for ( ; ; ) { + switch ( getopt_long( argc, argv, +#ifndef NO_LOGSIM + "l:s:" +#endif + "d:p:f:hV", long_opts, NULL ) ) { + default: + fprintf( stderr, "use 'ic35sync --help' for more information\n" ); + return 1; + case 'h': /* show Help and exit */ + usage(); + return 0; + case 'V': /* show Version and exit */ + versinfo(); + return 0; + case 'd': /* serial Device where IC35 */ + devname = optarg; + continue; + case 'p': /* password on IC35 */ + passwd = optarg; + continue; + case 'f': /* format of PIMfile */ + pimfmtstr = optarg; + continue; +#ifndef NO_LOGSIM + case 'l': /* communication logfile */ + logfname = optarg; + continue; + case 's': /* simulate: no communication */ + simfname = optarg; + continue; +#endif/*NO_LOGSIM*/ + case -1: /* end of options */ + break; + } + break; + } + if ( argc < optind+1 ) { + error( "missing command\n" + "use 'ic35sync --help' for more information" ); + return 1; + } + command = argv[optind]; + + /* + * initial info to logfile: date,time, version, command line + * if 'logfname' is NULL or empty, nothing will be logged. + */ + LOG_INIT(( logfname, "ic35sync", 0 )); + LOG_PROGINFO(( "ic35sync", pkgvers, pkgdate, bldinfo )); + LOG_ARGSINFO(( argc, argv )); + + /* + * setup simulated communication if requested + */ + COM_SIMINIT(( simfname )); + + /* + * status command does not use pimfile(s) + */ + if ( strcmp( command, "status" ) == 0 ) { + rval = ic35status( devname, passwd ) == OK ? 0 : 1; + LOG_CLOSE(()); + return rval; + } + + /* + * defaults for filenames are: + * - no filename + * use defaults depending on format + * - text format ic35.txt + * - binary format ic35.bin + * - vCard,vCal format ic35.vcard, ic35.vcal, ic35.memo + * - 1 filename + * Addresses, Schedule,ToDo, Memo all in 1 file + * - 2 filenames + * Addresses to 1st file, Schedule,ToDo,Memo to 2nd file + * - 3 filenames + * Addresses to 1st file, Schedule,ToDo to 2nd, Memo to 3rd + */ + if ( set_pim_format( pimfmtstr ) != OK ) { + error( "unknown format: %s", pimfmtstr ); + return 1; + } + argv += optind, argc -= optind; /* skip to non-option arguments */ + switch ( argc ) { + default: + error( "max. 3 pimfiles supported, have %d", argc - 1 ); + return 1; + case 4: /* 3 filenames */ + pimmemofname = argv[3]; + /* fall through */ + case 3: /* 2 filenames */ + pimvcalfname = argv[2]; + /* fall through */ + case 2: /* 1 filename */ + pimaddrfname = argv[1]; + break; + case 1: /* no filename */ + switch ( pim_format() ) { + case PIM_TXT: + pimaddrfname = "ic35.txt"; + break; + case PIM_BIN: + pimaddrfname = "ic35.bin"; + break; + case PIM_VCA: + pimaddrfname = "ic35.vcard"; + pimvcalfname = "ic35.vcal"; + pimmemofname = "ic35.memo"; + break; + } + break; + } + if ( pim_open( pimaddrfname, pimvcalfname, pimmemofname ) != OK ) { + error( "failed to open pimfile(s): %s %s %s\n", + pimaddrfname, + pimvcalfname ? pimvcalfname : "", + pimmemofname ? pimmemofname : "" ); + return 1; + } + + /* + * process command + */ + if ( strcmp( command, "export" ) == 0 ) { + rval = ic35export( devname, passwd ) == OK ? 0 : 1; + } else if ( strcmp( command, "import" ) == 0 ) { + rval = ic35import( devname, passwd ) == OK ? 0 : 1; + } else if ( strcmp( command, "sync" ) == 0 ) { + rval = ic35sync( devname, passwd ) == OK ? 0 : 1; + } else { + error( "unsupported command: %s", command ); + rval = 1; + } + + pim_close(); + LOG_CLOSE(()); + return rval; +} diff --git a/src/mgrproto.c b/src/mgrproto.c new file mode 100644 index 0000000..9c9a107 --- /dev/null +++ b/src/mgrproto.c @@ -0,0 +1,951 @@ +/************************************************************************ +* Copyright (C) 2001 Thomas Schulz * +* */ + static char rcsid[] = + "$Id: mgrproto.c,v 1.17 2001/11/20 23:08:35 thosch Exp $"; /* +* * +* IC35 manager protocol * +* * +************************************************************************* +* * +* ??? is "fixme" mark: sections of code needing fixes * +* * +* IC35 manager protocol * +* Mcmdrsp send command-byte, get+check response-byte * +* Msendblk send datablock, test checksum, ack/retry * +* Mrecvblk receive datablock, test checksum, ack/retry * +* MMCard access protocol * +* MMCsend send MMCard command using Msendblk * +* MMCrecv receive MMCard response using Mrecvblk * +* MMCard commands * +* MMCgetstatus check if MMCard present / absent * +* MMCgetlabel get MMCard label * +* MMCdiropen open MMCard directory * +* MMCdirgetlen get number of entries in MMCard directory * +* MMCdirread read MMCard directory entry * +* MMCdirclose close MMCard directory * +* MMCfiledel delete MMCard file * +* MMCfileopen open MMCard file * +* MMCfilestat get MMCard file attributes * +* MMCfileread read block from MMCard file * +* MMCfilewrite write block to MMCard file * +* MMCfileclose close MMCard file * +* * +************************************************************************/ + +#include /* malloc(), .. */ +#include /* memcpy(), .. */ + +#include "util.h" /* ERR,OK, uchar, .. */ +#include "comio.h" /* com_send(), .. */ +#include "genproto.h" /* putxxx(),getxxx() .. */ +#include "mgrproto.h" +NOTUSED(rcsid) + +#define MAXRETRY 5 /* max.retries for manager protocol */ + + +/* ==================================== */ +/* IC35 manager protocol */ +/* ==================================== */ + +/* send command-byte, get+check response-byte +* ------------------------------------------ +*/ +int +Mcmdrsp( uchar cmd, uchar rsp ) +{ + uchar rbyte; + int retry; + + for ( retry = 0; retry < MAXRETRY; ++retry ) { + com_send( &cmd, sizeof(cmd) ); + if ( com_recv( &rbyte, sizeof(rbyte) ) == sizeof(rbyte) + && rbyte == rsp ) + return OK; + } + return ERR; +} + +/* local: send/receive block and ack or nak,retry +* ---------------------------------------------- +* used by Msendblk(), Mrecvblk() for ack- or nak-handshake and retry, +* the trasnsmit function _sendblk(), _recvblk() is passed as function +* pointer 'pfxmit' and will set the appropriate timeout(s). +*/ +static int +_xmitblk( uchar * data, size_t dlen, int pfxmit(uchar* data, size_t dlen) ) +{ + int old_tmo; + int retry; + int rval = ERR; + + com_settimeout( old_tmo = com_settimeout( 0 ) ); + for ( retry = 0; retry < MAXRETRY+1; ++retry ) { + rval = (*pfxmit)( data, dlen ); /* xmit data, recv csum */ + if ( rval == dlen ) { /* datablock xmit OK */ + if ( Mcmdrsp( MCMDposack, MRSPgotack ) != OK ) + rval = ERR_acknak; + break; + } else if ( rval != ERR_recv /* bad checksum/length */ + && retry < MAXRETRY ) { /* and retries left */ + if ( Mcmdrsp( MCMDnegack, MRSPgotack ) != OK ) { + rval = ERR_acknak; + break; + } + continue; + } else /* recv.err / timeout */ + break; /* or out of retries */ + } + com_settimeout( old_tmo ); + return rval; +} + +/* send block and ack/retry +* ------------------------ +* send database block, receive checksum and test it, on success send +* acknowledge, receive ready response and return OK. +* on failure send nak and retry, after max.retries return ERR. +* -> (block of 'dlen' bytes) +* set timeout 10.0 sec +* <- cc_cc got them, checksum is cc_cc (arithmetic, LSB first) +* set timeout 3.0 sec +* -> 60 positive acknowledge +* <- A0 got ack +* on checksum mismatch send negative acknowledge: +* -> 62 negative acknowledge +* <- A0 got nak +* and restart send block. +* the long "(block of 'dlen' bytes)" must be sent with waiting! +*/ +static int +_sendblk( uchar * data, size_t dlen ) +{ + uchar rcsum[2]; + int lcsum; + ushort cksum; + int rval; + + com_sendw( data, dlen ); /* -> data (with wait) */ + com_settimeout( 10000 ); /* timeout 10.0 sec for chksum */ + if ( (lcsum = com_recv( rcsum, sizeof(rcsum) )) /* <- checksum */ + == sizeof(rcsum) + && (cksum = chksum( data, dlen )) + == getword( rcsum ) ) { + rval = dlen; + } else { + if ( lcsum == sizeof(rcsum) ) { /* checksum mismatch */ + LPRINTF(( L_NOISE, "_senddblk: chksum=%04X mismatch", cksum )); + rval = ERR_chksum; + } else /* recv.error / timeout */ + rval = ERR_recv; + } + LPRINTF(( L_NOISE, "_sendblk(data,%d) = %d", dlen, rval )); + com_settimeout( 3000 ); /* timeout 3.0 sec ack/nak resp */ + return rval; +} +int +Msendblk( uchar * data, size_t dlen ) +{ + return _xmitblk( data, dlen, _sendblk ); +} + +/* receive block and ack/retry +* --------------------------- +* receive data block, on success acknowledge and return OK. +* on failure send nak and retry, after max.retries return ERR. +* set timeout 2.0 sec +* <- (block of 'blen' bytes) cc_cc (arithmetic sum, LSB first) +* -> 60 positive acknowledge +* <- A0 got ack +* on mismatch of checksum or length, send negative acknowledge: +* -> 62 negative acknowledge +* <- A0 got nak +* and restart receive block. +*/ +static int +_recvblk( uchar * buff, size_t blen ) +{ + uchar rcsum[2]; + ushort cksum; + int lcsum; + int rval; + + rcsum[0] = rcsum[1] = (uchar)0x00; + com_settimeout( 2000 ); /* timeout 2.0 sec for data,ack */ + if ( (rval = com_recv( buff, blen )) >= 2 /* <- data */ + && (lcsum = com_recv( rcsum, sizeof(rcsum)) ) >= 0 ) { /* <- csum */ + if ( lcsum == 1 ) { + rcsum[1] = buff[--rval]; /* take .. */ + rcsum[0] = buff[--rval]; /* .. checksum bytes .. */ + } else if ( lcsum == 0 ) { + rcsum[1] = rcsum[0]; /* .. from .. */ + rcsum[0] = buff[--rval]; /* .. end of datablock */ + } + if ( (cksum = chksum( buff, rval )) != getword( rcsum ) ) { + LPRINTF(( L_NOISE, "_recvblk: chksum=%04X mismatch", cksum )); + rval = ERR_chksum; /* checksum mismatch */ + } + } else + rval = ERR_recv; /* too short for chksum */ + LPRINTF(( L_NOISE, "_recvblk(buff,%d) = %d", blen, rval )); + return rval; +} +int +Mrecvblk( uchar * buff, size_t blen ) +{ + return _xmitblk( buff, blen, _recvblk ); +} + + +/* ==================================== */ +/* MMCard access protocol */ +/* ==================================== */ +/* +* protocol of MMCard operations +* MMCsend +* use timeout 0.5 sec +* -> 15 are you there ? +* <- 90 yes i am here ! +* -> nn_nn expect nn_nn bytes from me +* <- E0 ok, send them +* sendretry: +* -> (block of nn_nn bytes) +* set timeout 10.0 sec +* <- cc_cc got them, checksum is cc_cc (arithmetic, LSB first) +* set timeout 3.0 sec +* -> 60 ack +* <- A0 got ack +* on checksum mismatch send negative acknowledge: +* -> 62 nak +* <- A0 got nak +* and restart at sendretry. +* the long "(block of nn_nn bytes)" must be sent with waiting! +* the blocklength nn_nn must be sent with wait before 1st byte! +* MMCrecv +* set timeout 5.0 sec +* <- nn_nn expect nn_nn bytes plus trailing checksum +* -> E0 ok, send them +* recvretry: +* <- (block of nn_nn bytes) cc_cc (arithmetic sum, LSB first) +* -> 60 ack +* <- A0 got ack +* on mismatch of checksum or length, send negative acknowledge: +* -> 62 nak +* <- A0 got nak +* and restart at recvretry. +*/ + +/* send MMCard command +* ------------------- +*/ +static int +MMCsend( uchar * sbuff, size_t slen ) +{ + uchar rchar; + uchar slbuff[2]; + int rval; + + if ( Mcmdrsp( MCMDmmcard, MRSPgotcmd ) == OK ) { /* -> 15 */ + putword( slbuff, slen ); /* <- 90 */ + com_sendw( slbuff, sizeof(slbuff) ); /* -> nn_nn (with wait) */ + if ( com_recv( &rchar, 1 ) == 1 /* <- E0 */ + && rchar == MRSPgotlen ) { + rval = Msendblk( sbuff, slen ); /* send cmd, ack/retry */ + if ( rval != slen ) /* Msendblk() bad length */ + rval = ERR; + } else /* com_recv() MRSPgotlen failed */ + rval = ERR; + } else /* Mcmdrsp() failed */ + rval = ERR; + return rval; +} + +/* receive MMCard response +* ----------------------- +*/ +static int +MMCrecv( uchar ** prbuff ) +{ + int old_tmo; + uchar rlbuff[2]; + size_t rblen; + uchar rspgotlen = MRSPgotlen; + uchar * rbuff; + int rval; + + old_tmo = com_settimeout( 5000 ); + if ( (rval = com_recv( rlbuff, sizeof(rlbuff) )) == sizeof(rlbuff) ) { + rblen = getword( rlbuff ); /* <- nn nn */ + if ( (rbuff = calloc( rblen, 1 )) != NULL ) { + com_send( &rspgotlen, 1 ); /* -> E0 */ + rval = Mrecvblk( rbuff, rblen ); /* recv resp, ack/retry */ + if ( rval == rblen ) { + *prbuff = rbuff; /* OK, buffer to caller */ + } else { + free( rbuff ); /* Mrecvblk() bad length */ + rval = ERR; + } + } else { /* calloc(rblen,1) failed */ + LPRINTF(( L_ERROR, "MMCrecv: calloc(%d,1) failed", rblen )); + rval = ERR; + } + } else /* com_recv(rlbuff,) failed */ + rval = ERR; + com_settimeout( old_tmo ); + return rval; +} + + +/* ============================ */ +/* MMCard commands */ +/* ============================ */ + +/* +* MMCard command implementation +* the MMCard commands correspond to IC35 SDK API functions and are +* implemented as remote procedure calls: encode arguments to command +* PDU and send it to IC35, receive response PDU from IC35 and decode +* results from PDU. +* struct mcmdxxxx/mrspxxxx define the command/response PDU encoding. +* all MMCxxxx() functions work like: +* - _init*pdu() allocates the command PDU buffer and encodes the +* command code into it +* - encode MMCxxxx() function argument(s) into command PDU buffer +* (except fdstat argument) +* - _sendrecv() optionally encodes the MMCxxxx() fdstat argument +* into the command PDU for file/directory commands, +* sends the command PDU to IC35 with MMCsend() and releases it, +* receives the response PDU from IC35 with MMCrecv(), +* optionally decodes FILE_IDEN from response PDU back into the +* fdstat argument, +* and returns the status decoded from the response PDU. +* - decode MMCxxxx() result arguments +* - decode from the response PDU into MMCxxxx() result arguments, +* release response PDU buffer and return status from _sendrecv(). +*/ + +/* MMCard command codes */ /* IC35 SDK API function: */ +#define MMCMDgetstatus 0x20 /* 13.1 mInitialCard() */ +#define MMCMDgetlabel 0x34 /* 13.4 mGetCardLabel() */ +#define MMCMDdiropen 0x2A /* 13.14 mOpenDirectory() */ +#define MMCMDdirgetlen 0x2B /* 13.15 mGetDirectorySubItemNum() */ +#define MMCMDdirread 0x2C /* 13.16 mGetDirectorySubItem() */ +#define MMCMDdirclose 0x2E /* 13.18 mCloseDirectory() */ +#define MMCMDfileopen 0x22 /* 13.6 mOpenFile() */ +#define MMCMDfilestat 0x26 /* 13.10 mGetFileInfo() */ +#define MMCMDfilewrite 0x23 /* 13.8 mWriteToFile() */ +#define MMCMDfileread 0x24 /* 13.9 mReadFromFile() */ +#define MMCMDfileclose 0x27 /* 13.11 mCloseFile() */ +#define MMCMDfiledel 0x28 /* 13.13 mDeleteFile() */ + +/* MMCard PDU definitions +* ---------------------- +*/ +/* getstatus, getlabel */ +struct mcmdstatlbl { /* stat label */ + char cmd[1]; /* 20 or 34 */ + char mmcard[6]; /* "MMCard" */ + char mmcnum[1]; /* "1" or "2" */ + uchar zero[1]; /* 00 */ +}; +struct mrspstat { + uchar stat[2]; /* 01 00 or FF FF */ +}; +struct mrsplbl { + uchar stat[2]; /* 01 00 */ + uchar zero[1]; /* 00 */ + char label[8+1]; /* [MMClabel] 00 */ + char reserved[11]; /* 20 20 00 00 00 00 00 00 00 00 48 */ +}; +/* diropen, fileopen */ +struct mcmdfdopen { /* dopen fopen */ + char cmd[1]; /* 2A or 22 */ + uchar mode[2]; /* 01 00 */ + uchar path[1]; /* [MMCardx\dir] 00 */ +}; +struct mrspfdopen { + uchar stat[2]; /* 01 00 */ + uchar iden[FIDENSZ]; /* (27 bytes FILE_IDEN) */ +}; +/* dirglen, dirclose, filestat, fileclose */ +struct mcmdfdstatcl { /* dglen dcls fstat fcls*/ + char cmd[1]; /* 2B 2E 26 27 */ + uchar iden[FIDENSZ]; /* (27 bytes FILE_IDEN) */ + uchar zero[1]; /* 00 */ +}; +struct mrspdirglen { + uchar iden[FIDENSZ]; /* (27 bytes FILE_IDEN) */ + uchar stat[2]; /* 01 00 */ + uchar ndent[2]; /* nn_nn */ +}; +struct mrspfdclose { + uchar iden[FIDENSZ]; /* (27 bytes FILE_IDEN) */ +}; +/* dirread, fileread */ +struct mcmdfdread { /* dread fread */ + uchar cmd[1]; /* 2C or 24 */ + uchar iden[FIDENSZ]; /* (27 bytes FILE_IDEN) */ + union { + uchar index[2]; /* in_dx dir-index */ + uchar blen[2]; /* nn_nn file-bufflen */ + } u; + uchar zero[1]; /* 00 */ +}; +struct mrspfdstat { + uchar iden[FIDENSZ]; /* (27 bytes FILE_IDEN) */ + uchar stat[2]; /* 01 00 */ + FILE_INFO dirent; /* (24 bytes FILE_INFO) */ +}; +struct mrspfileread { + uchar iden[FIDENSZ]; /* (27 bytes FILE_IDEN) */ + uchar stat[2]; /* 01 00 */ + uchar rlen[2]; /* nn_nn */ + uchar data[1]; /* filedata[nn_nn] */ +}; +/* filedel */ +struct mcmdfiledel { + char cmd[1]; /* 28 */ + uchar path[1]; /* [MMCardx\file] 00 */ +}; +/*resp mrspstat +*/ +/* filewrite */ +struct mcmdfilewrite { + uchar cmd[1]; /* 23 */ + uchar iden[FIDENSZ]; /* (27 bytes FILE_IDEN) */ + uchar wlen[2]; /* nn_nn */ + uchar data[1]; /* filedata[nn_nn] 00 */ +}; +struct mrspfilewrite { + uchar iden[FIDENSZ]; /* (27 bytes FILE_IDEN) */ + uchar stat[2]; /* 01 00 */ +}; + +union mcmdpdu { /* MMCard command PDU */ + uchar cmd; + struct mcmdstatlbl status; + struct mcmdstatlbl label; + struct mcmdfdopen diropen; + struct mcmdfdstatcl dirglen; + struct mcmdfdread dirread; + struct mcmdfdstatcl dirclose; + struct mcmdfiledel filedel; + struct mcmdfdopen fileopen; + struct mcmdfdstatcl filestat; + struct mcmdfdread fileread; + struct mcmdfilewrite filewrite; + struct mcmdfdstatcl fileclose; + uchar data[1]; +}; +union mrsppdu { /* MMCard response PDU */ + uchar stat[2]; + struct mrspstat status; + struct mrsplbl label; + struct mrspfdopen diropen; + struct mrspdirglen dirglen; + struct mrspfdstat dirread; + struct mrspfdclose dirclose; + struct mrspstat filedel; + struct mrspfdopen fileopen; + struct mrspfdstat filestat; + struct mrspfileread fileread; + struct mrspfilewrite filewrite; + struct mrspfdclose fileclose; + uchar data[1]; +}; +union mmcpdu { /* generic MMCard PDU */ + union mcmdpdu cmd; + union mrsppdu rsp; +}; + + +/* MMCard PDU table +* ---------------- +*/ +#define SPDULENOF(type) sizeof( struct type ) +#define FIDENOFFS(type) offsetof( struct type, iden ) +#define RSTATOFFS(type) offsetof( struct type, stat ) +#define NOFFS 0xFFFF + +struct mpdudesc { /* MMCard PDU description */ + uchar cmd; /* command code */ + size_t slen; /* fixed length of PDU */ + size_t sidenoffs; /* offset FILE_IDEN in cmd PDU */ + size_t rstatoffs; /* offset status in resp.PDU */ + size_t ridenoffs; /* offset FILE_IDEN in rsp PDU */ +}; +struct mpdudesc mmcpdutab[] = { + { MMCMDgetstatus, SPDULENOF(mcmdstatlbl), NOFFS, + RSTATOFFS(mrspstat), NOFFS, }, + { MMCMDgetlabel, SPDULENOF(mcmdstatlbl), NOFFS, + RSTATOFFS(mrsplbl), NOFFS, }, + { MMCMDdiropen, SPDULENOF(mcmdfdopen), NOFFS, + RSTATOFFS(mrspfdopen), FIDENOFFS(mrspfdopen) }, + { MMCMDdirgetlen, SPDULENOF(mcmdfdstatcl), FIDENOFFS(mcmdfdstatcl), + RSTATOFFS(mrspdirglen), FIDENOFFS(mrspdirglen) }, + { MMCMDdirread, SPDULENOF(mcmdfdread), FIDENOFFS(mcmdfdread), + RSTATOFFS(mrspfdstat), FIDENOFFS(mrspfdstat) }, + { MMCMDdirclose, SPDULENOF(mcmdfdstatcl), FIDENOFFS(mcmdfdstatcl), + NOFFS, FIDENOFFS(mrspfdclose) }, + { MMCMDfileopen, SPDULENOF(mcmdfdopen), NOFFS, + RSTATOFFS(mrspfdopen), FIDENOFFS(mrspfdopen) }, + { MMCMDfilestat, SPDULENOF(mcmdfdstatcl), FIDENOFFS(mcmdfdstatcl), + RSTATOFFS(mrspfdstat), FIDENOFFS(mrspfdstat) }, + { MMCMDfilewrite, SPDULENOF(mcmdfilewrite), FIDENOFFS(mcmdfilewrite), + RSTATOFFS(mrspfilewrite), FIDENOFFS(mrspfilewrite) }, + { MMCMDfileread, SPDULENOF(mcmdfdread), FIDENOFFS(mcmdfdread), + RSTATOFFS(mrspfileread), FIDENOFFS(mrspfileread) }, + { MMCMDfileclose, SPDULENOF(mcmdfdstatcl), FIDENOFFS(mcmdfdstatcl), + NOFFS, FIDENOFFS(mrspfdclose), }, + { MMCMDfiledel, SPDULENOF(mcmdfiledel), NOFFS, + RSTATOFFS(mrspstat), NOFFS }, + { 0, 0, 0, + 0, 0 } +}; + +/* init MMCard command PDU +* ----------------------- +* lookup MMCard PDU description by command code +* allocate MMCard PDU buffer plus optionally variable length +* setup MMCard command code and return allocated PDU length +*/ +static int /* init variable length PDU */ +_initvpdu( uchar cmd, size_t varlen, union mmcpdu ** ppdu ) +{ + struct mpdudesc * pmpdu; + union mmcpdu * spdu; + size_t pdulen; + + for ( pmpdu = mmcpdutab; pmpdu->slen != 0; ++pmpdu ) + if ( pmpdu->cmd == cmd ) { + pdulen = pmpdu->slen + varlen; + if ( ppdu == NULL /* allocate PDU buffer */ + || (spdu = calloc( 1, pdulen )) == NULL ) + break; + spdu->cmd.cmd = cmd; /* MMCard command code */ + *ppdu = spdu; + return pdulen; /* length of PDU buffer */ + } + return 0; +} +static int /* init fixe length PDU */ +_initfpdu( uchar cmd, union mmcpdu ** ppdu ) +{ + return _initvpdu( cmd, 0, ppdu ); +} + +/* send MMCard command PDU, receive response PDU +* --------------------------------------------- +* optionally encode FILE_IDEN into command PDU +* send command PDU and release it +* receive response PDU, MMCrecv() will allocate it +* optionally decode FILE_IDEN and status from response PDU +*/ +static long +_sendrecv( union mmcpdu ** ppdu, size_t * ppdulen, uchar * fdstat ) +{ + struct mpdudesc * pmpdu; + int rval; + + for ( pmpdu = mmcpdutab; pmpdu->cmd != (*ppdu)->cmd.cmd; ++pmpdu ) + if ( pmpdu->slen == 0 ) + return ERR; /* PDU description not in table */ + + if ( pmpdu->sidenoffs != NOFFS && fdstat ) /* encode FILE_IDEN */ + putbtxt( (*ppdu)->cmd.data + pmpdu->sidenoffs, fdstat, FIDENSZ ); + rval = MMCsend( (*ppdu)->cmd.data, *ppdulen ); /* send command PDU */ + free( *ppdu ); *ppdu = NULL; *ppdulen = 0; /* and release it */ + if ( rval < 0 ) /* ERR, send failed */ + return rval; + + if ( (rval = MMCrecv( (uchar**)ppdu )) < 0 ) /* recv response PDU */ + return rval; /* ERR, recv failed */ + *ppdulen = rval; /* recv'd PDU length */ + if ( pmpdu->ridenoffs != NOFFS && fdstat ) /* decode FILE_IDEN */ + getbtxt( (*ppdu)->rsp.data + pmpdu->ridenoffs, fdstat, FIDENSZ ); + if ( pmpdu->rstatoffs != NOFFS ) /* return status .. */ + return getword( (uchar*)*ppdu + pmpdu->rstatoffs );/* from resp PDU */ + else + return 0x0000; /* or no status */ +} + +/* convert FILE_INFO +* ----------------- +* converts integer fields from IC35's to host's byte order +*/ +static void +_cvtfinfo( FILE_INFO * pdudirent, FILE_INFO * hostdirent ) +{ + if ( pdudirent == NULL || hostdirent == NULL ) + return; + getbtxt( (uchar*)pdudirent, (uchar*)hostdirent, sizeof(*hostdirent) ); + hostdirent->ModifyTime = getword( (uchar*)&pdudirent->ModifyTime); + hostdirent->ModifyDate = getword( (uchar*)&pdudirent->ModifyDate); + hostdirent->FileSize = getdword( (uchar*)&pdudirent->FileSize ); +} + + +/* MMCard general commands */ +/* ======================= */ + +/* get MMCard status +* ----------------- +* IC35 SDK API 13.1 mInitialCard() +* command: 20 "MMCard" [mmc_num] 00 +* response: 01 00 MMCard present +* FF FF MMCard not detected +*/ +long +MMCgetstatus( int mmcnum ) +{ + union mmcpdu * pdu; + int pdulen; + long mstat; + + if ( (pdulen = _initfpdu( MMCMDgetstatus, &pdu )) <= 0 ) + return ERR; + + puttext( pdu->cmd.status.mmcard, "MMCard" ); + putbyte( pdu->cmd.status.mmcnum, (char)('0'+mmcnum) ); + + if ( (mstat = _sendrecv( &pdu, &pdulen, NULL )) < 0 ) + return mstat; + + free( pdu ); + return mstat; +} + +/* get MMCard label +* ---------------- +* IC35 ADK API 13.4 mGetCardLabel() +* command: 34 "MMCard" [mmc_num] 00 +* response: 01 00 00 label[8] 00 20 20 00 00 00 00 00 00 00 00 48 +*/ +long +MMCgetlabel( int mmcnum, char * plabel ) +{ + union mmcpdu * pdu; + int pdulen; + long mstat; + + if ( (pdulen = _initfpdu( MMCMDgetlabel, &pdu )) <= 0 ) + return ERR; + + /* encode cmdargs */ + puttext( pdu->cmd.status.mmcard, "MMCard" ); + putbyte( pdu->cmd.status.mmcnum, (char)('0'+mmcnum) ); + + if ( (mstat = _sendrecv( &pdu, &pdulen, NULL )) < 0 ) + return mstat; + + /* decode rspargs */ + gettext( pdu->rsp.label.label, plabel, strlen(pdu->rsp.label.label) ); + + free( pdu ); + return mstat; +} + + +/* MMCard directory commands */ +/* ========================= */ + +/* open MMC directory +* ------------------ +* IC35 SDK API 13.14 mOpenDirectory() +* command: 2A 01 00 [MMCard1\path\to\dir] 00 +* response: 01 00 fdiden[27] +*/ +long +MMCdiropen( char * dirpath, uchar * fdstat ) +{ + union mmcpdu * pdu; + int pdulen; + long mstat; + + if ( (pdulen = _initvpdu( MMCMDdiropen, strlen(dirpath), &pdu )) <= 0 ) + return ERR; + + /* encode cmdargs */ + putword( pdu->cmd.diropen.mode, 0x0001 ); + puttxt0( pdu->cmd.diropen.path, dirpath ); + + if ( (mstat = _sendrecv( &pdu, &pdulen, fdstat )) < 0 ) + return mstat; + + free( pdu ); + return mstat; +} +long +MMCdircreate( char * dirpath, uchar * fdstat ) +{ + union mmcpdu * pdu; + int pdulen; + long mstat; + + if ( (pdulen = _initvpdu( MMCMDdiropen, strlen(dirpath), &pdu )) <= 0 ) + return ERR; + + /* encode cmdargs */ + putword( pdu->cmd.diropen.mode, 0x0000 ); + puttxt0( pdu->cmd.diropen.path, dirpath ); + + if ( (mstat = _sendrecv( &pdu, &pdulen, fdstat )) < 0 ) + return mstat; + + free( pdu ); + return mstat; +} + +/* get num.of entries in MMCard directory +* -------------------------------------- +* IC35 ADK API 13.15 mGetDirectorySubItemNum() +* command: 2B fdiden[27] 00 +* response: fdiden[27] 01 00 nn_nn +*/ +long +MMCdirgetlen( uchar * fdstat, ushort * pndent ) +{ + union mmcpdu * pdu; + int pdulen; + long mstat; + + if ( (pdulen = _initfpdu( MMCMDdirgetlen, &pdu )) <= 0 ) + return ERR; + + if ( (mstat = _sendrecv( &pdu, &pdulen, fdstat )) < 0 ) + return mstat; + + /* decode rspargs */ + *pndent = getword( pdu->rsp.dirglen.ndent ); + + free( pdu ); + return mstat; +} + +/* read MMC directory +* ------------------ +* IC35 SDK API 13.16 mGetDirectorySubItem() +* command: 2C fdiden[27] in_dx 00 +* response: fdiden[27] 01 00 +* filename 00 ext 00 at ti_me_st_mp 00 00 fi_le_si_ze +*/ +long +MMCdirread( uchar * fdstat, ushort index, FILE_INFO * pdirent ) +{ + union mmcpdu * pdu; + int pdulen; + long mstat; + + if ( (pdulen = _initfpdu( MMCMDdirread, &pdu )) <= 0 ) + return ERR; + + /* encode cmdargs */ + putword( pdu->cmd.dirread.u.index, index ); + + if ( (mstat = _sendrecv( &pdu, &pdulen, fdstat )) < 0 ) + return mstat; + + /* decode rspargs */ + _cvtfinfo( &pdu->rsp.dirread.dirent, pdirent ); + + free( pdu ); + return mstat; +} + +/* close MMC directory +* ------------------- +* IC35 SDK API 13.18 mCloseDirectory() +* command: 2E fdiden[27] 00 +* response: fdiden[27] +*/ +long +MMCdirclose( uchar * fdstat ) +{ + union mmcpdu * pdu; + int pdulen; + long mstat; + + if ( (pdulen = _initfpdu( MMCMDdirclose, &pdu )) <= 0 ) + return ERR; + + if ( (mstat = _sendrecv( &pdu, &pdulen, fdstat )) < 0 ) + return mstat; + + free( pdu ); + return mstat; +} + + +/* MMCard file commands */ +/* ==================== */ + +/* delete MMC file +* --------------- +* IC35 SDK API 13.13 mDeleteFile() +* command: 28 [MMCard1\path\to\file] 00 +* response: 01 00 +*/ +long +MMCfiledel( char * filepath ) +{ + union mmcpdu * pdu; + int pdulen; + long mstat; + + if ( (pdulen = _initvpdu( MMCMDfiledel, strlen(filepath), &pdu )) <= 0 ) + return ERR; + + /* encode cmdargs */ + puttxt0( pdu->cmd.filedel.path, filepath ); + + if ( (mstat = _sendrecv( &pdu, &pdulen, NULL )) < 0 ) + return mstat; + + free( pdu ); + return mstat; +} + +/* open MMC file +* ------------- +* IC35 SDK API 13.6 mOpenFile() +* command: 22 01 00 [MMCard1\path\to\file] 00 +* response: 01 00 fdiden[27] +*/ +long +MMCfileopen( char * filepath, ushort mode, uchar * fdstat, ulong * psize ) +{ + union mmcpdu * pdu; + int pdulen; + long mstat; + + if ( (pdulen = _initvpdu( MMCMDfileopen, strlen(filepath), &pdu )) <= 0 ) + return ERR; + + /* encode cmdargs */ + putword( pdu->cmd.fileopen.mode, mode ); + puttxt0( pdu->cmd.fileopen.path, filepath ); + + if ( (mstat = _sendrecv( &pdu, &pdulen, fdstat )) < 0 ) + return mstat; + + /* decode rspargs */ + *psize = getdword( pdu->rsp.fileopen.iden+14 ); + + free( pdu ); + return mstat; +} + +/* get MMC file status +* ------------------- +* IC35 SDK API 13.10 mGetFileInfo() +* command: 26 fdiden[27] 00 +* response: fdiden[27] 01 00 +* filename 00 ext 00 at ti_me_st_mp 00 00 fi_le_si_ze +*/ +long +MMCfilestat( uchar * fdstat, FILE_INFO * pdirent ) +{ + union mmcpdu * pdu; + int pdulen; + long mstat; + + if ( (pdulen = _initfpdu( MMCMDfilestat, &pdu )) <= 0 ) + return ERR; + + if ( (mstat = _sendrecv( &pdu, &pdulen, fdstat )) < 0 ) + return mstat; + + /* decode rspargs */ + _cvtfinfo( &pdu->rsp.filestat.dirent, pdirent ); + + free( pdu ); + return mstat; +} + +/* read data from MMC file +* ----------------------- +* IC35 SDK API 13.9 mReadFormFile() +* command: 24 fdiden[27] nn_nn 00 +* response: fdiden[27] 01 00 rr_rr +* +*/ +long +MMCfileread( uchar * fdstat, uchar * buff, ushort blen, ushort * prlen ) +{ + union mmcpdu * pdu; + int pdulen; + long mstat; + size_t dlen; + + if ( (pdulen = _initfpdu( MMCMDfileread, &pdu )) <= 0 ) + return ERR; + + /* encode cmdargs */ + putword( pdu->cmd.fileread.u.blen, blen ); + + if ( (mstat = _sendrecv( &pdu, &pdulen, fdstat )) < 0 ) + return mstat; + + /* decode rspargs */ + *prlen = getword( pdu->rsp.fileread.rlen ); + dlen = pdulen - offsetof(struct mrspfileread, data); + getbtxt( pdu->rsp.fileread.data, buff, dlen ); + if ( dlen != *prlen ) + LPRINTF(( L_WARN, "MMCfileread: rlen=%u, dlen=%u", *prlen, dlen )); + + free( pdu ); + return mstat; +} + +/* write data to MMC file +* ---------------------- +* IC35 SDK API 13.8 mWriteToFile() +* command: 23 fdiden[27] ww_ww +* 00 +* response: fdiden[27] 01 00 +*/ +long +MMCfilewrite( uchar * fdstat, uchar * data, ushort dlen ) +{ + union mmcpdu * pdu; + int pdulen; + long mstat; + + if ( (pdulen = _initvpdu( MMCMDfilewrite, dlen, &pdu )) <= 0 ) + return ERR; + + /* encode cmdargs */ + putword( pdu->cmd.filewrite.wlen, dlen ); + putbtxt( pdu->cmd.filewrite.data, data, dlen ); + putbyte( pdu->cmd.filewrite.data+dlen, 0x00 ); + + if ( (mstat = _sendrecv( &pdu, &pdulen, fdstat )) < 0 ) + return mstat; + + free( pdu ); + return mstat; +} + +/* close MMC file +* -------------- +* IC35 SDK API 13.11 mCloseFile() +* command: 27 fdiden[27] 00 +* response: fdiden[27] +*/ +long +MMCfileclose( uchar * fdstat ) +{ + union mmcpdu * pdu; + int pdulen; + long mstat; + + if ( (pdulen = _initfpdu( MMCMDfileclose, &pdu )) <= 0 ) + return ERR; + + if ( (mstat = _sendrecv( &pdu, &pdulen, fdstat )) < 0 ) + return mstat; + + free( pdu ); + return mstat; +} diff --git a/src/mgrproto.h b/src/mgrproto.h new file mode 100644 index 0000000..6ebe06b --- /dev/null +++ b/src/mgrproto.h @@ -0,0 +1,95 @@ +/************************************************************************ +* Copyright (C) 2001 Thomas Schulz * +* * +* $Id: mgrproto.h,v 1.8 2001/11/20 23:08:35 thosch Exp $ * +* * +* header for IC35 manager protocol * +* * +************************************************************************/ +#ifndef _MGRPROTO_H +#define _MGRPROTO_H 1 + +#include "util.h" /* uchar */ + + +/* manager command bytes */ +#define MCMDdisconn (uchar)0x01 /* disconnect */ +#define MCMDreset (uchar)0x09 /* reset communication */ +#define MCMDident (uchar)0x10 /* identify: get "DCS_SDK" 00 */ +#define MCMDbackup (uchar)0x13 /* backup database */ +#define MCMDrestinit (uchar)0x14 /* restore database command-1 */ +#define MCMDmmcard (uchar)0x15 /* start MMCard transaction */ +#define MCMDinfo (uchar)0x18 /* get backup info "0128" */ +#define MCMDinit (uchar)0x50 /* status command-2, no response*/ +#define MCMDposack (uchar)0x60 /* positive acknowledge */ +#define MCMDnegack (uchar)0x62 /* negative acknowledge */ +#define MCMDrestdata (uchar)0x70 /* restore database command-2 */ +#define MCMDstatus (uchar)0xFF /* get 16400 byte status block */ +/* manager response bytes */ +#define MRSPgotcmd (uchar)0x90 /* IC35 got command */ +#define MRSPgotack (uchar)0xA0 /* IC35 got ack/nak */ +#define MRSPrestdata (uchar)0xC0 /* restore database response-2 */ +#define MRSPgotlen (uchar)0xE0 /* IC35/PC got length */ + +/* MMCard file attributes (from IC35 SDK Mmc.h) */ +#define MMCattrReadOnly 0x01 +#define MMCattrHidden 0x02 +#define MMCattrSystemFile 0x04 +#define MMCattrVolumeLabel 0x08 +#define MMCattrDirectory 0x10 +#define MMCattrArchive 0x20 +/* MMCard file open modes (see IC35 SDK MMc.h) */ +#define MMCopenexist 0x0001 /* open existing file for read,write */ +#define MMCcreatrunc 0x0000 /* create new truncate existing file */ + + +#pragma pack(1) +typedef struct _file_info { /* directory entry (see IC35 SDK Mmc.h) */ + char FileName[8+1]; + char ExtName[3+1]; + uchar Attribute; + ushort ModifyTime; + ushort ModifyDate; + ushort Reserved; + ulong FileSize; +} FILE_INFO; +typedef struct _file_iden { /* file identifier (see IC35 SDK Mmc.h) */ + ushort Sector; + ushort Cluster; + ushort SectorOffset; + ushort DirItemOffset; + ushort StartCluster; + ulong FilePointer; + ulong FileSize; + uchar CacheNo; + ushort CurCluster; + ushort CurClusterNo; + ushort CurSectorNo; + ushort Reserved; +} FILE_IDEN; +#pragma pack() +#define FIDENSZ sizeof(FILE_IDEN) + + +int Mcmdrsp( uchar cmd, uchar rsp ); /* send cmd, get+check rsp */ + +int Msendblk( uchar * data, size_t dlen ); /* send block, ack/retry */ +int Mrecvblk( uchar * buff, size_t blen ); /* receive block, ack/retry */ + +long MMCgetstatus( int mmcnum ); +long MMCgetlabel( int mmcnum, char * plabel ); + +long MMCdiropen( char * dirpath, uchar * fdstat ); +long MMCdircreate( char * dirpath, uchar * fdstat ); +long MMCdirgetlen( uchar * fdstat, ushort * pndent ); +long MMCdirread( uchar * fdstat, ushort index, FILE_INFO * pdirent ); +long MMCdirclose( uchar * fdstat ); + +long MMCfiledel( char * filepath ); +long MMCfileopen( char * filepath, ushort mode, uchar * fdstat, ulong * psize ); +long MMCfilestat( uchar * fdstat, FILE_INFO * pdirent ); +long MMCfileread( uchar * fdstat, uchar * buff, ushort blen, ushort * prlen ); +long MMCfilewrite( uchar * fdstat, uchar * data, ushort dlen ); +long MMCfileclose( uchar * fdstat ); + +#endif /*_MGRPROTO_H*/ diff --git a/src/mgrtrans.c b/src/mgrtrans.c new file mode 100644 index 0000000..29822ce --- /dev/null +++ b/src/mgrtrans.c @@ -0,0 +1,943 @@ +/************************************************************************ +* Copyright (C) 2001 Thomas Schulz * +* */ + static char rcsid[] = + "$Id: mgrtrans.c,v 1.17 2001/11/20 23:08:35 thosch Exp $"; /* +* * +* IC35 manager transactions * +* * +************************************************************************* +* * +* ??? is "fixme" mark: sections of code needing fixes * +* +* communication phases +* mconnect +* mdisconnect +* MMCard info +* mmc_status +* mmc_label +* MMCard directory ops +* mmc_opendir +* mmc_createdir +* mmc_readdir +* mmc_closedir +* mmctstampstr +* mmctstampunixtime +* MMCard file operations +* mmc_delfile +* mmc_openfile +* mmc_statfile +* mmc_readfile +* mmc_writefile +* mmc_closefile +* IC35 database backup,restore +* readdatabase +* writedatabase +* * +************************************************************************/ + +#include /* fprintf(), .. */ +#include /* memcpy(), strlen() ..*/ +#include /* malloc(), .. */ +#include /* usleep() */ +#include /* size_t, .. */ +#include /* time_t, localtime()..*/ +#include + +#include "util.h" /* ERR,OK, uchar, .. */ +#include "comio.h" /* com_init(), .. */ +#include "genproto.h" /* welcome() */ +#include "mgrproto.h" /* manager protocol */ +#include "mgrtrans.h" +NOTUSED(rcsid); + + +/* ==================================== */ +/* communication phases */ +/* ==================================== */ + +/* welcome phase +* ------------- +* opens comm.device and does welcome handshake +*/ +static int +mgrwelcome( char * devname ) +{ + int rval; + + LPRINTF(( L_INFO, "welcome(%s) ..", devname )); + if ( com_init( devname ) != OK ) { + error( "welcome failed: com_init(%s) failed", devname ); + return ERR; + } + message( "welcome, start IC35 !" ); + if ( (rval = welcome( 0x40 )) != OK ) { + error( "welcome %s", rval == ERR_intr ? "aborted" + : "failed: no response" ); + return ERR; + } + message( "connected" ); + LPRINTF(( L_INFO, "welcome(%s) OK", devname )); + return OK; +} + +/* identify +* -------- +* communication PC <-> IC35: +* reset-phase -> 09 +* <- 90 +* ident-phase -> 10 +* <- 90 "DCS_SDK" 00 +*/ +static int +identify( char rtext[7+1] ) +{ + int rlen; + char rbuff[7+1]; /* for "DCS_SDK\x00" */ + + LPRINTF(( L_INFO, "identify .." )); + + if ( Mcmdrsp( MCMDreset, MRSPgotcmd ) != OK ) { + error( "identify failed: reset-phase" ); + return ERR; + } + if ( Mcmdrsp( MCMDident, MRSPgotcmd ) != OK + || (rlen = com_recv( rbuff, sizeof(rbuff) )) <= 0 ) { + error( "identify failed: ident-phase" ); + return ERR; + } + if ( rtext != NULL ) + strncat( strcpy( rtext, "" ), rbuff, rlen ); + + LPRINTF(( L_INFO, "identify OK" )); + return OK; +} + +/* init IC35, optionally get status data +* ------------------------------------- +* communication PC <-> IC35: +* optional: +* -> FF +* <- [block of 16400 bytes] +* mandatory: +* -> 50 +* <- 90 or timeout 0.1 sec +* IC35 may send response, but anyway needs time to get ready +*/ +#define STATSIZE 16400 + +static int +status( char * fname ) +{ + uchar cmd; + char * rbuff; + int rlen = -1; + FILE * fp; + int old_tmo; + uchar rsp; + + LPRINTF(( L_INFO, "status(%s) ..", fname ? fname : "NULL" )); + + if ( fname && *fname ) { + if ( (rbuff = malloc( STATSIZE )) != NULL ) { + cmd = MCMDstatus; + com_send( &cmd, 1 ); + if ( (rlen = com_recv( rbuff, STATSIZE )) < 0 ) { + error( "status failed: receive" ); + free( rbuff ); + return ERR; + } + if ( (fp = fopen( fname, "w")) != NULL ) { + fwrite( rbuff, 1, rlen, fp ); + fclose( fp ); + } else { + message( "cannot open statfile: %s", fname ); + } + free( rbuff ); + } else { + message( "cannot read statdata: no memory (%d)", STATSIZE ); + } + } + cmd = MCMDinit; + com_send( &cmd, 1 ); /* -> 50 */ + old_tmo = com_settimeout( 100 ); + com_recv( &rsp, 1 ); /* <- 90 or wait 100 msec */ + com_settimeout( old_tmo ); + + if ( rlen >= 0 ) /* received status block */ + LPRINTF(( L_INFO, "status received %d bytes statdata", rlen )); + else + LPRINTF(( L_INFO, "status OK" )); + return OK; +} + +/* open com-device and connect with IC35 +* ------------------------------------- +*/ +int +mconnect( char * devname, char * statfname ) +{ + int rval; + char idtext[8]; + + LPRINTF(( L_INFO, "mconnect(%s,%s) ..", + devname, statfname ? statfname : "NULL" )); + if ( (rval = mgrwelcome( devname )) == OK + && (rval = identify( idtext )) == OK ) { + message( "identity \"%s\"", idtext ); + if ( statfname && *statfname ) + message( "statdata %s", statfname ); + rval = status( statfname ); + } + if ( rval != OK ) + mdisconnect(); + LPRINTF(( L_INFO, "mconnect(%s,%s) %s", + devname, statfname ? statfname : "NULL", + rval == OK ? "OK" : "ERR" )); + return rval; +} + +/* disconnect from IC35 and close com-device +* ----------------------------------------- +* communication PC <-> IC35: +* -> 09 +* timeout +* -> 09 +* <- 90 +* -> 01 +* timeout +* -> 01 +* <- 90 +* until response '\x90' is received, send '\x09' or '\x01' +* respectively will be retried up to 5 times. +*/ +int +mdisconnect( void ) +{ + uchar cmd, rsp; + int rval; + + message( "disconnect" ); + if ( (rval = Mcmdrsp( cmd = MCMDreset, rsp = MRSPgotcmd )) != OK + || (rval = Mcmdrsp( cmd = MCMDdisconn, rsp = MRSPgotcmd )) != OK ) + message( "disconnect sent '\\x%02X', failed receive '\\x%02X'", + cmd, rsp ); + com_exit(); + LPRINTF(( L_INFO, "disconnect done." )); + return rval; +} + + +/* ==================================== */ +/* IC35 MMCard info */ +/* ==================================== */ + +/* local: convert mstat to text +* ---------------------------- +*/ +static char * +_mstatxt( long mstat ) +{ + static char mstatxt[2+11+1]; /* "(-nnnnnnnnnn)"\0 */ + + if ( mstat >= 0 ) + sprintf( mstatxt, "(%04hX)", (ushort)mstat ); + else + sprintf( mstatxt, "(%ld)", mstat ); + return mstatxt; +} + +/* get MMCard status +* ----------------- +* returns: +* 1 OK, MMCard found +* 0 OK, MMCard not detected +* -1 ERR, communication failed +*/ +int +mmc_status( int mmcnum ) +{ + long mstat; + + if ( mmcnum != 1 && mmcnum != 2 ) { + error( "mmc_status: bad MMCard number %d", mmcnum ); + return ERR; + } + LPRINTF(( L_INFO, "mmc_status(%d) ..", mmcnum )); + if ( (mstat = MMCgetstatus( mmcnum )) < 0 + || !( mstat == 0xFFFF || mstat == 0x0001 ) ) { + error( "mmc_status failed %s", _mstatxt(mstat) ); + return ERR; + } + LPRINTF(( L_INFO, "mmc_status(%d) = %04X", mmcnum, mstat )); + return mstat == 0xFFFF ? 0 : mstat; +} + +/* get MMCard label +* ---------------- +*/ +int +mmc_label( int mmcnum, char label[11+1] ) +{ + long mstat; + + if ( mmcnum != 1 && mmcnum != 2 ) { + error( "mmc_label: bad MMCard number %d", mmcnum ); + return ERR; + } + LPRINTF(( L_INFO, "mmc_label(%d) ..", mmcnum )); + if ( (mstat = MMCgetlabel( mmcnum, label )) != 0x0001 ) { + error( "mmc_label failed %s", _mstatxt(mstat) ); + return ERR; + } + LPRINTF(( L_INFO, "mmc_label(%d) = \"%s\"", mmcnum, label )); + return OK; +} + + +/* ==================================== */ +/* IC35 MMCard directory ops */ +/* ==================================== */ + +struct mmcdir { /* MMC directory descriptor */ + uchar fdiden[FIDENSZ];/* MMC dir/file access ident */ + MMCDIRENT dirent; /* decoded directory entry */ + ushort ndirent; /* number of directory entries */ + ushort dirindex; /* current directory index */ +}; + +/* local: convert FILE_INFO to MMCDIRENT +* ------------------------------------- +*/ +static void +_mmc_finfo2dirent( FILE_INFO * finfo, MMCDIRENT * dirent ) +{ + if ( finfo == NULL || dirent == NULL ) + return; + strncat( strcpy( dirent->name, "" ), finfo->FileName, 8 ); + if ( strlen( finfo->ExtName ) != 0 ) { + strcat( dirent->name, "." ); + strncat( dirent->name, finfo->ExtName, 3 ); + } + dirent->attr = finfo->Attribute; + dirent->tstamp = finfo->ModifyDate << 16 | finfo->ModifyTime; + dirent->size = finfo->FileSize; +} + +/* open MMC directory +* ------------------ +*/ +MMCDIR * +mmc_opendir( char * dirpath ) +{ + MMCDIR * dirp; + long mstat; + + if ( (dirp = malloc( sizeof(*dirp) )) == NULL ) { + error( "mmc_opendir failed: no memory (%d)", sizeof(*dirp) ); + return NULL; + } + LPRINTF(( L_INFO, "mmc_opendir(%s) ..", dirpath )); + if ( (mstat = MMCdiropen( dirpath, dirp->fdiden )) != 0x0001 ) { + error( "mmc_opendir(%s) diropen failed %s", dirpath, _mstatxt(mstat) ); + return NULL; + } + if ( (mstat = MMCdirgetlen( dirp->fdiden, &dirp->ndirent )) != 0x0001 ) { + error( "mmc_opendir(%s) dirgetlen failed %s", dirpath,_mstatxt(mstat) ); + return NULL; + } + dirp->dirindex = 0; + LPRINTF(( L_INFO, "mmc_opendir(%s) OK, ndirent=%d", + dirpath, dirp->ndirent )); + return dirp; +} +/* open MMC directory +* ------------------ +*/ +MMCDIR * +mmc_createdir( char * dirpath ) +{ + MMCDIR * dirp; + long mstat; + + if ( (dirp = malloc( sizeof(*dirp) )) == NULL ) { + error( "mmc_createdir failed: no memory (%d)", sizeof(*dirp) ); + return NULL; + } + LPRINTF(( L_INFO, "mmc_createdir(%s) ..", dirpath )); + if ( (mstat = MMCdircreate( dirpath, dirp->fdiden )) != 0x0001 ) { + error( "mmc_createdir(%s) dircreate failed %s", dirpath, _mstatxt(mstat) ); + return NULL; + } + if ( (mstat = MMCdirgetlen( dirp->fdiden, &dirp->ndirent )) != 0x0001 ) { + error( "mmc_createdir(%s) dirgetlen failed %s", dirpath,_mstatxt(mstat) ); + return NULL; + } + dirp->dirindex = 0; + LPRINTF(( L_INFO, "mmc_createdir(%s) OK, ndirent=%d", + dirpath, dirp->ndirent )); + return dirp; +} + +/* read MMC directory +* ------------------ +*/ +MMCDIRENT * +mmc_readdir( MMCDIR * dirp ) +{ + long mstat; + FILE_INFO mmcdirent; + + if ( dirp == NULL ) + return NULL; + if ( dirp->dirindex >= dirp->ndirent ) { + LPRINTF(( L_INFO, "mmc_readdir: eodir (%d)", dirp->dirindex )); + return NULL; + } + ++dirp->dirindex; + LPRINTF(( L_INFO, "mmc_readdir index=%d ..", dirp->dirindex )); + if ( (mstat = MMCdirread( dirp->fdiden, dirp->dirindex, &mmcdirent )) + != 0x0001 ) { + error( "mmc_readdir failed %s", _mstatxt(mstat) ); + return NULL; + } + _mmc_finfo2dirent( &mmcdirent, &dirp->dirent ); + LPRINTF(( L_INFO, "mmc_readdir OK" )); + return &dirp->dirent; +} + +/* close MMC directory +* ------------------- +*/ +int +mmc_closedir( MMCDIR * dirp ) +{ + if ( dirp == NULL ) + return OK; + LPRINTF(( L_INFO, "mmc_closedir .." )); + if ( MMCdirclose( dirp->fdiden ) < 0 ) { + error( "mmc_closedir failed" ); + return ERR; + } + free( dirp ); + LPRINTF(( L_INFO, "mmc_closedir OK" )); + return OK; +} + +/* convert MMC timestamp +* --------------------- +* the timestamp of MMCard dir/file is encoded like DOS: +* 3322222 2222 21111 11111 100000 00000 bit- .. +* 1098765 4321 09876 54321 098765 43210 .. number +* yyyyyyy mmmm ddddd hhhhh mmmmmm sssss bit fields +* the bit field meanings are: +* yyyyyyy years since 1980 +* mmmm month 1..12 +* ddddd day 1..31 +* hhhhh hour 00..24 +* mmmmmm minute 00..59 +* sssss second/2 00..31 +* mmctstampstr() returns: +* (char*) timestamp string: yyyy-mm-dd hh:mm:ss +* mmctstampunixtime() returns: +* (time_t) timestamp converted to Unix time +*/ +static struct tm * +_mmctstamptm( ulong tstamp ) +{ + static struct tm mmctm; + + mmctm.tm_year = ((tstamp & 0xFE000000) >> (4+5+5+6+5)) + 80; + mmctm.tm_mon = ((tstamp & 0x01E00000) >> ( 5+5+6+5)) - 1; + mmctm.tm_mday = (tstamp & 0x001F0000) >> ( 5+6+5); + mmctm.tm_hour = (tstamp & 0x0000F800) >> ( 6+5); + mmctm.tm_min = (tstamp & 0x000007E0) >> ( 5); + mmctm.tm_sec = (tstamp & 0x0000001F) * 2; + return &mmctm; +} +char * +mmctstampstr( ulong tstamp ) +{ + static char ymdhms[19+1]; + struct tm * ptm; + + ptm = _mmctstamptm( tstamp ); + sprintf( ymdhms, "%04u-%02u-%02u %02u:%02u:%02u", + ptm->tm_year+1900, ptm->tm_mon+1, ptm->tm_mday, + ptm->tm_hour, ptm->tm_min, ptm->tm_sec ); + return ymdhms; +} +time_t +mmctstampunixtime( ulong tstamp ) +{ + struct tm * ptm; + + ptm = _mmctstamptm( tstamp ); + ptm->tm_isdst = -1; + return mktime( ptm ); +} + + +/* ==================================== */ +/* IC35 MMCard file operations */ +/* ==================================== */ + +struct mmcfile { /* MMC file descriptor */ + uchar fdiden[FIDENSZ];/* MMC dir/file access ident */ + MMCDIRENT filestat; /* decoded file status */ + ulong size; /* file size */ + ulong offset; /* current offset in file */ +}; + +/* delete MMC file +* --------------- +*/ +int +mmc_delfile( char * filepath ) +{ + long mstat; + + LPRINTF(( L_INFO, "mmc_delfile(%s) ..", filepath )); + if ( (mstat = MMCfiledel( filepath )) != 0x0001 ) { + error( "mmc_delfile(%s) failed %s", filepath, _mstatxt(mstat) ); + return ERR; + } + LPRINTF(( L_INFO, "mmc_delfile(%s) OK", filepath )); + return OK; +} + +/* open MMC file +* ------------- +*/ +MMCFILE * +mmc_openfile( char * filepath, ushort mode ) +{ + MMCFILE * fp; + long mstat; + + if ( (fp = malloc( sizeof(*fp) )) == NULL ) { + error( "mmc_openfile failed: no memory (%d)", sizeof(*fp) ); + return NULL; + } + LPRINTF(( L_INFO, "mmc_openfile(%s) ..", filepath )); + if ( (mstat = MMCfileopen( filepath, mode, fp->fdiden, &fp->size )) + != 0x0001 ) { + error( "mmc_openfile(%s) failed %s", filepath, _mstatxt(mstat) ); + return NULL; + } + fp->offset = 0; + LPRINTF(( L_INFO, "mmc_openfile(%s) OK, size=%lu", filepath, fp->size )); + return fp; +} + +/* get MMC file status +* ------------------- +*/ +MMCDIRENT * +mmc_statfile( MMCFILE * fp ) +{ + long mstat; + FILE_INFO mmcdirent; + + if ( fp == NULL ) + return NULL; + LPRINTF(( L_INFO, "mmc_statfile .." )); + if ( (mstat = MMCfilestat( fp->fdiden, &mmcdirent )) != 0x0001 ) { + error( "mmc_statfile failed %s", _mstatxt(mstat) ); + return NULL; + } + _mmc_finfo2dirent( &mmcdirent, &fp->filestat ); + LPRINTF(( L_INFO, "mmc_statfile OK" )); + return &fp->filestat; +} + +/* read data from MMC file +* ----------------------- +*/ +int +mmc_readfile( MMCFILE * fp, uchar * buff, size_t blen ) +{ + long mstat; + ushort rlen; + + if ( fp == NULL ) + return ERR; + if ( fp->offset >= fp->size ) { + LPRINTF(( L_INFO, "mmc_readfile: eofile (%d)", fp->offset )); + return 0; + } + LPRINTF(( L_INFO, "mmc_readfile(buff,%d) ..", blen )); + if ( fp->offset + blen > fp->size ) + blen = fp->size - fp->offset; + if ( (mstat = MMCfileread( fp->fdiden, buff, blen, &rlen )) != 0x0001 ) { + error( "mmc_readfile failed %s", _mstatxt(mstat) ); + return ERR; + } + fp->offset += rlen; + LPRINTF(( L_INFO, "mmc_readfile(buff,%d) = %d", blen, rlen )); + return rlen; +} + +/* write data to MMC file +* ---------------------- +*/ +int +mmc_writefile( MMCFILE * fp, uchar * data, size_t dlen ) +{ + long mstat; + + if ( fp == NULL ) + return ERR; + LPRINTF(( L_INFO, "mmc_writefile(data,%d) ..", dlen )); + if ( (mstat = MMCfilewrite( fp->fdiden, data, dlen )) != 0x0001 ) { + error( "mmc_writefile failed %s", _mstatxt(mstat) ); + return ERR; + } + if ( fp->offset == fp->size ) + fp->size += dlen; + fp->offset += dlen; + LPRINTF(( L_INFO, "mmc_writefile(data,%d) OK", dlen )); + return dlen; +} + +/* close MMC file +* -------------- +*/ +int +mmc_closefile( MMCFILE * fp ) +{ + if ( fp == NULL ) + return OK; + LPRINTF(( L_INFO, "mmc_closefile .." )); + if ( MMCfileclose( fp->fdiden ) < 0 ) { + error( "mmc_closefile failed" ); + return ERR; + } + free( fp ); + LPRINTF(( L_INFO, "mmc_closefile OK" )); + return OK; +} + + +/* ==================================== */ +/* IC35 database backup,restore */ +/* ==================================== */ + +#define HEADBLKSZ 136 +#define DATABLKSZ 16384 +#define INFOBLKSZ 4 +#define NDATABLKS 26 +#define NINFOBLKS 2 + +/* get database info +* ----------------- +*/ +static int +_getdbinfo( uchar * buff, size_t blen ) +{ + if ( Mcmdrsp( MCMDinfo, MRSPgotcmd ) != OK ) + return ERR; + return com_recv( buff, blen ); +} + +/* textify protocol errorcode +* -------------------------- +*/ +static char * +_dbetext( int len ) +{ + static char etext[24+1]; + + switch ( len ) { + case ERR_recv: + return "recv.error/timeout"; + case ERR_acknak: + return "ack/nak handshake failed"; + case ERR_chksum: + return "checksum error"; + default: + sprintf( etext, "%s (%d)", len < 0 ? "error" : "bad length", len ); + return etext; + } +} + +/* IC35 database backup to file +* ============================ +* communication PC <-> IC35: +* backup command +* -> 13 command backup +* <- 90 response gotcmd +* on receive error / timeout retry sending command +* headblock +* <- [136-byte-block] cc_cc headblock +* -> 60 positive acknowledge +* <- A0 response gotack +* or on checksum mismatch: +* -> 62 negative acknowlegde +* <- A0 reponse gotnak +* and receive headblock again +* datablocks +* <- [16384-byte-block] cc_cc datablock +* -> 60 positive acknowledge +* <- A0 response gotack +* repeat receive for 26 datablocks of 16384 bytes. for each datablock +* do retry on checksum mismatch like above for headblock. +* database info +* -> 18 command getinfo +* <- 90 response gotcmd +* <- 30 31 32 38 infoblock-1 +* -> 18 command getinfo +* <- 90 response gotcmd +* <- 30 31 32 38 infoblock-2 +*/ + +/* read IC35 database to file +* -------------------------- +*/ +static int +_readdatabase( char * fname, FILE * fp, uchar * buff ) +{ + ulong frlen; + int rlen; + int i; + + LPRINTF(( L_INFO, "readdatabase %s ..", fname )); + + /* send backup command and wait for IC35 response */ + if ( Mcmdrsp( MCMDbackup, MRSPgotcmd ) != OK ) { + error( "readdatabase backup command failed" ); + return ERR; + } + /* receive head-,data-blocks from IC35 and write to file */ + frlen = 0; + for ( i = 0; i <= NDATABLKS; ++i ) { + char btext[24+1]; + size_t blen; + if ( i == 0 ) { + fprintf( stderr, "read database -> %s head ", fname ); + strcpy( btext, "headblock" ); + blen = HEADBLKSZ; + } else { + sprintf( btext, "datablock-%d", i ); + blen = DATABLKSZ; + } + if ( (rlen = Mrecvblk( buff, blen )) != blen ) { + fprintf( stderr, "\n" ); + error( "readdatabase %s %s", btext, _dbetext( rlen ) ); + return ERR; + } + frlen += rlen; + fprintf( stderr, "\rread database -> %s %ldk ", + fname, (frlen+511)/1024 ); + if ( fwrite( buff, sizeof(buff[0]), rlen, fp ) != rlen ) { + fprintf( stderr, "\n" ); + error( "readdatabase %s %s: write error %s (%d)", + btext, fname, strerror(errno), errno ); + return ERR; + } + } + /* receive database info from IC35 and write to file */ + fprintf( stderr, "\nread database -> %s info ", fname ); + for ( i = 1; i <= NINFOBLKS; ++i ) { + if ( (rlen = _getdbinfo( buff, INFOBLKSZ )) != INFOBLKSZ ) { + fprintf( stderr, "\n" ); + error( "readdatabase info-%d recv.error/timeout", i ); + return ERR; + } + if ( fwrite( buff, sizeof(buff[0]), rlen, fp ) != rlen ) { + fprintf( stderr, "\n" ); + error( "readdatabase info-%d %s: write error %s (%d)", + i, fname, strerror(errno), errno ); + return ERR; + } + } + fprintf( stderr, "\n" ); + + LPRINTF(( L_INFO, "readdatabase %s OK", fname )); + return OK; +} + +/* read IC35 database: init,free resources +* --------------------------------------- +*/ +int +readdatabase( char * fname ) +{ + uchar * buff; + FILE * fp; + int rval; + + if ( (buff = malloc( DATABLKSZ )) != NULL ) { + if ( fname && *fname + && (fp = fopen( fname, "w" )) != NULL ) { + rval = _readdatabase( fname, fp, buff ); + fclose( fp ); + } else { + error( "readdatabase cannot open %s", fname ? fname : "(NULL)" ); + rval = ERR; + } + free( buff ); + } else { + error( "readdatabase failed: no memory (%d)", DATABLKSZ ); + rval = ERR; + } + return rval; +} + + +/* IC35 database restore from file +* =============================== +* communication PC <-> IC35: +* database info +* -> 18 command getinfo +* <- 90 response gotcmd +* <- 30 31 32 38 infoblock-1 +* -> 18 command getinfo +* <- 90 response gotcmd +* <- 30 31 32 38 infoblock-2 +* check both infoblocks matching with tail of database file, +* on mismatch abort and do not write database to IC35. +* restore commands +* -> 14 command restore-1 +* <- 90 response gotcmd +* -> 70 command restore-2 +* <- C0 response restore-2 +* on receive error / timeout retry sending command-1/2. +* headblock +* -> [136-byte-block] headblock +* <- cc_cc response checksum +* -> 60 positive acknowledge +* <- A0 response gotack +* or on checksum mismatch: +* -> 62 negative acknowlegde +* <- A0 reponse gotnak +* and send headblock again. +* datablocks +* -> [16384-byte-block] datablock +* <- cc_cc response checksum +* -> 60 positive acknowledge +* <- A0 response gotack +* repeat send for 26 datablocks of 16384 bytes. for each datablock do +* retry on checksum mismatch like above for headblock. +* after all data is written to IC35, wait 3.25 sec to +* avoid failure in mdisconnect(). +* warning: IC35 does NOT restore phonetype and date+time, +* they must be set manually after restore from file ! +*/ + +/* write IC35 database from file +* ----------------------------- +*/ +static int +_writedatabase( char * fname, FILE * fp, uchar * buff ) +{ + ulong fwlen; + int wlen; + int i; + + LPRINTF(( L_INFO, "writedatabase %s ..", fname )); + + /* check info from IC35 matching info in database file */ + fprintf( stderr, "write database <> %s info ", fname ); + fseek( fp, -2*INFOBLKSZ, SEEK_END ); + for ( i = 1; i <= NINFOBLKS; ++i ) { + uchar info[INFOBLKSZ]; + if ( (wlen = _getdbinfo( buff, INFOBLKSZ )) != INFOBLKSZ ) { + fprintf( stderr, "\n" ); + error( "writedatabase info-%d recv.error/timeout", i ); + return ERR; + } + if ( fread( info, sizeof(info[0]), INFOBLKSZ, fp ) != INFOBLKSZ ) { + fprintf( stderr, "\n" ); + error( "writedatabase info-%d %s: read error %s (%d)", + i, fname, strerror(errno), errno ); + return ERR; + } + if ( memcmp( buff, info, INFOBLKSZ ) != 0 ) { + char * hextext; + int j; + fprintf( stderr, "\n" ); + if ( (hextext = malloc( INFOBLKSZ*3 + 1 )) != NULL ) { + strcpy( hextext, "" ); + for ( j = 0; j < INFOBLKSZ; ++j ) + sprintf( hextext+strlen(hextext), "%02X ", buff[j] ); + message( "%s IC35", hextext ); + strcpy( hextext, "" ); + for ( j = 0; j < INFOBLKSZ; ++j ) + sprintf( hextext+strlen(hextext), "%02X ", info[j] ); + message( "%s %s", hextext, fname ); + free( hextext ); + } + error( "writedatabase info-%d mismatch", i ); + } + } + rewind( fp ); + + /* send restore commands and wait for IC35 responses */ + fprintf( stderr, "\n" ); + if ( Mcmdrsp( MCMDrestinit, MRSPgotcmd ) < 0 + || Mcmdrsp( MCMDrestdata, MRSPrestdata ) < 0 ) { + error( "writedatabase restore command failed" ); + return ERR; + } + /* read headblock,datablocks from file and send to IC35 */ + fwlen = 0; + for ( i = 0; i <= NDATABLKS; ++i ) { + char btext[24+1]; + size_t blen; + if ( i == 0 ) { + fprintf( stderr, "write database <- %s head ", fname ); + strcpy( btext, "headblock" ); + blen = HEADBLKSZ; + } else { + sprintf( btext, "datablock-%d", i ); + blen = DATABLKSZ; + } + if ( fread( buff, sizeof(buff[0]), blen, fp ) != blen ) { + fprintf( stderr, "\n" ); + error( "writedatabase %s %s: read error %s (%d)", + btext, fname, strerror(errno), errno ); + return ERR; + } + if ( (wlen = Msendblk( buff, blen )) != blen ) { + fprintf( stderr, "\n" ); + error( "writedatabase %s %s", btext, _dbetext( wlen ) ); + return ERR; + } + fwlen += wlen; + fprintf( stderr, "\rwrite database <- %s %ldk ", + fname, (fwlen+511)/1024 ); + } + fprintf( stderr, "\n" ); + usleep( 3250000 ); /* wait 3.25 sec for IC35 doing restore */ + + LPRINTF(( L_INFO, "writedatabase %s OK", fname )); + return OK; +} + +/* write IC35 database: init,free resources +* ---------------------------------------- +*/ +int +writedatabase( char * fname ) +{ + uchar * buff; + FILE * fp; + int rval; + + if ( (buff = malloc( DATABLKSZ )) != NULL ) { + if ( fname && *fname + && (fp = fopen( fname, "r" )) != NULL ) { + rval = _writedatabase( fname, fp, buff ); + fclose( fp ); + } else { + error( "writedatabase cannot open %s", fname ? fname : "(NULL)" ); + rval = ERR; + } + free( buff ); + } else { + error( "writedatabase failed: no memory (%d)", DATABLKSZ ); + rval = ERR; + } + return rval; +} diff --git a/src/mgrtrans.h b/src/mgrtrans.h new file mode 100644 index 0000000..ab74628 --- /dev/null +++ b/src/mgrtrans.h @@ -0,0 +1,52 @@ +/************************************************************************ +* Copyright (C) 2001 Thomas Schulz * +* * +* $Id: mgrtrans.h,v 1.7 2001/11/20 23:08:35 thosch Exp $ * +* * +* header for IC35 manager transactions * +* * +************************************************************************/ +#ifndef _MGRTRANS_H +#define _MGRTRANS_H 1 + +#include /* time_t */ + +#include "util.h" /* uchar,ulong, .. */ +#include "mgrproto.h" /* MMCattr*, open modes */ + + +typedef struct mmcdir MMCDIR; +typedef struct mmcdirent { /* exported MMC directory entry */ + char name[8+1+3+1]; /* filename.ext */ + uchar attr; /* file attributes */ + ulong tstamp; /* timestamp (DOS-format) */ + ulong size; /* size in bytes */ +} MMCDIRENT; + +typedef struct mmcfile MMCFILE; + + +int mconnect( char * devname, char * statfname ); +int mdisconnect( void ); + +int mmc_status( int mmcnum ); +int mmc_label( int mmcnum, char label[11+1] ); + +MMCDIR * mmc_opendir( char * dirpath ); +MMCDIR * mmc_createdir( char * dirpath ); +MMCDIRENT * mmc_readdir( MMCDIR * dirp ); +int mmc_closedir( MMCDIR * dirp ); +char * mmctstampstr( ulong tstamp ); +time_t mmctstampunixtime( ulong tstamp ); + +int mmc_delfile( char * filepath ); +MMCFILE * mmc_openfile( char * filepath, ushort mode ); +MMCDIRENT * mmc_statfile( MMCFILE * fp ); +int mmc_readfile( MMCFILE * fp, uchar * buff, size_t blen ); +int mmc_writefile( MMCFILE * fp, uchar * data, size_t dlen ); +int mmc_closefile( MMCFILE * fp ); + +int readdatabase( char * fname ); +int writedatabase( char * fname ); + +#endif /*_MGRTRANS_H*/ diff --git a/src/port.h b/src/port.h new file mode 100644 index 0000000..1706547 --- /dev/null +++ b/src/port.h @@ -0,0 +1,94 @@ +/*************************************************************************** +(C) Copyright 1996 Apple Computer, Inc., AT&T Corp., International +Business Machines Corporation and Siemens Rolm Communications Inc. + +modified 2000 by Thomas Schulz: +$Id: port.h,v 1.3 2001/02/10 03:08:41 tsch Rel $ + +For purposes of this license notice, the term Licensors shall mean, +collectively, Apple Computer, Inc., AT&T Corp., International +Business Machines Corporation and Siemens Rolm Communications Inc. +The term Licensor shall mean any of the Licensors. + +Subject to acceptance of the following conditions, permission is hereby +granted by Licensors without the need for written agreement and without +license or royalty fees, to use, copy, modify and distribute this +software for any purpose. + +The above copyright notice and the following four paragraphs must be +reproduced in all copies of this software and any software including +this software. + +THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS AND NO LICENSOR SHALL HAVE +ANY OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS OR +MODIFICATIONS. + +IN NO EVENT SHALL ANY LICENSOR BE LIABLE TO ANY PARTY FOR DIRECT, +INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT +OF THE USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +EACH LICENSOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO ANY WARRANTY OF NONINFRINGEMENT OR THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. + +The software is provided with RESTRICTED RIGHTS. Use, duplication, or +disclosure by the government are subject to restrictions set forth in +DFARS 252.227-7013 or 48 CFR 52.227-19, as applicable. + +***************************************************************************/ + +#ifndef __PORT_H__ +#define __PORT_H__ 1 + + +#if defined(__CPLUSPLUS__) || defined(__cplusplus) +extern "C" { +#endif + +/* some of these #defines are commented out because */ +/* Visual C++ sets them on the compiler command line instead */ + +/* #define _DEBUG */ +/* #define WIN32 */ +/* #define WIN16 */ +/* #define _WINDOWS */ +/* #define __MWERKS__ */ +/* #define INCLUDEMFC */ + +#define vCardClipboardFormat "+//ISBN 1-887687-00-9::versit::PDI//vCard" +#define vCalendarClipboardFormat "+//ISBN 1-887687-00-9::versit::PDI//vCalendar" + +/* The above strings vCardClipboardFormat and vCalendarClipboardFormat +are globally unique IDs which can be used to generate clipboard format +ID's as per the requirements of a specific platform. For example, in +Windows they are used as the parameter in a call to RegisterClipboardFormat. +For example: + + CLIPFORMAT foo = RegisterClipboardFormat(vCardClipboardFormat); + +*/ + +#define vCardMimeType "text/x-vCard" +#define vCalendarMimeType "text/x-vCalendar" + +#define DLLEXPORT(t) t + +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif + +#define stricmp strcasecmp +#ifdef __STRICT_ANSI__ +#include "util.h" /* strcasecmp() */ +#endif + +#if defined(__CPLUSPLUS__) || defined(__cplusplus) +} +#endif + +#endif /* __PORT_H__ */ diff --git a/src/synproto.c b/src/synproto.c new file mode 100644 index 0000000..8e0b6b3 --- /dev/null +++ b/src/synproto.c @@ -0,0 +1,649 @@ +/************************************************************************ +* Copyright (C) 2000 Thomas Schulz * +* */ + static char rcsid[] = + "$Id: synproto.c,v 1.11 2001/06/17 23:37:36 tsch Rel $"; /* +* * +* IC35 synchronize protocol * +* * +************************************************************************* +* * +* ??? is "fixme" mark: sections of code needing fixes * +* * +************************************************************************/ + +#include /* sprintf(), .. */ +#include /* va_start(), .. */ +#include /* memcpy(), strlen() ..*/ +#include /* struct tm, time(),.. */ +#include /* size_t, .. */ + +#include "util.h" /* ERR,OK, uchar, .. */ +#include "comio.h" /* com_send(), .. */ +#include "genproto.h" /* putxxx(),getxxx() .. */ +#include "synproto.h" /* Level-4 commands, .. */ +NOTUSED(rcsid) + + +/* Level-1 PDU Ids */ +#define L1INIT 0x01 /* initialize 01 03 00 */ +#define L1DSEL 0x02 /* data select 02 ll ll cc cc */ +#define L1DREQ 0x04 /* data request 04 03 00 */ +#define L1EXIT 0x05 /* exit 05 03 00 */ +#define L1ACK0 0xF0 /* ack to initialize */ +#define L1ACK1 0xF1 /* ack to datasel,exit */ +#define L1DATA 0xF2 /* data response F2 ll ll cc cc */ + +/* Level-2 PDU Ids */ +#define L2INIT 0x80 /* cmd,rsp: identification */ +#define L2EXIT 0x81 /* cmd: disconnect */ +#define L2WMORE 0x02 /* cmd: write non-last of multiblk record */ +#define L2WLAST 0x82 /* cmd: write last of multi-block record */ +#define L2READ 0x83 /* cmd: read more of multi-block record */ +#define L2RMORE 0x20 /* rsp: non-last data of multi-block record */ +#define L2RLAST 0xA0 /* rsp: last data of multi-block record */ +#define L2RCONT 0x90 /* rsp: write more of multi-block record */ + +/* Level-3 PDU Ids */ +#define L3MORE 0x48 /* non-last of multi-block */ +#define L3LAST 0x49 /* last of multi-block or single */ +#define L3IDENT 0x4A /* identification */ + +/* Level-4 PDU definitions */ + +#define pdulenof(type,first,last) \ + ( offsetof(type,last) + sizeof(((type*)0)->last) \ + - offsetof(type,first) ) + +struct hdr { + uchar id; /* id */ + uchar len[2]; /* ll ll */ +}; +union recid { + uchar recid[4]; /* re_cd_id fi IC35 record-ID */ + struct { + uchar rec[3]; /* re_cd_id record-id part */ + uchar file[1]; /* fi file-id part */ + } id; +}; + +struct cmdident { + struct hdr l1head; /* 02 ll ll */ + struct hdr l2head; /* 80 ll ll */ + uchar magic[4]; /* 10 00 64 00 */ + struct hdr l3head; /* 4A ll ll */ + char text[28]; /* "INVENTEC CORPORATION PRODUCT" */ +}; +#define LCMDident pdulenof(struct cmdident, magic, text) +struct rspident { + struct hdr l1head; /* F2 ll ll */ + struct hdr l2head; /* A0 ll ll */ + uchar magic[4]; /* 10 00 D0 07 */ + struct hdr l3head; /* 4A ll ll */ + char text[31]; /* "INVENTEC CORPORATION DCS15 1.28" */ +}; + +struct cmdgetpower { + uchar cmd[2]; /* 03 01 */ + char text[5]; /* "Power" */ + uchar zero[3]; /* 00 00 00 */ +}; +#define LCMDgetpower pdulenof(struct cmdgetpower, cmd, zero) +struct rspgetpower { + uchar rsp[2]; /* 03 01 */ +}; +struct cmdpassword { + uchar cmd[2]; /* 03 00 */ + char text[8]; /* [password] */ +}; +#define LCMDpassword pdulenof(struct cmdpassword, cmd, text) +struct rsppassword { + uchar rsp[2]; /* 01 01 */ +}; +struct cmdcategory { + uchar cmd[2]; /* 03 02 */ + char name[8]; /* [category] */ +}; +#define LCMDcategory pdulenof(struct cmdcategory, cmd, name) +struct rspcategory { + uchar rsp[2]; /* 00 01 */ +}; +struct cmdgetdtime { + uchar cmd[2]; /* 02 00 */ + uchar zero[1]; /* 00 */ +}; +#define LCMDgetdtime pdulenof(struct cmdgetdtime, cmd, zero) +struct rspgetdtime { + uchar mdyhms[14]; /* [mmddyyyyhhmmss] */ + uchar zero[2]; /* 00 00 */ +}; +struct cmdsetdtime { + uchar cmd[2]; /* 02 01 */ + uchar zero1[1]; /* 00 */ + char mdyhms[14]; /* [mmddyyyyhhmmss] */ + uchar zero2[2]; /* 00 00 */ +}; +#define LCMDsetdtime pdulenof(struct cmdsetdtime, cmd, zero2) +/* rspsetdtime is done only */ + +struct cmdfopen { + uchar cmd[2]; /* 00 02 */ + uchar zero[7]; /* 00 00 00 00 00 00 00 */ + uchar lf2[4]; /* length+2 (dword) */ + uchar lf[1]; /* length of filename */ + uchar fname[10+1]; /* [filename] 02 */ +}; +#define LCMDfopen (pdulenof(struct cmdfopen, cmd, lf) + 1) /* dynamic */ +struct rspfopen { + uchar fd[2]; /* fd __ */ +}; +struct cmdfclose { + uchar cmd[2]; /* 00 03 */ + uchar fd[2]; /* fd __ */ + uchar zero[4]; /* 00 00 00 00 */ +}; +#define LCMDfclose pdulenof(struct cmdfclose, cmd, zero) +/* rspfclose is done only */ + +struct cmdfgetlen { + uchar cmd[2]; /* 01 03 (or 01 04) */ + uchar fd[2]; /* fd __ */ + uchar zero[4]; /* 00 00 00 00 */ +}; +#define LCMDfgetlen pdulenof(struct cmdfgetlen, cmd, zero) +struct rspfgetlen { + uchar n[2]; /* n_ __ */ +}; +struct cmdfgetrec { + uchar cmd[2]; /* 01 06 (or 01 07) */ + uchar fd[2]; /* fd __ */ + uchar index[2]; /* id x_ (not 01 07) */ + uchar zero[2]; /* 00 00 00 00 */ +}; +#define LCMDfgetrec pdulenof(struct cmdfgetrec, cmd, zero) +struct cmdfgetirec { + uchar cmd[2]; /* 01 05 */ + uchar fd[2]; /* fd __ */ + union recid uid; /* re c- id fi IC35 record-ID */ +}; +#define LCMDfgetirec pdulenof(struct cmdfgetirec, cmd, uid) +struct rspfgetrec { + union recid uid; /* re c. id fi IC35 record-ID */ + uchar fx[1]; /* fx change-flag on IC35 ? */ + uchar data[1]; /* flens,fdata.. (variable len) */ +}; +struct cmdfputrec { /* putrec or updrec */ + uchar cmd[2]; /* 01 08 01 09 */ + uchar fd[2]; /* fd __ */ + union recid uid; /* 00 00 00 00 or re c- id fi */ + uchar magic[3]; /* m1 m2 m3 */ + uchar zero2[2]; /* 00 00 */ + uchar lr[4]; /* length of record (dword) */ + uchar data[1]; /* flens,fdata.. (variable len) */ +}; +#define LCMDfputrec pdulenof(struct cmdfputrec, cmd, lr) /* dynamic */ +struct rspfputrec { + union recid uid; /* re_cd_id fi IC35 record-ID */ +}; +struct cmdfclrchg { /* clrchg or delrec */ + uchar cmd[2]; /* 01 08 01 02 */ + uchar fd[2]; /* fd __ */ + union recid uid; /* re c- id fi IC35 record-ID */ +}; +#define LCMDfclrchg pdulenof(struct cmdfclrchg, cmd, uid) +/* rspfclrchg is done only */ + +struct cmdpdu { + struct hdr l1head; /* 01,02,04,05 */ + struct hdr l2head; /* 80,02,82,83,81 */ + struct hdr l3head; /* 48,49 */ + union { + uchar cmd[2]; + struct cmdgetpower getpower; + struct cmdpassword password; + struct cmdcategory category; + struct cmdgetdtime getdtime; + struct cmdsetdtime setdtime; + struct cmdfopen fopen; + struct cmdfclose fclose; + struct cmdfgetlen fgetlen; + struct cmdfgetrec fgetrec; + struct cmdfgetirec fgetirec; + struct cmdfputrec fputrec; + struct cmdfclrchg fclrchg; + } u; +}; +struct rsppdu { + struct hdr l1head; /* F0,F1,F2 */ + struct hdr l2head; /* 20,A0,90 */ + struct hdr l3head; /* 48,49 */ + union { + uchar rsp[2]; + struct rspgetpower getpower; + struct rsppassword password; + struct rspcategory category; + struct rspgetdtime getdtime; + /* rspsetdtime response done only */ + struct rspfopen fopen; + /* rspfclose response done only */ + struct rspfgetlen fgetlen; + struct rspfgetrec fgetrec; + struct rspfputrec fputrec; + /* rspfclrchg response done only */ + } u; +}; + + +/* ============================================ */ +/* utilities for PDU encode/decode */ +/* ============================================ */ + +/* encode data items to PDU +* ------------------------ +* putbyte() etc. imported from genproto.c +*/ +static int +puthdr( uchar * pduptr, uchar id, int len ) +{ + putbyte( pduptr+0, id ); + putword( pduptr+1, (ushort)(len+3) ); + return len+3; +} +static void +putcmd( uchar * pduptr, ushort cmd ) +{ + putbyte( pduptr+0, (cmd & 0xFF00) >> 8 ); /* MSB first .. */ + putbyte( pduptr+1, cmd & 0x00FF ); /* LSB second (!) */ +} + +/* decode data items from PDU +* -------------------------- +* getbyte() etc. imported from genproto.c +*/ +static ushort +getrsp( uchar * pduptr ) +{ + return (getbyte( pduptr+0 ) << 8) | getbyte( pduptr+1 ); +} + + +/* ==================================== */ +/* Level-1 protocol */ +/* ==================================== */ +/* +* the Level-1 protocol transports a command to IC35 (->) +* and receives a response from IC35 (<-): +* L2sendcmd: +* use default timeout (0.5 sec) +* -> 01 03 00 init +* <- F0 ack0 +* -> 02 ll ll cc cc command to IC35 +* set timeout 2.0 sec for ack1 response +* <- F1 ack1 +* restore previous timeout (0.5 sec) +* L2recvrsp: +* -> 04 03 00 request response +* <- F2 ll ll cc cc response from IC35 +* -> 05 03 00 exit +* <- F1 ack1 +* longer timeout 2.0 sec for ack1 response to command is needed +* e.g. for get_modflen. experiments showed ack1 response times +* of 0.7 .. 0.9 sec for ca. 860 address records. +*/ + +static uchar xbuff[4096]; /* PDU transmit buffer */ + +/* send Level-1 PDU +* ---------------- +*/ +static int +L1send( uchar id, uchar* pdu, size_t l2len ) +{ + size_t slen; + uchar * spdu; + uchar sbuff[3]; + + if ( pdu != NULL ) { /* long PDU with data and checksum */ + slen = puthdr( spdu = pdu, id, l2len+2 ); + putword( spdu+slen-2, chksum( spdu, slen-2 ) ); + } else { /* short PDU without data,checksum */ + slen = puthdr( spdu = sbuff, id, 0 ); + } + return com_send( spdu, slen ); +} + +/* receive Level-1 PDU +* ------------------- +* returns: +* <= 0 receive error: timeout, bad checksum of long PDU +* == 0 short PDU received, *p_id = PDU-Id +* > 0 long PDU received, *p_id = PDU-Id, *buff = PDU-data +*/ +static int +L1recv( uchar* prid, uchar * pdu, size_t blen ) +{ + int rlen, pdulen; + + if ( prid == NULL ) + return ERR; /* semantic error */ + *prid = '\0'; + rlen = com_recv( prid, sizeof(*prid) ); + if ( rlen < 1 ) + return rlen; /* recv.ERR, timeout */ + if ( *prid != L1DATA ) + return OK; /* received ack0,ack1 */ + + if ( pdu == NULL ) { + /*??? flush receive data */ + return ERR; /* semantic error */ + } + pdu[0] = *prid; + rlen = com_recv( pdu+1, 2 ); + if ( rlen < 2 ) + return ERR; /* miss length field */ + pdulen = getword( pdu+1 ); + if ( pdulen <= 3 ) + return ERR; /* too short data resp */ + rlen = com_recv( pdu+3, min(pdulen,blen)-3 ); + if ( rlen < pdulen - 3 ) + return ERR; /* length mismatch */ + if ( chksum( pdu, pdulen-2 ) != getword( pdu+pdulen-2 ) ) + return ERR; /* checksum mismatch */ + + return pdulen - 3 - 2; /* length of L2data */ +} + + +/* send command with Level-1 protocol +* ---------------------------------- +*/ +static int +L2sendcmd( uchar* pdu, size_t l2len ) +{ + uchar rid; + int old_tmo; + int rval; + + rval = OK; + L1send( L1INIT, NULL, 0 ); /* -> 01 03 00 */ + L1recv( &rid, NULL, 0 ); /* <- F0 */ + if ( rid != L1ACK0 ) /* missing ack0 */ + return ERR; + L1send( L1DSEL, pdu, l2len ); /* -> 02 ll ll cc cc */ + old_tmo = com_settimeout( 2000 ); /* timeout 2.0 sec for ack1resp */ + L1recv( &rid, NULL, 0 ); /* <- F1 */ + com_settimeout( old_tmo ); /* restore previous timeout */ + if ( rid != L1ACK1 ) /* missing ack1 */ + return ERR; + return rval; +} + +/* receive response with Level-1 protocol +* -------------------------------------- +*/ +static int +L2recvrsp( uchar* rsp, size_t lrsp ) +{ + int rlen, rval; + uchar rid; + + rval = ERR; + L1send( L1DREQ, NULL, 0 ); /* -> 04 03 00 */ + rlen = L1recv( &rid, rsp, lrsp ); /* <- F2 ll ll cc cc */ + if ( rid == L1DATA ) + rval = rlen; /* return length of L2data */ + L1send( L1EXIT, NULL, 0 ); /* -> 05 03 00 */ + L1recv( &rid, NULL, 0 ); /* <- F1 */ + /*??? ignore if missing ack1 to exit */ + return rval; +} + + +/* ==================================== */ +/* Level-4 commands,responses */ +/* ==================================== */ + +/* encode command and send it +* -------------------------- +* depending on the command-id 'cmd' zero, one or more parameters +* are encoded. +*/ +int +sendcmd( ushort cmd, ... ) +{ + va_list argp; + struct cmdpdu * pdu = (struct cmdpdu *)&xbuff[0]; + size_t pdusize = sizeof(xbuff); + size_t pdulen; + uchar l2id, l3id; + + memset( pdu, 0, pdusize ); + putcmd( pdu->u.cmd, cmd ); + va_start( argp, cmd ); + switch ( cmd ) { + default: + return ERR; /* invalid 'cmd' */ + case CMDident: + { struct cmdident *idpdu = (struct cmdident *)pdu; + char * inventec = "INVENTEC CORPORATION PRODUCT"; + putbtxt( idpdu->magic, "\x10\x00\x64\x00", sizeof(idpdu->magic) ); + puttext( idpdu->text, inventec ); + puthdr( (uchar*)&idpdu->l3head, L3IDENT, strlen(inventec) ); + pdulen = LCMDident; + l2id = L2INIT; l3id = 0; + } break; + case CMDdisconn: + pdulen = 0; + l2id = L2EXIT; l3id = 0; + break; + case CMDgetpower: + puttext( pdu->u.getpower.text, "Power" ); + pdulen = LCMDgetpower; + l2id = L2WLAST; l3id = L3LAST; + break; + case CMDpassword: + puttext( pdu->u.password.text, va_arg( argp, char* ) ); + pdulen = LCMDpassword; + l2id = L2WLAST; l3id = L3LAST; + break; + case CMDcategory: + puttext( pdu->u.category.name, va_arg( argp, char* ) ); + pdulen = LCMDcategory; + l2id = L2WLAST; l3id = L3LAST; + break; + case CMDgetdtime: + pdulen = LCMDgetdtime; + l2id = L2READ; l3id = L3LAST; + break; + case CMDsetdtime: + puttext( pdu->u.setdtime.mdyhms, va_arg( argp, char* ) ); + pdulen = LCMDsetdtime; + l2id = L2WLAST; l3id = L3LAST; + break; + case CMDfopen: + { char * strarg = va_arg( argp, char* ); + putdword( pdu->u.fopen.lf2, (ulong)(strlen(strarg) + 2) ); + putbyte( pdu->u.fopen.lf, (uchar)strlen(strarg) ); + puttext( pdu->u.fopen.fname, strarg ); + putbyte( pdu->u.fopen.fname+strlen(strarg), '\x02' ); + pdulen = LCMDfopen + strlen(strarg); + l2id = L2WLAST; l3id = L3LAST; + } break; + case CMDfclose: + putword( pdu->u.fclose.fd, (ushort)va_arg( argp, int ) ); + pdulen = LCMDfclose; + l2id = L2WLAST; l3id = L3LAST; + break; + case CMDfgetlen: + case CMDfgetmlen: + putword( pdu->u.fgetlen.fd, (ushort)va_arg( argp, int ) ); + pdulen = LCMDfgetlen; + l2id = L2READ; l3id = L3LAST; + break; + case CMDfgetirec: + putword( pdu->u.fgetirec.fd, (ushort)va_arg( argp, int ) ); + putdword( pdu->u.fgetirec.uid.recid, va_arg( argp, ulong ) ); + pdulen = LCMDfgetirec; + l2id = L2READ; l3id = L3LAST; + break; + case CMDfgetrec: + putword( pdu->u.fgetrec.fd, (ushort)va_arg( argp, int ) ); + putword( pdu->u.fgetrec.index, (ushort)va_arg( argp, int ) ); + pdulen = LCMDfgetrec; + l2id = L2READ; l3id = L3LAST; + break; + case CMDfgetmrec: + putword( pdu->u.fgetrec.fd, (ushort)va_arg( argp, int ) ); + pdulen = LCMDfgetrec; + l2id = L2READ; l3id = L3LAST; + break; + case CMDfgetmore: + pdulen = 0; + l2id = L2READ; l3id = 0; + break; + case CMDfputrec: + case CMDfupdrec: + { bool last = va_arg( argp, bool ); + uchar * bstrarg; + size_t bstrlen; + putword( pdu->u.fputrec.fd, (ushort)va_arg( argp, int ) ); + putdword( pdu->u.fputrec.uid.recid, va_arg( argp, ulong ) ); + putbtxt( pdu->u.fputrec.magic, va_arg( argp, uchar* ), + sizeof(pdu->u.fputrec.magic) ); + putdword( pdu->u.fputrec.lr, va_arg( argp, size_t ) ); + bstrarg = va_arg( argp, uchar* ); + bstrlen = va_arg( argp, size_t ); + putbtxt( pdu->u.fputrec.data, bstrarg, bstrlen ); + pdulen = LCMDfputrec + bstrlen; + if ( ! last ) + l2id = L2WMORE, l3id = L3MORE; + else + l2id = L2WLAST, l3id = L3LAST; + } break; + case CMDfputmore: + { bool last = va_arg( argp, bool ); + uchar * bstrarg = va_arg( argp, uchar* ); + size_t bstrlen = va_arg( argp, size_t ); + putbtxt( pdu->u.cmd, bstrarg, bstrlen ); + pdulen = bstrlen; + if ( ! last ) + l2id = L2WMORE, l3id = L3MORE; + else + l2id = L2WLAST, l3id = L3LAST; + } break; + case CMDfdelrec: + case CMDfclrchg: + putword( pdu->u.fclrchg.fd, (ushort)va_arg( argp, int ) ); + putdword( pdu->u.fclrchg.uid.recid, va_arg( argp, ulong ) ); + pdulen = LCMDfclrchg; + l2id = L2WLAST; l3id = L3LAST; + break; + } + va_end( argp ); + if ( l3id ) + pdulen = puthdr( (uchar*)&pdu->l3head, l3id, pdulen ); + pdulen = puthdr( (uchar*)&pdu->l2head, l2id, pdulen ); + return L2sendcmd( (uchar*)pdu, pdulen ); +} + +/* receive response and decode it +* ------------------------------ +* ??? check received l2id,l3id match with cmd acc.to protocol +*/ +int +recvrsp( ushort cmd, ... ) +{ + va_list argp; + uchar l2id, l3id; + ushort rsp; + int pdulen; + size_t pdusize = sizeof(xbuff); + struct rsppdu * pdu = (struct rsppdu *)&xbuff[0]; + + va_start( argp, cmd ); + memset( pdu, 0, pdusize ); + if ( (pdulen = L2recvrsp( (uchar*)pdu, pdusize )) < 0 ) + return pdulen; + l2id = getbyte( (uchar*)&pdu->l2head ); + l3id = 0x00; + if ( pdulen >= sizeof(pdu->l2head) ) { + pdulen -= sizeof(pdu->l2head); + l3id = getbyte( (uchar*)&pdu->l3head ); + if ( pdulen >= sizeof(pdu->l3head) ) + pdulen -= sizeof(pdu->l3head); + } + rsp = getrsp( pdu->u.rsp ); + switch ( cmd ) { + default: + return ERR; /* invalid 'cmd' */ + case CMDident: + { struct rspident *idpdu = (struct rspident *)pdu; + char * ptext = va_arg( argp, char* ); + size_t lptext = va_arg( argp, size_t ); + pdulen -= sizeof(idpdu->magic); + gettext( idpdu->text, ptext, min(pdulen, lptext) ); + } break; + case CMDdisconn: + break; + case CMDgetpower: + { ushort * pstate = va_arg( argp, ushort* ); + *pstate = rsp; + } break; + case CMDpassword: + { ushort * pstate = va_arg( argp, ushort* ); + *pstate = rsp; + } break; + case CMDgetdtime: + { char * pdtime = va_arg( argp, char* ); + size_t lpdtime = va_arg( argp, size_t ); + gettext( pdu->u.getdtime.mdyhms, pdtime, + min(sizeof(pdu->u.getdtime.mdyhms), lpdtime) ); + } break; + case CMDsetdtime: + break; + case CMDcategory: + { ushort * pstate = va_arg( argp, ushort* ); + *pstate = rsp; + } break; + case CMDfopen: + { ushort * pfd = va_arg( argp, ushort* ); + *pfd = getword( pdu->u.fopen.fd ); + } break; + case CMDfclose: + break; + case CMDfgetlen: + case CMDfgetmlen: + { ushort * pflen = va_arg( argp, ushort* ); + *pflen = getword( pdu->u.fgetlen.n ); + } break; + case CMDfgetrec: + { bool * plast = va_arg( argp, bool* ); + ulong * prid = va_arg( argp, ulong* ); + uchar * pchg = va_arg( argp, uchar* ); + uchar * buff = va_arg( argp, uchar* ); + size_t blen = va_arg( argp, size_t ); + *plast = (bool)( l2id == L2RLAST ); + *prid = getdword( pdu->u.fgetrec.uid.recid ); + *pchg = getbyte( pdu->u.fgetrec.fx ); + memcpy( buff, pdu->u.fgetrec.data, min(blen,pdulen) ); + pdulen -= offsetof(struct rspfgetrec, data); /* length of record data */ + } break; + case CMDfgetmore: + { bool * plast = va_arg( argp, bool* ); + uchar * buff = va_arg( argp, uchar* ); + size_t blen = va_arg( argp, size_t ); + *plast = (bool)( l2id == L2RLAST ); + memcpy( buff, pdu->u.rsp, min(blen,pdulen) ); + } break; + case CMDfputrec: + if ( l2id == L2RLAST && l3id == L3LAST ) { + ulong * prid = va_arg( argp, ulong* ); + *prid = getdword( pdu->u.fputrec.uid.recid ); + } + break; + case CMDfdelrec: + case CMDfclrchg: + break; + } + return pdulen; +} diff --git a/src/synproto.h b/src/synproto.h new file mode 100644 index 0000000..c09d777 --- /dev/null +++ b/src/synproto.h @@ -0,0 +1,44 @@ +/************************************************************************ +* Copyright (C) 2000 Thomas Schulz * +* * +* $Id: synproto.h,v 1.3 2000/12/03 07:50:44 tsch Rel $ * +* * +* header for IC35 synchronize protocol * +* * +************************************************************************/ +#ifndef _SYNPROTO_H +#define _SYNPROTO_H 1 + +#include "util.h" /* ushort */ + + +/* Level-4 commands,responses */ +#define CMDident 0x1000 +#define CMDpassword 0x0300 +#define RSPpasswdOK 0x0101 +#define CMDgetpower 0x0301 +#define RSPpowerOK 0x0301 +#define CMDcategory 0x0302 +#define RSPcategOK 0x0001 +#define CMDgetdtime 0x0200 +#define CMDsetdtime 0x0201 +#define CMDfopen 0x0002 +#define CMDfclose 0x0003 +#define CMDfdelrec 0x0102 /* delete record */ +#define CMDfgetlen 0x0103 /* get total number of records */ +#define CMDfgetmlen 0x0104 /* get number of modified records */ +#define CMDfgetirec 0x0105 /* read record by recID */ +#define CMDfgetrec 0x0106 /* read record by index */ +#define CMDfgetmrec 0x0107 /* read next modified record */ +#define CMDfgetmore 0x83 +#define CMDfputrec 0x0108 /* write record, gets new recID */ +#define CMDfupdrec 0x0109 /* update record, keeps recID */ +#define CMDfputmore 0x90 +#define CMDfclrchg 0x010A /* commit record */ +#define CMDdisconn 0x81 + + +int sendcmd( ushort cmd, ... ); /* encode command and send */ +int recvrsp( ushort cmd, ... ); /* receive response and decode */ + +#endif /*_SYNPROTO_H*/ diff --git a/src/syntrans.c b/src/syntrans.c new file mode 100644 index 0000000..271ae00 --- /dev/null +++ b/src/syntrans.c @@ -0,0 +1,515 @@ +/************************************************************************ +* Copyright (C) 2000 Thomas Schulz * +* */ + static char rcsid[] = + "$Id: syntrans.c,v 1.19 2001/03/02 02:09:59 tsch Rel $"; /* +* * +* IC35 synchronize transactions * +* * +************************************************************************* +* * +* ??? is "fixme" mark: sections of code needing fixes * +* * +************************************************************************/ + +#include /* fprintf(), .. */ +#include /* memcpy(), strlen() ..*/ +#include /* calloc(), .. */ +#include /* size_t, .. */ + +#include "util.h" /* ERR,OK, uchar, .. */ +#include "comio.h" /* com_init(), .. */ +#include "genproto.h" /* welcome() */ +#include "synproto.h" /* Level-4 commands, .. */ +#include "syntrans.h" +#include "ic35frec.h" /* IC35REC, .. */ +NOTUSED(rcsid); + + +/* ==================================== */ +/* comunication phases */ +/* ==================================== */ + +/* welcome phase +* ------------- +* opens comm.device and does welcome handshake +*/ +static int +synwelcome( char * devname ) +{ + int rval; + + LPRINTF(( L_INFO, "welcome(%s) ..", devname )); + if ( com_init( devname ) != OK ) { + error( "welcome failed: com_init(%s) failed", devname ); + return ERR; + } + message( "welcome, start IC35 !" ); + if ( (rval = welcome( 0x41 )) != OK ) { + error( "welcome %s", rval == ERR_intr ? "aborted" + : "failed: no response" ); + return ERR; + } + message( "connected" ); + LPRINTF(( L_INFO, "welcome(%s) OK", devname )); + return OK; +} + +/* identify +* -------- +*/ +static int +identify( char * rtext ) +{ + char idtext[39+1]; + + LPRINTF(( L_INFO, "identify .." )); + if ( sendcmd( CMDident ) < 0 + || recvrsp( CMDident, idtext, sizeof(idtext) ) < 0 ) { + error( "identify failed" ); + return ERR; + } + if ( rtext != NULL ) + strncat( strcpy( rtext, "" ), idtext, sizeof(idtext)-1 ); + LPRINTF(( L_INFO, "identify OK" )); + return OK; +} + +/* power +* ----- +*/ +static int +power( void ) +{ + ushort pstate = 0xFFFF; + + LPRINTF(( L_INFO, "power .." )); + if ( sendcmd( CMDgetpower ) < 0 + || recvrsp( CMDgetpower, &pstate ) < 0 + || pstate != RSPpowerOK ) { + error( "power failed (%04X)", pstate ); + return ERR; + } + LPRINTF(( L_INFO, "power OK (%04X)", pstate )); + return OK; +} + +/* authenticate +* ------------ +*/ +static int +authenticate( char * passwd ) +{ + ushort pstate = 0xFFFF; + + LPRINTF(( L_INFO, "password .." )); + if ( sendcmd( CMDpassword, passwd ) < 0 + || recvrsp( CMDpassword, &pstate ) < 0 + || pstate != RSPpasswdOK ) { + error( "password failed (%04X)", pstate ); + return ERR; + } + LPRINTF(( L_INFO, "password OK (%04X)", pstate )); + return OK; +} + +/* read/write IC35 reference info +* ------------------------------ +* reference info can be an arbitrary string of up to 16 chars. +* IC35sync/Windows uses mmddyyyyhhmmss to retrieve date+time of +* last sync from IC35 or store it into IC35. +*/ +int +ReadSysInfo( char * infobuff ) +{ + char rinfo[16+1]; + + LPRINTF(( L_INFO, "ReadSysInfo .." )); + if ( sendcmd( CMDgetdtime ) < 0 + || recvrsp( CMDgetdtime, rinfo, sizeof(rinfo) ) < 0 ) { + error( "ReadSysInfo failed" ); + return ERR; + } + if ( infobuff != NULL ) + strncat( strcpy( infobuff, "" ), rinfo, sizeof(rinfo)-1 ); + LPRINTF(( L_INFO, "ReadSysInfo OK (%s)", rinfo )); + return OK; +} +int +WriteSysInfo( char * infodata ) +{ + LPRINTF(( L_INFO, "WriteSysInfo %s ..", infodata )); + if ( sendcmd( CMDsetdtime, infodata ) < 0 + || recvrsp( CMDsetdtime ) < 0 ) { + error( "WriteSysInfo \"%s\" failed", infodata ); + return ERR; + } + message( "set sysinfo \"%s\"", infodata ); + LPRINTF(( L_INFO, "WriteSysInfo OK" )); + return OK; +} + +/* category +* -------- +* ??? semantics not clear +*/ +int +category( char * name ) +{ + ushort state = 0xFFFF; + + LPRINTF(( L_INFO, "category(%s) ..", name )); + if ( sendcmd( CMDcategory, name ) < 0 + || recvrsp( CMDcategory, &state ) < 0 ) { + error( "category(%s) failed (%04hX)", name, state ); + return ERR; + } + LPRINTF(( L_INFO, "category(%s) OK (%04hX)", name, state )); + return OK; +} + +/* open com-device and connect with IC35 +* ------------------------------------- +*/ +int +connect( char * devname, char * passwd, char * rdtime ) +{ + int rval; + char idtext[64]; + + if ( (rval = synwelcome( devname )) == OK + && (rval = identify( idtext )) == OK + && (rval = power()) == OK + && (rval = authenticate( passwd )) == OK + && (rval = ReadSysInfo( rdtime )) == OK ) + if ( rdtime != NULL ) + message( "version \"%s\" sysinfo \"%s\"", idtext, rdtime ); + else + message( "version \"%s\"", idtext ); + else + disconnect(); + return rval; +} + +/* disconnect from IC35 and close com-device +* ----------------------------------------- +*/ +int +disconnect( void ) +{ + message( "disconnect" ); + sendcmd( CMDdisconn ); + recvrsp( CMDdisconn ); + com_exit(); + LPRINTF(( L_INFO, "disconnect done." )); + return OK; +} + + +/* ==================================== */ +/* access file on IC35 */ +/* ==================================== */ + +/* open file +* --------- +*/ +int +open_file( char * fname ) +{ + ushort fd; + + LPRINTF(( L_INFO, "open_file(%s) ..", fname )); + if ( sendcmd( CMDfopen, fname ) < 0 + || recvrsp( CMDfopen, &fd ) < 0 ) { + error( "openfile(%s) failed", fname ); + return ERR; + } + LPRINTF(( L_INFO, "open_file(%s) fd=%04hX", fname, fd )); + return fd; +} + +/* get filelength +* -------------- +*/ +/* get total number of records */ +int +get_flen( int fd ) +{ + ushort flen; + + LPRINTF(( L_INFO, "get_flen(fd=%04hX) ..", fd )); + if ( sendcmd( CMDfgetlen, fd ) < 0 + || recvrsp( CMDfgetlen, &flen ) < 0 ) { + error( "get_flen(fd=%04X) failed", fd ); + return ERR; + } + LPRINTF(( L_INFO, "get_flen(fd=%04X) %hd records", fd, flen )); + return flen; +} +/* get number of modified records */ +int +get_mod_flen( int fd ) +{ + ushort flen; + + LPRINTF(( L_INFO, "get_mod_flen(fd=%04X) ..", fd )); + if ( sendcmd( CMDfgetmlen, fd ) < 0 + || recvrsp( CMDfgetmlen, &flen ) < 0 ) { + error( "get_mod_flen(fd=%04X) failed", fd ); + return ERR; + } + LPRINTF(( L_INFO, "get_mod_flen(fd=%04X) %hd records", fd, flen )); + return flen; +} + +/* read file record +* ---------------- +* there are 3 methods to read records from IC35 file: +* - read_frec read record by index +* - read_mod_frec read next modified record +* - read_id_frec read record by record-ID +* return: +* -1 ERR, communication error +* 0 uncommitted deleted record (read_mod_frec) +* or record does not exist (read_id_frec) +* >0 OK, length of record data +*/ +static int +_read_frec( IC35REC * rec ) +{ + ulong rid; /* record-id on IC35 */ + uchar chg; /* change-flag on IC35 */ + bool last; /* last of multi-block */ + uchar * buff; + size_t blen; + uchar *rptr, *rend; + int rlen; + + if ( (buff = calloc( blen = MAXRLEN, sizeof(*buff) )) == NULL ) { + error( "_read_frec() failed: no memory for buffer (%d)", blen ); + return -1; + } + rend = (rptr = buff) + blen; + for ( ; ; ) { + if ( rptr == buff ) + rlen = recvrsp( CMDfgetrec, &last, &rid, &chg, rptr, rend - rptr ); + else + rlen = recvrsp( CMDfgetmore, &last, rptr, rend - rptr ); + if ( rlen < 0 ) { + free( buff ); + error( "_read_frec() failed: recvrsp(%s)", + rptr == buff ? "getrec" : "getmore" ); + return ERR; + } + rptr += rptr + rlen <= rend ? rlen : 0; + if ( last ) + break; + sendcmd( CMDfgetmore ); + } + rlen = rptr - buff; + set_ic35recid( rec, rid ); + set_ic35recchg( rec, chg ); + set_ic35recdata( rec, buff, rlen ); + free( buff ); + return rlen; +} +/* read file record by record-ID */ +int +read_id_frec( int fd, ulong rid, IC35REC * rec ) +{ + int rlen; + + LPRINTF(( L_INFO, "read_id_frec(fd=%04X,rid=%08lX) ..", fd, rid )); + if ( sendcmd( CMDfgetirec, fd, rid ) < 0 ) { + error( "read_id_frec(fd=%04X,rid=%08lX) failed: sendcmd", fd, rid ); + return ERR; + } + if ( (rlen = _read_frec( rec )) < 0 ) { + error( "read_id_frec(fd=%04X,rid=%08lX) failed: recvrsp", fd, rid ); + return ERR; + } + if ( ic35recid( rec ) != rid ) { + LPRINTF(( L_INFO, "read_id_frec(fd=%04X,rid=%08lX) non-exist", fd, rid )); + return 0; + } + LPRINTF(( L_INFO, "read_id_frec(fd=%04X,rid=%08lX) len=%d", fd, rid, rlen )); + return rlen; +} +/* read file record by index */ +int +read_frec( int fd, int idx, IC35REC * rec ) +{ + int rlen; + + LPRINTF(( L_INFO, "read_frec(fd=%04X,i=%04X) ..", fd, idx )); + if ( sendcmd( CMDfgetrec, fd, idx ) < 0 ) { + error( "read_frec(fd=%04X,i=%04X) failed: sendcmd", fd, idx ); + return ERR; + } + if ( (rlen = _read_frec( rec )) < 0 ) { + error( "read_frec(fd=%04X,i=%04X) failed: recvrsp", fd, idx ); + return ERR; + } + LPRINTF(( L_INFO, "read_frec(fd=%04X,i=%04X) len=%d", fd, idx, rlen )); + return rlen; +} +/* read next modified file record */ +int +read_mod_frec( int fd, IC35REC * rec ) +{ + int rlen; + + LPRINTF(( L_INFO, "read_mod_frec(fd=%04X) ..", fd )); + if ( sendcmd( CMDfgetmrec, fd ) < 0 ) { + error( "read_mod_frec(fd=%04X) failed: sendcmd", fd ); + return ERR; + } + if ( (rlen = _read_frec( rec )) < 0 ) { + error( "read_mod_frec(fd=%04X) failed: recvrsp", fd ); + return ERR; + } + LPRINTF(( L_INFO, "read_mod_frec(fd=%04X) len=%d", fd, rlen )); + return rlen; +} + +/* write/update file record +* ------------------------ +* write_frec() writes new record, which gets new record-ID, +* update_frec() writes new data into an existing record and +* keeps same record-ID. +* ??? the meaning of magic is unclear, perceived were: +* IC35file bytes +* Addresses 51 00 00 +* Memo 58 48 99 +* 60 16 99 +* Schedule 10 00 00 +* ToDoList 10 00 00 +*/ +#define DOwrite TRUE +#define DOupdate FALSE + +static int +_write_frec( bool do_write, int fd, IC35REC * rec ) +{ + uchar * data; + size_t dlen; + uchar * wptr; + size_t wlen; + uchar * magic; + bool last; + int rval; + ulong orecid, recid; + + orecid = ic35recid( rec ); + get_ic35recdata( rec, &data, &dlen ); + switch ( FileId( orecid ) ) { + case FILEADDR: magic = "\x51\x00\x00"; break; + case FILEMEMO: magic = "\x60\x16\x99"; break; + case FILETODO: + case FILESCHED: magic = "\x10\x00\x00"; break; + default: magic = "\x00\x00\x00"; break; + } + wptr = data; + do { + wlen = data+dlen - wptr; + if ( wlen > 80 ) wlen = 80; + last = (bool)( wptr+wlen >= data+dlen ); + if ( wptr == data ) + if ( do_write ) + rval = sendcmd( CMDfputrec, last, fd, 0L, magic, dlen, + wptr, wlen ); + else /* update */ + rval = sendcmd( CMDfupdrec, last, fd, orecid, magic, dlen, + wptr, wlen ); + else + rval = sendcmd( CMDfputmore, last, wptr, wlen ); + if ( rval < 0 ) + return -1; + if ( recvrsp( CMDfputrec, &recid ) < 0 ) + return -2; + wptr += wlen; + } while ( wptr < data+dlen ); + set_ic35recid( rec, recid ); + return 0; +} +/* write record, gets new record-ID */ +int +write_frec( int fd, IC35REC * rec ) +{ + int rval; + + LPRINTF(( L_INFO, "write_frec(fd=%04X) ..", fd )); + if ( (rval = _write_frec( DOwrite, fd, rec )) < 0 ) { + error( "write_frec(fd=%04X) failed: %s", + fd, rval == -1 ? "sendcmd" : "recvrsp" ); + return ERR; + } + LPRINTF(( L_INFO, "write_frec(fd=%04X) rid=%08lX", fd, ic35recid( rec ) )); + return OK; +} +/* write record update, keeps record-ID */ +int +update_frec( int fd, IC35REC * rec ) +{ + int rval; + ulong orecid; + + orecid = ic35recid( rec ); + LPRINTF(( L_INFO, "update_frec(fd=%04X,orid=%08lX) ..", fd, orecid )); + if ( (rval = _write_frec( DOupdate, fd, rec )) < 0 ) { + error( "update_frec(fd=%04X,orid=%08lX) failed: %s", + fd, orecid, rval == -1 ? "sendcmd" : "recvrsp" ); + return ERR; + } + LPRINTF(( L_INFO, "update_frec(fd=%04X,orid=%08lX) nrid=%08lX", + fd, orecid, ic35recid( rec ) )); + return OK; +} + +/* delete file record +* ------------------ +*/ +int +delete_frec( int fd, ulong recid ) +{ + LPRINTF(( L_INFO, "delete_frec(fd=%04X,rid=%08lX) ..", fd, recid )); + if ( sendcmd( CMDfdelrec, fd, recid ) < 0 + || recvrsp( CMDfdelrec ) < 0 ) { + error( "delete_frec(fd=%04X,rid=%08X) failed", fd, recid ); + return ERR; + } + LPRINTF(( L_INFO, "delete_frec(fd=%04X,rid=%08lX) OK", fd, recid )); + return OK; +} + +/* commit file record, i.e. reset change flag +* ------------------------------------------ +*/ +int +commit_frec( int fd, ulong recid ) +{ + LPRINTF(( L_INFO, "commit_frec(fd=%04X,rid=%08lX) ..", fd, recid )); + if ( sendcmd( CMDfclrchg, fd, recid ) < 0 + || recvrsp( CMDfclrchg ) < 0 ) { + error( "commit_frec(fd=%04hX,rid=%08X) failed", fd, recid ); + return ERR; + } + LPRINTF(( L_INFO, "commit_frec(fd=%04hX,rid=%08lX) OK", fd, recid )); + return OK; +} + +/* close file +* ---------- +*/ +int +close_file( int fd ) +{ + LPRINTF(( L_INFO, "close_file(fd=%04X) ..", fd )); + if ( sendcmd( CMDfclose, fd ) < 0 + || recvrsp( CMDfclose ) < 0 ) { + error( "close_file(fd=%04X) failed", fd ); + return ERR; + } + LPRINTF(( L_INFO, "close_file(fd=%04X) OK", fd )); + return OK; +} diff --git a/src/syntrans.h b/src/syntrans.h new file mode 100644 index 0000000..e8e860f --- /dev/null +++ b/src/syntrans.h @@ -0,0 +1,37 @@ +/************************************************************************ +* Copyright (C) 2000 Thomas Schulz * +* * +* $Id: syntrans.h,v 1.10 2000/12/21 11:05:52 tsch Rel $ * +* * +* header for IC35 synchronize transactions * +* * +************************************************************************/ +#ifndef _SYNTRANS_H +#define _SYNTRANS_H 1 + +#include /* size_t */ + +#include "util.h" /* uchar, .. */ +#include "ic35frec.h" /* IC35REC */ + + +int connect( char * devname, char * passwd, char * rdtime ); +int disconnect( void ); + +int ReadSysInfo( char * infobuff ); +int WriteSysInfo( char * infodata ); +int category( char * name ); + +int open_file( char * fname ); +int get_flen( int fd ); +int get_mod_flen( int fd ); +int read_id_frec( int fd, ulong rid, IC35REC * rec ); +int read_frec( int fd, int idx, IC35REC * rec ); +int read_mod_frec( int fd, IC35REC * rec ); +int write_frec( int fd, IC35REC * rec ); +int update_frec( int fd, IC35REC * rec ); +int delete_frec( int fd, ulong recid ); +int commit_frec( int fd, ulong recid ); +int close_file( int fd ); + +#endif /*_SYNTRANS_H*/ diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..cf6068f --- /dev/null +++ b/src/util.c @@ -0,0 +1,298 @@ +/************************************************************************ +* Copyright (C) 2000 Thomas Schulz * +* */ + static char rcsid[] = + "$Id: util.c,v 1.9 2001/06/11 09:14:59 tsch Rel $"; /* +* * +* IC35 utilities: logging, errors, messages * +* * +************************************************************************* +* * +* conditional compile on NO_LOGSIM: if #defined, logging of the * +* IC35 communications is NOT supported, default WITH logging. * +* conditional compile on __STRICT_ANSI__: if #defined, substitute * +* functions, which are not available with the ANSI C standard. * +* (compilation with 'gcc -ansi ..' does #define __STRICT_ANSI__) * +* * +* logging #ifndef NO_LOGSIM * +* log_init initialize logfile, logtag and loglevel * +* log_close close logfile * +* lprintf printf to logfile * +* ldump dump data to logfile * +* log_proginfo log program name, version and build info * +* log_argsinfo log command line of program invocation * +* (error-)messages * +* vlmessage local: vprintf message * +* message output and log L_MESG message * +* error output and log L_ERROR message * +* fatal output,log L_FATAL message and abort program * +* _not_impl report unimplemented feature * +* substitute functions #ifdef __STRICT_ANSI__ * +* strncasecmp * +* strcasecmp * +* strdup * +* * +************************************************************************/ + +#include /* fprintf(), .. */ +#include /* va_start(), .. */ +#include /* malloc(), free(), .. */ +#include /* strcpy(), .. */ +#include /* size_t, .. */ +#include /* gettimeofday(), .. */ +#include /* struct tm, time() .. */ + +#include "util.h" +NOTUSED(rcsid) + + +/* logging +* ------- +* format of log entries: +* hh:mm:ss.mmmm tag level message +* hh:mm:ss.mmmm tag level addr hex... char... +*/ +#ifndef NO_LOGSIM +static char * logfname = NULL; +static FILE * logfp = NULL; +static char * logtag = ""; +static int loglevel = L_DEBUG; + +static int +_log_init( char * l_fname, char * l_tag, int level ) +{ + if ( l_fname && *l_fname ) { + logfp = fopen( logfname = l_fname, "w" ); + if ( logfp == NULL ) + return ERR; + } + if ( l_tag && *l_tag ) + logtag = l_tag; + if ( level ) + loglevel = level; + return OK; +} +void +log_init( char * l_fname, char * l_tag, int level ) +{ + if ( _log_init( l_fname, l_tag, level ) != OK ) + fatal( "cannot open logfile: %s", l_fname ); +} +void +log_close( void ) +{ + if ( logfp ) { + fclose( logfp ); + logfp = NULL; + } +} +static char * +ltstamp( void ) +{ + static char timestamp[16]; + struct timeval tv; + struct tm * ptm; + + gettimeofday( &tv, NULL ); + ptm = localtime( (time_t*)&tv.tv_sec ); + sprintf( timestamp, "%02d:%02d:%02d.%04d", + ptm->tm_hour, ptm->tm_min, ptm->tm_sec, + (int)(tv.tv_usec/100) ); + return timestamp; +} +static void +vltprintf( int level, char * tstamp, const char * format, va_list argp ) +{ + if ( logfp == NULL || level > loglevel ) + return; + fprintf( logfp, "%s %s %d ", tstamp, logtag, level ); + vfprintf( logfp, format, argp ); + fprintf( logfp, "\n" ); +} +static void +vlprintf( int level, const char * format, va_list argp ) +{ + vltprintf( level, ltstamp(), format, argp ); +} +void +lprintf( int level, const char * format, ... ) +{ + va_list argp; + + va_start( argp, format ); + vlprintf( level, format, argp ); + va_end( argp ); +} +static void +ltprintf( int level, char * tstamp, const char * format, ... ) +{ + va_list argp; + + va_start( argp, format ); + vltprintf( level, tstamp, format, argp ); + va_end( argp ); +} +void +ldump( int level, void * data, size_t dlen, const char * format, ... ) +{ + va_list argp; + char * tstamp; + int i; + uchar *dptr, *dend; + char *hdptr, hdump[5+3*16+1]; + char *cdptr, cdump[16+1]; + + tstamp = ltstamp(); + va_start( argp, format ); + vltprintf( level, tstamp, format, argp ); + va_end( argp ); + dend = (dptr = (uchar*)data) + dlen; + while ( dptr < dend ) { + hdptr = hdump; + cdptr = memset( cdump, 0, sizeof(cdump) ); + sprintf( hdptr, "%04lX:", dptr - (uchar*)data ); hdptr += 5; + for ( i = 0; i < 16 && dptr < dend; ++i, ++dptr ) { + sprintf( hdptr, " %02X", *dptr ); hdptr += 3; + *cdptr++ = ' ' < *dptr && *dptr <= '~' ? *dptr : '.'; + } + ltprintf( level, tstamp, " %-53s %s", hdump, cdump ); + } +} + +/* log program info +* ---------------- +* current date and time, as log has only hh:mm:ss.mmmm stamps +* program name, version and release date +* build date+time, user, host +*/ +void +log_proginfo( char * name, char * version, char * date, char * bldinfo ) +{ + time_t tnow; + struct tm * ptm; + char dtime[24+1]; /* yyyy-mm-dd hh:mm:ss zzzz */ + + tnow = time( NULL ); + ptm = localtime( &tnow ); + strftime( dtime, sizeof(dtime), "%Y-%m-%d %T %Z", ptm ); + lprintf( L_MESG, "%s", dtime ); + lprintf( L_MESG, "%s %s (%s)", name, version, date ); + lprintf( L_MESG, "%s", bldinfo ); +} + +/* log command line +* ---------------- +*/ +void +log_argsinfo( int argc, char ** argv ) +{ + int i, len; + char * cmdline; + + for ( i = len = 0; i < argc; ++i ) + len += strlen( argv[i] ) + 1; + if ( (cmdline = malloc( len )) != NULL ) { + strcpy( cmdline, argv[0] ); + for ( i = 1; i < argc; ++i ) + strcat( strcat( cmdline, " " ), argv[i] ); + lprintf( L_MESG, "cmd: %s", cmdline ); + free( cmdline ); + } +} +#endif /*NO_LOGSIM*/ + + +/* output (error-)message +* ---------------------- +*/ +static void +vlmessage( int level, const char * format, va_list argp ) +{ + vfprintf( stderr, format, argp ); + fprintf( stderr, "\n" ); +#ifndef NO_LOGSIM + vlprintf( level, format, argp ); +#endif +} +void +message( const char * format, ... ) +{ + va_list argp; + + va_start( argp, format ); + vlmessage( L_MESG, format, argp ); + va_end( argp ); +} +void +error( const char * format, ... ) +{ + va_list argp; + + va_start( argp, format ); + vlmessage( L_ERROR, format, argp ); + va_end( argp ); +} +void +fatal( const char * format, ... ) +{ + va_list argp; + + va_start( argp, format ); + vlmessage( L_FATAL, format, argp ); + va_end( argp ); + exit( 1 ); +} + +/* report unimplemented feature +* ---------------------------- +*/ +int +_not_impl( char * feature ) +{ + error( "%s NOT IMPLEMENTED", feature ); + return ERR; +} + + +/* substitute functions +* -------------------- +* the functions below are unavailable on compilation with "-ansi", +* which #defines __STRICT_ANSI__ +*/ +#ifdef __STRICT_ANSI__ +#include /* toupper() */ + +int +strncasecmp( const char * s1, const char * s2, size_t n ) +{ + int cmp; + + for ( ; n; ++s1, ++s2, --n ) { + cmp = toupper(*s1) - toupper(*s2); + if ( cmp != 0 ) + return cmp; + if ( !( *s1 && *s2 ) ) + break; + } + return 0; +} +int +strcasecmp( const char * s1, const char * s2 ) +{ + size_t n; + + if ( (n = strlen(s1)) < strlen(s2) ) + n = strlen(s2); + return strncasecmp( s1, s2, n ); +} + +char * +strdup( const char * s ) +{ + char * nstr; + + if ( (nstr = malloc( strlen(s) + 1 )) != NULL ) + strcpy( nstr, s ); + return nstr; +} +#endif /*__STRICT_ANSI__*/ diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..5e444d5 --- /dev/null +++ b/src/util.h @@ -0,0 +1,96 @@ +/************************************************************************ +* Copyright (C) 2000 Thomas Schulz * +* * +* $Id: util.h,v 1.9 2001/03/02 02:08:32 tsch Rel $ * +* * +* header for IC35 utilities * +* * +************************************************************************/ +#ifndef _UTIL_H +#define _UTIL_H 1 + +#include /* size_t */ + +#ifdef HAVE_CONFIG_H +#include /* NO_LOGSIM */ +#endif + + +#define NOTUSED(x) static void _nu_ ## x( void ) \ + { (void)( _nu_ ## x + 0 ); (void)( x + 0 ); } +#define UNUSED(x) (void)( x + 0 ) /* LINT: unused function arg */ + +#if !defined(OK) || !defined(ERR) +# define OK 0 +# define ERR -1 +#endif +#if !defined(TRUE) || !defined(FALSE) +# define TRUE 1 +# define FALSE 0 +#endif + +#define bool int +#define uchar unsigned char +#define ushort unsigned short +#define uint unsigned int +#define ulong unsigned long + +#if !defined(min) || !defined(max) +# define min(a,b) ( (a) < (b) ? (a) : (b) ) +# define max(a,b) ( (a) < (b) ? (b) : (a) ) +#endif +#ifndef offsetof +# define offsetof(type,fld) (size_t)&( ((type*)0)->fld ) +#endif +#ifndef alenof +# define alenof(array) ( sizeof(array) / sizeof(array[0]) ) +#endif + + +/* log levels */ +#define L_FATAL 0 /* program will end, SYSLOG LOG_ALERT */ +#define L_ERROR 1 /* action should be taken, SYSLOG LOG_ERR */ +#define L_AUDIT 2 /* serious info, SYSLOG LOG_NOTICE */ +#define L_WARN 3 /* something goes wrong ? */ +#define L_MESG 4 /* info - level 1 */ +#define L_INFO 5 /* info - level 2 */ +#define L_NOISE 6 /* info - level 3 */ +#define L_DEBUG 7 /* debugging, very noisy */ + +#ifdef NO_LOGSIM /* log and com-simulation disabled */ +#define LOG_INIT(args) +#define LOG_CLOSE(args) +#define LPRINTF(args) +#define LDUMP(args) +#define LOG_PROGINFO(args) +#define LOG_ARGSINFO(args) +#else /*!NO_LOGSIM*/ /* log and com-simulation enabled */ +#define LOG_INIT(args) log_init args +#define LOG_CLOSE(args) log_close args +#define LPRINTF(args) lprintf args +#define LDUMP(args) ldump args +#define LOG_PROGINFO(args) log_proginfo args +#define LOG_ARGSINFO(args) log_argsinfo args +#endif /*NO_LOGSIM*/ + + +void log_init( char * l_fname, char * l_tag, int level ); +void log_close( void ); +void lprintf( int level, const char * format, ... ); +void ldump( int level, void * data, size_t dlen, const char * format, ... ); +void log_proginfo( char * name, char * version, char * date, char * bldinfo ); +void log_argsinfo( int argc, char ** argv ); + +void message( const char * format, ... ); +void error( const char * format, ... ); +void fatal( const char * format, ... ); + +int _not_impl( char * feature ); + +#ifdef __STRICT_ANSI__ +int strcasecmp( const char * s1, const char * s2 ); +int strncasecmp( const char * s1, const char * s2, size_t n ); +char * strdup( const char * s ); +#endif /*__STRICT_ANSI__*/ + +#endif /*_UTIL_H*/ diff --git a/src/vcaconv.c b/src/vcaconv.c new file mode 100644 index 0000000..839cf0f --- /dev/null +++ b/src/vcaconv.c @@ -0,0 +1,722 @@ +/************************************************************************ +* Copyright (C) 2000,2001 Thomas Schulz * +* */ + static char rcsid[] = + "$Id: vcaconv.c,v 1.17 2001/02/17 20:56:49 tsch Rel $"; /* +* * +* vCard,vCalendar conversion * +* * +************************************************************************* +* * +* ??? is "fixme" mark: sections of code needing fixes * +* * +* for usage run vcaconv -h or see function usage() below. * +* * +************************************************************************/ + +#include /* printf(), .. */ +#include /* strcasecmp(), .. */ +#include /* getopt(), optarg, .. */ +#include /* isdigit() */ +#include /* struct tm, time() .. */ + +#include "vcc.h" /* VObject, .. */ +#include "syntrans.h" /* FILEADDR, .. */ +#include "dataio.h" /* ic35addr_to_vcard()..*/ +#include "vcutil.h" /* vca_type(), .. */ +#include "util.h" /* uchar, .. */ +NOTUSED(rcsid) + + +extern char * pkgvers; /* these are all */ +extern char * pkgdate; /* in versinfo.c, which is */ +extern char * bldinfo; /* auto-generated by Makefile */ + + +/* get VObject's string value (dupStr()'d) +* -------------------------- +*/ +static char * +dupStrValue( VObject * vobj, const char * id ) +{ + char * strval; + + if ( (strval = dupStringValue( vobj, id )) == NULL ) + strval = dupStr( "", 0 ); + return strval; +} + + +/* ==================================================== */ +/* parse VObject list from vCard/vCal file */ +/* ==================================================== */ + +/* parse error handler +* ------------------- +*/ +static bool _vca_error; +static char * _vca_fname; + +static void +_vca_errmsg( char * msg ) /* mimeErrorHandler for Parse_MIME_FromFile() */ +{ + error( "vcafile \"%s\": %s", _vca_fname, msg ); + _vca_error = TRUE; +} + +/* parse vCard,vCal file +* --------------------- +*/ +static VObject * +vca_parse( char * fname ) +{ + FILE * infp; + VObject * vlist; + + if ( fname && *fname && strcmp( fname, "-" ) != 0 ) { + if ( (infp = fopen( fname, "r" )) == NULL ) { + error( "cannot open vcafile: %s", fname ); + return NULL; + } + } else { + fname = "(stdin)"; + infp = stdin; + } + registerMimeErrorHandler( _vca_errmsg ); + _vca_error = FALSE; + _vca_fname = fname; + vlist = Parse_MIME_FromFile( infp ); + if ( _vca_error ) { + cleanVObjects( vlist ); + vlist = NULL; + } + if ( infp != stdin ) + fclose( infp ); + return vlist; +} + + +/* ==================================== */ +/* sort list of VObjects */ +/* ==================================== */ + +/* compare property of VObjects +* ---------------------------- +*/ +static int +_compare_vprop( const char * id, VObject * vobj1, VObject * vobj2 ) +{ + int cmp; + + if ( !( id && *id ) + || (vobj1 == NULL && vobj2 == NULL) ) + return 0; + if ( vobj1 == NULL ) + return -1; + if ( vobj2 == NULL ) + return +1; + + if ( strcasecmp( id, VCNameProp ) == 0 ) { + VObject * vprop1 = isAPropertyOf( vobj1, id ); + VObject * vprop2 = isAPropertyOf( vobj2, id ); + if ( (cmp = _compare_vprop( VCFamilyNameProp, vprop1, vprop2 )) != 0 ) + return cmp; + else + return _compare_vprop( VCGivenNameProp, vprop1, vprop2 ); + } + if ( strcasecmp( id, XPilotIdProp ) == 0 ) { + ulong val1 = LongValue( vobj1, id ); + ulong val2 = LongValue( vobj2, id ); + if ( val1 < val2 ) + return -1; + if ( val1 > val2 ) + return +1; + return 0; + } + if ( strcasecmp( id, VCLastModifiedProp ) == 0 + || strcasecmp( id, VCLastRevisedProp ) == 0 + || strcasecmp( id, VCDTstartProp ) == 0 + || strcasecmp( id, VCDTendProp ) == 0 ) { + time_t time1 = isodtime_to_unixtime( vobj1, id ); + time_t time2 = isodtime_to_unixtime( vobj2, id ); + if ( time1 < time2 ) + return -1; + if ( time1 > time2 ) + return +1; + return 0; + } + { + char * str1 = dupStrValue( vobj1, id ); + char * str2 = dupStrValue( vobj2, id ); + cmp = strcmp( str1, str2 ); + deleteStr( str1 ); + deleteStr( str2 ); + return cmp; + } +} + +/* compare VObjects for sorting +* ---------------------------- +*/ +static int +_compare_vobj( const void * pvobj1, const void * pvobj2 ) +{ + VObject * vobj1 = *(VObject**)pvobj1; + VObject * vobj2 = *(VObject**)pvobj2; + int type1 = vca_type( vobj1 ); + int type2 = vca_type( vobj2 ); + int cmp; + + if ( type1 < type2 ) + return -1; + else if ( type1 > type2 ) + return +1; + + switch ( type1 ) { + case VCARD: /* addr: Name, REV, */ + if ( (cmp = _compare_vprop( VCNameProp, vobj1, vobj2 )) != 0 ) + return cmp; + if ( (cmp = _compare_vprop( VCLastRevisedProp, vobj1, vobj2 )) != 0 ) + return cmp; + break; + case VMEMO: /* memo: subject, */ + if ( (cmp = _compare_vprop( VCSummaryProp, vobj1, vobj2 )) != 0 ) + return cmp; + if ( (cmp = _compare_vprop( VCLastModifiedProp, vobj1, vobj2 )) != 0 ) + return cmp; + break; + case VEVENT: /* vcal.sched: DTSTART, subject, rev, */ + if ( (cmp = _compare_vprop( VCDTstartProp, vobj1, vobj2 )) != 0 ) + return cmp; + if ( (cmp = _compare_vprop( VCSummaryProp, vobj1, vobj2 )) != 0 ) + return cmp; + if ( (cmp = _compare_vprop( VCLastModifiedProp, vobj1, vobj2 )) != 0 ) + return cmp; + break; + case VTODO: /* vcal.todo: DTSTART, subject, rev, */ + if ( (cmp = _compare_vprop( VCDueProp, vobj1, vobj2 )) != 0 ) + return cmp; + if ( (cmp = _compare_vprop( VCSummaryProp, vobj1, vobj2 )) != 0 ) + return cmp; + if ( (cmp = _compare_vprop( VCLastModifiedProp, vobj1, vobj2 )) != 0 ) + return cmp; + break; + case VCAL: /* multiple vCAL ? VERSION, PRODID */ + if ( (cmp = _compare_vprop( VCVersionProp, vobj1, vobj2 )) != 0 ) + return cmp; + return _compare_vprop( VCProdIdProp, vobj1, vobj2 ); + } + /* same with above compares: X-PILOTID, UID */ + if ( (cmp = _compare_vprop( XPilotIdProp, vobj1, vobj2 )) != 0 ) + return cmp; + return _compare_vprop( VCUniqueStringProp, vobj1, vobj2 ); +} + +/* sort VObject's property list +* ---------------------------- +*/ +struct propseq { + int seq; + char * id; +}; +static struct propseq _propseq[] = { + { -6, VCNameProp }, + { -5, VCDTstartProp }, + { -4, VCDTendProp }, + { -3, VCDueProp }, + { -2, VCSummaryProp }, + { -1, VCDescriptionProp }, + { +1, VCCategoriesProp }, + { +2, VCDCreatedProp }, + { +3, VCLastModifiedProp }, + { +4, VCLastRevisedProp }, + { +5, VCUniqueStringProp }, + { +6, XPilotIdProp }, + { +7, XPilotStatusProp }, + { 0, NULL } + }; +static int +_propid_seq( const char * id ) +{ + struct propseq * pseq; + + for ( pseq = _propseq; pseq->id; ++pseq ) + if ( strcasecmp( id, pseq->id ) == 0 ) + return pseq->seq; + return 0; +} +static int /* compare properties for sorting */ +_compare_props( const void * pvprop1, const void * pvprop2 ) +{ + VObject * vprop1 = *(VObject**)pvprop1; + VObject * vprop2 = *(VObject**)pvprop2; + const char *id1, *id2; + int seq1, seq2; + char *str1, *str2; + int cmp; + + seq1 = _propid_seq( id1 = vObjectName( vprop1 ) ); + seq2 = _propid_seq( id2 = vObjectName( vprop2 ) ); + if ( seq1 < seq2 ) + return -1; + if ( seq1 > seq2 ) + return +1; + if ( (cmp = strcasecmp( id1, id2 )) == 0 ) { + cmp = strcmp( str1 = dupStrValue( vprop1, NULL ), + str2 = dupStrValue( vprop2, NULL ) ); + deleteStr( str1 ); + deleteStr( str2 ); + } + return cmp; +} +static void /* sort VObject's property list */ +vobjsort( VObject * vobj ) +{ + size_t n, i; + VObjectIterator iter; + VObject ** vproptab; + VObject * vprop; + + n = 0; /* count number of properties, alloc table */ + initPropIterator( &iter, vobj ); + while ( moreIteration( &iter ) ) { + (void)nextVObject( &iter ); + ++n; + } + vproptab = malloc ( n * sizeof(vproptab[0]) ); + if ( vproptab == NULL ) + return; + /* build table of property ptrs and sort */ + i = 0; + initPropIterator( &iter, vobj ); + while ( i < n && moreIteration( &iter ) ) + vproptab[i++] = nextVObject( &iter ); + qsort( vproptab, n, sizeof(vproptab[0]), _compare_props ); + /* rebuild property list in sorted sequ */ + for ( i = 0; i < n; ++i ) + if ( (vprop = delProp( vobj, vproptab[i] )) != NULL ) + addVObjectProp( vobj, vprop ); + + free( vproptab ); +} + +/* sort VObject list +* ----------------- +*/ +static int +vca_sort( VObject ** vlist ) +{ + size_t n, i; + VObject * vcal; + VObject ** vobjtab; + VObject * vobj; + VObjectIterator iter; + + /* count number of VObjects, alloc table */ + n = 0; vcal = NULL; + for ( vobj = *vlist; vobj !=NULL; vobj = nextVObjectInList( vobj ) ) { + ++n; + if ( vca_type( vobj ) == VCAL ) { + if ( vcal == NULL ) + vcal = vobj; + initPropIterator( &iter, vobj ); + while ( moreIteration( &iter ) ) { + (void)nextVObject( &iter ); + ++n; + } + } + } + vobjtab = malloc ( n * sizeof(vobjtab[0]) ); + if ( vobjtab == NULL ) + return ERR; + + /* build table of VObject ptrs and sort */ + i = 0; + for ( vobj = *vlist; vobj !=NULL; vobj = nextVObjectInList( vobj ) ) { + vobjtab[i++] = vobj; + if ( vca_type( vobj ) == VCAL ) { + initPropIterator( &iter, vobj ); + while ( moreIteration( &iter ) ) + vobjtab[i++] = nextVObject( &iter ); + } + } + qsort( vobjtab, n, sizeof(vobjtab[0]), _compare_vobj ); + + /* rebuild VObject list in sorted sequ */ + for ( i = 0; i < n; ++i ) { + vobj = vobjtab[i]; + switch ( vca_type( vobj ) ) { + case VCARD: + case VMEMO: + vobjsort( vobj ); /* sort VObject's property list */ + /* fall thru */ + case VCAL: + vobj = delList( vlist, vobj ); + if ( vobj != NULL ) + addList( vlist, vobj ); + break; + case VEVENT: + case VTODO: + if ( vcal == NULL ) + break; + vobjsort( vobj ); /* sort VObject's property list */ + vobj = delProp( vcal, vobj ); + if ( vobj != NULL ) + addVObjectProp( vcal, vobj ); + break; + } + } + free( vobjtab ); + + return OK; +} + + +/* ==================================== */ +/* create telbook for handy */ +/* ==================================== */ + +static bool +hasCategory( VObject * vobj, char * wanted ) +{ + char * categories; + char * category; + + if ( !( vobj && wanted && *wanted ) ) + return FALSE; + categories = dupStrValue( vobj, VCCategoriesProp ); + for ( category = strtok( categories, ";" ); + category; category = strtok( NULL, ";" ) ) + if ( strcasecmp( category, wanted ) == 0 ) + break; + deleteStr( categories ); + return (bool)( category != NULL ); +} +static char * +NameValue( VObject * vobj, const char * type ) +{ + VObject * vprop; + char * namestr; + + if ( !( vobj && type && *type ) + || (vprop = isAPropertyOf( vobj, VCNameProp )) == NULL ) + return NULL; + if ( (namestr = dupStrValue( vprop, type )) && *namestr ) + return namestr; + deleteStr( namestr ); + if ( (namestr = dupStrValue( vprop, VCFamilyNameProp )) && *namestr ) + return namestr; + deleteStr( namestr ); + return dupStrValue( vprop, VCGivenNameProp ); +} +static char * +TelnoValue( VObject * vobj, const char * type ) +{ + VObjectIterator iter, teliter; + VObject * vprop; + VObject * teltype; + VObject * telpref; + VObject * teldflt; + char * telstr, *src, *dst; + + if ( !( vobj && type && *type ) ) + return NULL; + teltype = telpref = teldflt = NULL; + initPropIterator( &iter, vobj ); + while ( moreIteration( &iter ) ) { + vprop = nextVObject( &iter ); + if ( strcmp( vObjectName( vprop ), VCTelephoneProp ) != 0 ) + continue; + if ( teltype == NULL + && isAPropertyOf( vprop, type ) ) + teltype = vprop; + if ( telpref == NULL + && isAPropertyOf( vprop, VCPreferredProp ) ) + telpref = vprop; + initPropIterator( &teliter, vprop ); + if ( teldflt == NULL + && ! moreIteration( &teliter ) ) + teldflt = vprop; + } + telstr = dupStrValue( teltype ? teltype : + ( telpref ? telpref : teldflt ), NULL ); + if ( (dst = src = telstr) && *telstr ) { + do { + if ( (src == telstr && *src == '+') /* international +49 */ + || isdigit( *src ) ) /* skip separators, because */ + *dst++ = *src; /* handy does not like them */ + } while ( *src++ ); + } + return telstr; +} + +/* +* special categories for handy telbook: +* S35SIM pre-installed entries from S35i +* HANDY created for import into handy +* special conversions +* - vCards for handy have only VCNameProp.VCFamilyNameProp and +* VCTelephoneProp with VCCellularProp attribute +* for category "Business" and other derive from VCFamilyNameProp +* for category "Personal" derive from VCGivenNameProp +* - for multiple telnos make multiple vCards with append to name: +* Business: append none, "-priv", "-mobil" for WORK, HOME, CELL +* Personal: append "-Buero", none, "-mobil" for WORK, HOME, CELL +* - for category "Personal" make "VIP" entries by appending "!" +*/ +static VObject * +new_handycard( char * name, char * suffix, char * telno ) +{ + VObject * vcard; + char namebuff[18+1]; + time_t tnow; + struct tm * ptm; + char isodtime[24]; + + if ( !(name && *name) + || !(telno && *telno) + || (vcard = newVObject( VCCardProp )) == NULL ) + return NULL; + + strncat( strcpy( namebuff, "" ), name, sizeof(namebuff)-1 ); + if ( suffix && *suffix ) { + namebuff[ sizeof(namebuff)-1 - 4 ] = '\0'; + strncat( namebuff, suffix, sizeof(namebuff)-1 - strlen(namebuff) ); + } + addPropValue( vcard, VCFullNameProp, namebuff ); + addPropValue( addProp( vcard, VCNameProp ), VCFamilyNameProp, namebuff ); + + addProp( addPropValue( vcard, VCTelephoneProp, telno ), VCCellularProp ); + addPropValue( vcard, VCCategoriesProp, "HANDY" ); + + tnow = time( NULL ); + ptm = localtime( &tnow ); + strftime( isodtime, sizeof(isodtime), "%Y-%m-%dT%H:%M:%S", ptm ); + addPropValue( vcard, VCLastRevisedProp, isodtime ); + + return vcard; +} +static int +vcard_to_handy( VObject ** vlist ) +{ + VObject * vobj; + VObject * vcard; + VObject * vdel; + VObject * handylist; + VObject * vnext; + char * name, *tel1, *tel2; + + vdel = handylist = NULL; + for ( vobj = *vlist; vobj !=NULL; vobj = nextVObjectInList( vobj ) ) { + if ( vca_type( vobj ) != VCARD ) + continue; + if ( hasCategory( vobj, "HANDY" ) ) { + if ( vdel ) + cleanVObject( vdel ); + vdel = delList( vlist, vobj ); + continue; + } + if ( hasCategory( vobj, "NOTHANDY" ) ) + continue; + + if ( hasCategory( vobj, "S35SIM" ) ) { + name = NameValue( vobj, VCGivenNameProp ); + if ( (tel1 = TelnoValue( vobj, VCCellularProp )) && *tel1 + && (vcard = new_handycard( name, NULL, tel1 )) != NULL ) + addList( &handylist, vcard ); + deleteStr( tel1 ); + deleteStr( name ); + + } else if ( hasCategory( vobj, "Personal" ) ) { + name = NameValue( vobj, VCGivenNameProp ); + if ( (tel1 = TelnoValue( vobj, VCHomeProp )) && *tel1 + && (vcard = new_handycard( name, "!", tel1 )) != NULL ) + addList( &handylist, vcard ); + + if ( (tel2 = TelnoValue( vobj, VCWorkProp )) && *tel2 + && strcmp( tel1, tel2 ) != 0 + && (vcard = new_handycard( name, "-Büro!", tel2 )) != NULL ) + addList( &handylist, vcard ); + deleteStr( tel2 ); + + if ( (tel2 = TelnoValue( vobj, VCCellularProp )) && *tel2 + && strcmp( tel1, tel2 ) != 0 + && (vcard = new_handycard( name, "-mobil!", tel2 )) != NULL ) + addList( &handylist, vcard ); + deleteStr( tel2 ); + deleteStr( tel1 ); + deleteStr( name ); + + } else { /* Business etc. */ + name = NameValue( vobj, VCFamilyNameProp ); + if ( (tel1 = TelnoValue( vobj, VCWorkProp )) && *tel1 + && (vcard = new_handycard( name, NULL, tel1 )) != NULL ) + addList( &handylist, vcard ); + + if ( (tel2 = TelnoValue( vobj, VCHomeProp )) && *tel2 + && strcmp( tel1, tel2 ) != 0 + && (vcard = new_handycard( name, "-priv", tel2 )) != NULL ) + addList( &handylist, vcard ); + deleteStr( tel2 ); + + if ( (tel2 = TelnoValue( vobj, VCCellularProp )) && *tel2 + && strcmp( tel1, tel2 ) != 0 + && (vcard = new_handycard( name, "-mobil", tel2 )) != NULL ) + addList( &handylist, vcard ); + deleteStr( tel2 ); + deleteStr( tel1 ); + deleteStr( name ); + } + } + if ( vdel ) + cleanVObject( vdel ); + + vobj = handylist; + while ( vobj ) { + vnext = nextVObjectInList( vobj ); + addList( vlist, vobj ); + vobj = vnext; + } + return OK; +} + + +/* -------------------- M A I N - program ----------------------------- */ +static void +versinfo( void ) +{ + printf( "vCard/vCalendar converter %s (%s)\n", pkgvers, pkgdate ); + printf( "%s\n", bldinfo ); + printf( + "Copyright (C) 2000,2001 Thomas Schulz\n" + "This is free software; see the GNU General Public Licence version 2 in\n" + "the file named COPYING for copying conditions. There is NO warranty.\n" + ); +} +static void +usage( void ) +{ + static char * usetext[] = { + "usage:", + " vcaconv bin2vca binfile vcafile", + " convert IC35 binary data to vCard,vCalendar format", + " vcaconv bin2txt binfile txtfile", + " convert IC35 binary data to plain text format", + " vcaconv vca2bin vcafile binfile", + " convert vCard,vCalendar file to IC35 binary data", + " vcaconv prvca vcafile", + " pretty print vCard/vCalendar format 'vcafile'", + " vcaconv sortvca vcafile", + " sort vCard,vCalendar file to standard output", + " vcaconv imphandy vcafile", + " create vCards in HANDY category for upload to mobile phone", + " if inputfile or outputfile is absent or -, standard input", + " or standard output will be used", + "options:", + " -V --version show version information and exit", + " -h --help show this help and exit", + NULL + }; + char ** lptr; + + for ( lptr = usetext; *lptr != NULL; ++lptr ) + printf( "%s\n", *lptr ); +} + +int +main( int argc, char *argv[] ) +{ + static struct option const long_opts[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 } + }; + + for ( ; ; ) { + switch ( getopt_long( argc, argv, "hV", long_opts, NULL ) ) { + default: /* invalid option */ + fprintf( stderr, "use 'vcaconv --help' for more information\n" ); + return 1; + case 'h': /* show Help and exit */ + usage(); + return 0; + case 'V': /* show Version and exit */ + versinfo(); + return 0; + case -1: /* end of options */ + break; + } + break; + } + if ( argc < 2 ) { + error( "missing conversion\n" + "use 'vcaconv --help' for more information" ); + return 1; + } + if ( ( strcmp( argv[1], "bin2vca" ) == 0 + || strcmp( argv[1], "bin2txt" ) == 0 + || strcmp( argv[1], "vca2bin" ) == 0 ) + && 2 <= argc && argc <= 4 ) { + void * rec; + + argv[1][3] = '\0'; /* split input,output format */ + if ( pim_openinp( argv[1]+0, argc >= 3 ? argv[2] : "-" ) != OK ) { + error( "bad input format/file: %s %s\n", argv[1]+0, argv[2] ); + return 1; + } + if ( pim_openout( argv[1]+4, argc >= 4 ? argv[3] : "-" ) != OK ) { + error( "bad output format/file: %s %s\n", argv[1]+4, argv[3] ); + return 1; + } + while ( (rec = pim_getrec( FILE_ANY )) != NULL ) + pim_putrec( rec ); + pim_close(); + + } else if ( strcmp( argv[1], "prvca" ) == 0 + && 2 <= argc && argc <= 3 ) { + VObject * vlist; + VObject * vdel; + + if ( (vlist = vca_parse( argc >= 3 ? argv[2] : "-" )) == NULL ) + return 1; + while ( vlist ) { + printVObject( stdout, vlist ); + vlist = nextVObjectInList( vdel = vlist ); + cleanVObject( vdel ); + } + + } else if ( strcmp( argv[1], "sortvca" ) == 0 + && 2 <= argc && argc <= 3 ) { + VObject * vlist; + VObject * vdel; + + if ( (vlist = vca_parse( argc >= 3 ? argv[2] : "-" )) == NULL ) + return 1; + vca_sort( &vlist ); + while ( vlist ) { + writeVObject( stdout, vlist ); + vlist = nextVObjectInList( vdel = vlist ); + cleanVObject( vdel ); + } + + } else if ( strcmp( argv[1], "imphandy" ) == 0 + && 2 <= argc && argc <= 3 ) { + VObject * vlist; + VObject * vdel; + + if ( (vlist = vca_parse( argc >= 3 ? argv[2] : "-" )) == NULL ) + return 1; + vcard_to_handy( &vlist ); + while ( vlist ) { + writeVObject( stdout, vlist ); + vlist = nextVObjectInList( vdel = vlist ); + cleanVObject( vdel ); + } + + } else { + error( "unknown conversion: %s\n" + "use 'vcaconv --help' for more information", argv[1] ); + return 1; + } + return 0; +} diff --git a/src/vcc.c b/src/vcc.c new file mode 100644 index 0000000..57a44b5 --- /dev/null +++ b/src/vcc.c @@ -0,0 +1,2710 @@ +/* A Bison parser, made by GNU Bison 3.7. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 Free Software Foundation, + Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, + especially those whose name start with YY_ or yy_. They are + private implementation details that can be changed or removed. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "3.7" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 0 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + + + +/* First part of user prologue. */ +#line 1 "vcc.y" + + +/*************************************************************************** +(C) Copyright 1996 Apple Computer, Inc., AT&T Corp., International +Business Machines Corporation and Siemens Rolm Communications Inc. + +modified 2000 by Thomas Schulz: +$Id: vcc.y,v 1.7 2001/02/10 03:09:24 tsch Rel $ + +For purposes of this license notice, the term Licensors shall mean, +collectively, Apple Computer, Inc., AT&T Corp., International +Business Machines Corporation and Siemens Rolm Communications Inc. +The term Licensor shall mean any of the Licensors. + +Subject to acceptance of the following conditions, permission is hereby +granted by Licensors without the need for written agreement and without +license or royalty fees, to use, copy, modify and distribute this +software for any purpose. + +The above copyright notice and the following four paragraphs must be +reproduced in all copies of this software and any software including +this software. + +THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS AND NO LICENSOR SHALL HAVE +ANY OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS OR +MODIFICATIONS. + +IN NO EVENT SHALL ANY LICENSOR BE LIABLE TO ANY PARTY FOR DIRECT, +INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT +OF THE USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +EACH LICENSOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO ANY WARRANTY OF NONINFRINGEMENT OR THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. + +The software is provided with RESTRICTED RIGHTS. Use, duplication, or +disclosure by the government are subject to restrictions set forth in +DFARS 252.227-7013 or 48 CFR 52.227-19, as applicable. + +***************************************************************************/ + +/* + * src: vcc.c + * doc: Parser for vCard and vCalendar. Note that this code is + * generated by a yacc parser generator. Generally it should not + * be edited by hand. The real source is vcc.y. The #line directives + * can be commented out here to make it easier to trace through + * in a debugger. However, if a bug is found it should + * be fixed in vcc.y and this file regenerated. + */ + + +/* debugging utilities */ +#if __DEBUG +#define DBG_(x) printf x +#else +#define DBG_(x) +#endif + +/**** External Functions ****/ + +/* assign local name to parser variables and functions so that + we can use more than one yacc based parser. +*/ + +#define yyparse mime_parse +#define yylex mime_lex +#define yyerror mime_error +#define yychar mime_char +/* #define p_yyval p_mime_val */ +#undef yyval +#define yyval mime_yyval +/* #define p_yylval p_mime_lval */ +#undef yylval +#define yylval mime_yylval +#define yydebug mime_debug +#define yynerrs mime_nerrs +#define yyerrflag mime_errflag +#define yyss mime_ss +#define yyssp mime_ssp +#define yyvs mime_vs +#define yyvsp mime_vsp +#define yylhs mime_lhs +#define yylen mime_len +#define yydefred mime_defred +#define yydgoto mime_dgoto +#define yysindex mime_sindex +#define yyrindex mime_rindex +#define yygindex mime_gindex +#define yytable mime_table +#define yycheck mime_check +#define yyname mime_name +#define yyrule mime_rule +#define YYPREFIX "mime_" + + +#ifndef _NO_LINE_FOLDING +#define _SUPPORT_LINE_FOLDING 1 +#endif + +/* undef below if compile with MFC */ +/* #define INCLUDEMFC 1 */ + +#if defined(WIN32) || defined(_WIN32) +#ifdef INCLUDEMFC +#include +#endif +#endif + +#include +#ifndef __MWERKS__ +#include +#endif +#include +#include +#include +#include "vcc.h" + +/**** Types, Constants ****/ + +#define YYDEBUG 0 /* 1 to compile in some debugging code */ +#define YYERROR_VERBOSE 1 /* verbose parse error reports */ +#define MAXTOKEN 256 /* maximum token (line) length */ +#define YYSTACKSIZE 50 /* ~unref ? */ +#define MAXLEVEL 10 /* max # of nested objects parseable */ + /* (includes outermost) */ + + +/**** Global Variables ****/ +int mime_lineNum, mime_numErrors; /* yyerror() can use these */ +static VObject* vObjList; +static VObject *curProp; +static VObject *curObj; +static VObject* ObjStack[MAXLEVEL]; +static int ObjStackTop; + + +/* A helpful utility for the rest of the app. */ +#if __CPLUSPLUS__ +extern "C" { +#endif + + extern void Parse_Debug(const char *s); + static void yyerror(char *s); + +#if __CPLUSPLUS__ + }; +#endif + +int yyparse(void); +static int yylex(void); + +enum LexMode { + L_NORMAL, + L_VCARD, + L_VCAL, + L_VEVENT, + L_VTODO, + L_VALUES, + L_BASE64, + L_QUOTED_PRINTABLE + }; + +/**** Private Forward Declarations ****/ +static int pushVObject(const char *prop); +static VObject* popVObject(void); +static char* lexGetDataFromBase64(void); +static void lexPopMode(int top); +static int lexWithinMode(enum LexMode mode); +static void lexPushMode(enum LexMode mode); +static void enterProps(const char *s); +static void enterAttr(const char *s1, const char *s2); +static void appendValue(const char *value); +static void mime_error_(char *s); + + +#line 250 "vcc.c" + +# ifndef YY_CAST +# ifdef __cplusplus +# define YY_CAST(Type, Val) static_cast (Val) +# define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast (Val) +# else +# define YY_CAST(Type, Val) ((Type) (Val)) +# define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) +# endif +# endif +# ifndef YY_NULLPTR +# if defined __cplusplus +# if 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# else +# define YY_NULLPTR ((void*)0) +# endif +# endif + + +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int yydebug; +#endif + +/* Token kinds. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + YYEMPTY = -2, + YYEOF = 0, /* "end of file" */ + YYerror = 256, /* error */ + YYUNDEF = 257, /* "invalid token" */ + EQ = 258, /* EQ */ + COLON = 259, /* COLON */ + DOT = 260, /* DOT */ + SEMICOLON = 261, /* SEMICOLON */ + SPACE = 262, /* SPACE */ + HTAB = 263, /* HTAB */ + LINESEP = 264, /* LINESEP */ + NEWLINE = 265, /* NEWLINE */ + BEGIN_VCARD = 266, /* BEGIN_VCARD */ + END_VCARD = 267, /* END_VCARD */ + BEGIN_VCAL = 268, /* BEGIN_VCAL */ + END_VCAL = 269, /* END_VCAL */ + BEGIN_VEVENT = 270, /* BEGIN_VEVENT */ + END_VEVENT = 271, /* END_VEVENT */ + BEGIN_VTODO = 272, /* BEGIN_VTODO */ + END_VTODO = 273, /* END_VTODO */ + BEGIN_VMEMO = 274, /* BEGIN_VMEMO */ + END_VMEMO = 275, /* END_VMEMO */ + ID = 276, /* ID */ + STRING = 277 /* STRING */ + }; + typedef enum yytokentype yytoken_kind_t; +#endif +/* Token kinds. */ +#define YYEOF 0 +#define YYerror 256 +#define YYUNDEF 257 +#define EQ 258 +#define COLON 259 +#define DOT 260 +#define SEMICOLON 261 +#define SPACE 262 +#define HTAB 263 +#define LINESEP 264 +#define NEWLINE 265 +#define BEGIN_VCARD 266 +#define END_VCARD 267 +#define BEGIN_VCAL 268 +#define END_VCAL 269 +#define BEGIN_VEVENT 270 +#define END_VEVENT 271 +#define BEGIN_VTODO 272 +#define END_VTODO 273 +#define BEGIN_VMEMO 274 +#define END_VMEMO 275 +#define ID 276 +#define STRING 277 + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 184 "vcc.y" + + char *str; + VObject *vobj; + + +#line 349 "vcc.c" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + + +extern YYSTYPE yylval; + +int yyparse (void); + + +/* Symbol kind. */ +enum yysymbol_kind_t +{ + YYSYMBOL_YYEMPTY = -2, + YYSYMBOL_YYEOF = 0, /* "end of file" */ + YYSYMBOL_YYerror = 1, /* error */ + YYSYMBOL_YYUNDEF = 2, /* "invalid token" */ + YYSYMBOL_EQ = 3, /* EQ */ + YYSYMBOL_COLON = 4, /* COLON */ + YYSYMBOL_DOT = 5, /* DOT */ + YYSYMBOL_SEMICOLON = 6, /* SEMICOLON */ + YYSYMBOL_SPACE = 7, /* SPACE */ + YYSYMBOL_HTAB = 8, /* HTAB */ + YYSYMBOL_LINESEP = 9, /* LINESEP */ + YYSYMBOL_NEWLINE = 10, /* NEWLINE */ + YYSYMBOL_BEGIN_VCARD = 11, /* BEGIN_VCARD */ + YYSYMBOL_END_VCARD = 12, /* END_VCARD */ + YYSYMBOL_BEGIN_VCAL = 13, /* BEGIN_VCAL */ + YYSYMBOL_END_VCAL = 14, /* END_VCAL */ + YYSYMBOL_BEGIN_VEVENT = 15, /* BEGIN_VEVENT */ + YYSYMBOL_END_VEVENT = 16, /* END_VEVENT */ + YYSYMBOL_BEGIN_VTODO = 17, /* BEGIN_VTODO */ + YYSYMBOL_END_VTODO = 18, /* END_VTODO */ + YYSYMBOL_BEGIN_VMEMO = 19, /* BEGIN_VMEMO */ + YYSYMBOL_END_VMEMO = 20, /* END_VMEMO */ + YYSYMBOL_ID = 21, /* ID */ + YYSYMBOL_STRING = 22, /* STRING */ + YYSYMBOL_YYACCEPT = 23, /* $accept */ + YYSYMBOL_mime = 24, /* mime */ + YYSYMBOL_vobjects = 25, /* vobjects */ + YYSYMBOL_26_1 = 26, /* $@1 */ + YYSYMBOL_vobject = 27, /* vobject */ + YYSYMBOL_vcard = 28, /* vcard */ + YYSYMBOL_29_2 = 29, /* $@2 */ + YYSYMBOL_30_3 = 30, /* $@3 */ + YYSYMBOL_items = 31, /* items */ + YYSYMBOL_item = 32, /* item */ + YYSYMBOL_33_4 = 33, /* $@4 */ + YYSYMBOL_prop = 34, /* prop */ + YYSYMBOL_35_5 = 35, /* $@5 */ + YYSYMBOL_attr_params = 36, /* attr_params */ + YYSYMBOL_attr_param = 37, /* attr_param */ + YYSYMBOL_attr = 38, /* attr */ + YYSYMBOL_name = 39, /* name */ + YYSYMBOL_values = 40, /* values */ + YYSYMBOL_41_6 = 41, /* $@6 */ + YYSYMBOL_value = 42, /* value */ + YYSYMBOL_vmemo = 43, /* vmemo */ + YYSYMBOL_44_7 = 44, /* $@7 */ + YYSYMBOL_45_8 = 45, /* $@8 */ + YYSYMBOL_vcal = 46, /* vcal */ + YYSYMBOL_47_9 = 47, /* $@9 */ + YYSYMBOL_48_10 = 48, /* $@10 */ + YYSYMBOL_calitems = 49, /* calitems */ + YYSYMBOL_calitem = 50, /* calitem */ + YYSYMBOL_eventitem = 51, /* eventitem */ + YYSYMBOL_52_11 = 52, /* $@11 */ + YYSYMBOL_53_12 = 53, /* $@12 */ + YYSYMBOL_todoitem = 54, /* todoitem */ + YYSYMBOL_55_13 = 55, /* $@13 */ + YYSYMBOL_56_14 = 56 /* $@14 */ +}; +typedef enum yysymbol_kind_t yysymbol_kind_t; + + + + +#ifdef short +# undef short +#endif + +/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure + and (if available) are included + so that the code can choose integer types of a good width. */ + +#ifndef __PTRDIFF_MAX__ +# include /* INFRINGES ON USER NAME SPACE */ +# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_STDINT_H +# endif +#endif + +/* Narrow types that promote to a signed type and that can represent a + signed or unsigned integer of at least N bits. In tables they can + save space and decrease cache pressure. Promoting to a signed type + helps avoid bugs in integer arithmetic. */ + +#ifdef __INT_LEAST8_MAX__ +typedef __INT_LEAST8_TYPE__ yytype_int8; +#elif defined YY_STDINT_H +typedef int_least8_t yytype_int8; +#else +typedef signed char yytype_int8; +#endif + +#ifdef __INT_LEAST16_MAX__ +typedef __INT_LEAST16_TYPE__ yytype_int16; +#elif defined YY_STDINT_H +typedef int_least16_t yytype_int16; +#else +typedef short yytype_int16; +#endif + +#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST8_TYPE__ yytype_uint8; +#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST8_MAX <= INT_MAX) +typedef uint_least8_t yytype_uint8; +#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX +typedef unsigned char yytype_uint8; +#else +typedef short yytype_uint8; +#endif + +#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST16_TYPE__ yytype_uint16; +#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST16_MAX <= INT_MAX) +typedef uint_least16_t yytype_uint16; +#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX +typedef unsigned short yytype_uint16; +#else +typedef int yytype_uint16; +#endif + +#ifndef YYPTRDIFF_T +# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__ +# define YYPTRDIFF_T __PTRDIFF_TYPE__ +# define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__ +# elif defined PTRDIFF_MAX +# ifndef ptrdiff_t +# include /* INFRINGES ON USER NAME SPACE */ +# endif +# define YYPTRDIFF_T ptrdiff_t +# define YYPTRDIFF_MAXIMUM PTRDIFF_MAX +# else +# define YYPTRDIFF_T long +# define YYPTRDIFF_MAXIMUM LONG_MAX +# endif +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned +# endif +#endif + +#define YYSIZE_MAXIMUM \ + YY_CAST (YYPTRDIFF_T, \ + (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1) \ + ? YYPTRDIFF_MAXIMUM \ + : YY_CAST (YYSIZE_T, -1))) + +#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X)) + + +/* Stored state numbers (used for stacks). */ +typedef yytype_int8 yy_state_t; + +/* State numbers in computations. */ +typedef int yy_state_fast_t; + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + + +#ifndef YY_ATTRIBUTE_PURE +# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) +# else +# define YY_ATTRIBUTE_PURE +# endif +#endif + +#ifndef YY_ATTRIBUTE_UNUSED +# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +# else +# define YY_ATTRIBUTE_UNUSED +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(E) ((void) (E)) +#else +# define YYUSE(E) /* empty */ +#endif + +#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + +#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ +# define YY_IGNORE_USELESS_CAST_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") +# define YY_IGNORE_USELESS_CAST_END \ + _Pragma ("GCC diagnostic pop") +#endif +#ifndef YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_END +#endif + + +#define YY_ASSERT(E) ((void) (0 && (E))) + +#if !defined yyoverflow + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS +# include /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* !defined yyoverflow */ + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yy_state_t yyss_alloc; + YYSTYPE yyvs_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYPTRDIFF_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / YYSIZEOF (*yyptr); \ + } \ + while (0) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYPTRDIFF_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 16 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 61 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 23 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 34 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 52 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 69 + +/* YYMAXUTOK -- Last valid token kind. */ +#define YYMAXUTOK 277 + + +/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, with out-of-bounds checking. */ +#define YYTRANSLATE(YYX) \ + (0 <= (YYX) && (YYX) <= YYMAXUTOK \ + ? YY_CAST (yysymbol_kind_t, yytranslate[YYX]) \ + : YYSYMBOL_YYUNDEF) + +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex. */ +static const yytype_int8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22 +}; + +#if YYDEBUG + /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +static const yytype_int16 yyrline[] = +{ + 0, 215, 215, 219, 218, 221, 225, 226, 227, 232, + 231, 242, 241, 253, 254, 258, 257, 267, 271, 270, + 275, 281, 282, 285, 288, 292, 299, 302, 302, 303, + 307, 308, 313, 312, 318, 317, 325, 324, 330, 329, + 335, 336, 340, 341, 342, 347, 346, 358, 357, 371, + 370, 382, 381 +}; +#endif + +/** Accessing symbol of state STATE. */ +#define YY_ACCESSING_SYMBOL(State) YY_CAST (yysymbol_kind_t, yystos[State]) + +#if YYDEBUG || 0 +/* The user-facing name of the symbol whose (internal) number is + YYSYMBOL. No bounds checking. */ +static const char *yysymbol_name (yysymbol_kind_t yysymbol) YY_ATTRIBUTE_UNUSED; + +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "\"end of file\"", "error", "\"invalid token\"", "EQ", "COLON", "DOT", + "SEMICOLON", "SPACE", "HTAB", "LINESEP", "NEWLINE", "BEGIN_VCARD", + "END_VCARD", "BEGIN_VCAL", "END_VCAL", "BEGIN_VEVENT", "END_VEVENT", + "BEGIN_VTODO", "END_VTODO", "BEGIN_VMEMO", "END_VMEMO", "ID", "STRING", + "$accept", "mime", "vobjects", "$@1", "vobject", "vcard", "$@2", "$@3", + "items", "item", "$@4", "prop", "$@5", "attr_params", "attr_param", + "attr", "name", "values", "$@6", "value", "vmemo", "$@7", "$@8", "vcal", + "$@9", "$@10", "calitems", "calitem", "eventitem", "$@11", "$@12", + "todoitem", "$@13", "$@14", YY_NULLPTR +}; + +static const char * +yysymbol_name (yysymbol_kind_t yysymbol) +{ + return yytname[yysymbol]; +} +#endif + +#ifdef YYPRINT +/* YYTOKNUM[NUM] -- (External) token number corresponding to the + (internal) symbol number NUM (which must be that of a token). */ +static const yytype_int16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277 +}; +#endif + +#define YYPACT_NINF (-42) + +#define yypact_value_is_default(Yyn) \ + ((Yyn) == YYPACT_NINF) + +#define YYTABLE_NINF (-52) + +#define yytable_value_is_error(Yyn) \ + 0 + + /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +static const yytype_int8 yypact[] = +{ + 23, -9, -12, -15, 10, -42, 13, -42, -42, -42, + 5, -4, 18, 14, 5, 7, -42, 23, -42, -42, + 19, 0, 33, 34, -42, 24, 25, -42, 27, 8, + -42, -42, -42, 26, -42, -42, -42, -42, -42, 38, + 5, 29, 5, 30, -42, -42, -42, 28, 31, -42, + 38, 35, -42, 36, -42, -42, 40, 41, -42, 50, + -42, -42, -42, -42, -42, 31, 28, -42, -42 +}; + + /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ +static const yytype_int8 yydefact[] = +{ + 0, 9, 36, 32, 0, 2, 3, 6, 8, 7, + 0, 0, 0, 0, 0, 0, 1, 0, 17, 26, + 0, 0, 0, 18, 12, 45, 49, 44, 0, 0, + 42, 43, 39, 0, 35, 4, 10, 13, 15, 0, + 0, 0, 0, 0, 37, 40, 33, 31, 0, 19, + 22, 0, 48, 0, 52, 30, 0, 29, 23, 24, + 21, 46, 50, 16, 27, 0, 31, 25, 28 +}; + + /* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -42, -42, 39, -42, -42, -42, -42, -42, -10, -42, + -42, -42, -42, 9, -42, -42, -41, -11, -42, -42, + -42, -42, -42, -42, -42, -42, 32, -42, -42, -42, + -42, -42, -42, -42 +}; + + /* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int8 yydefgoto[] = +{ + -1, 4, 5, 17, 6, 7, 10, 11, 27, 21, + 47, 22, 39, 49, 50, 58, 23, 56, 66, 57, + 8, 14, 15, 9, 12, 13, 28, 29, 30, 40, + 41, 31, 42, 43 +}; + + /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ +static const yytype_int8 yytable[] = +{ + 20, 18, -38, -11, 33, -34, 18, 59, 24, 18, + 16, 37, -14, -5, -14, -14, -14, -14, -14, 18, + -14, 19, -41, 25, 67, 26, 19, 34, 32, 19, + 51, 36, 53, 25, 1, 26, 2, 38, -20, 19, + -47, 44, 3, -51, 48, 52, 46, 64, 54, 63, + 55, 61, 19, 65, 62, 68, 35, 0, 0, 60, + 0, 45 +}; + +static const yytype_int8 yycheck[] = +{ + 10, 1, 14, 12, 14, 20, 1, 48, 12, 1, + 0, 21, 12, 0, 14, 15, 16, 17, 18, 1, + 20, 21, 14, 15, 65, 17, 21, 20, 14, 21, + 40, 12, 42, 15, 11, 17, 13, 4, 4, 21, + 16, 14, 19, 18, 6, 16, 20, 6, 18, 9, + 22, 16, 21, 3, 18, 66, 17, -1, -1, 50, + -1, 29 +}; + + /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_int8 yystos[] = +{ + 0, 11, 13, 19, 24, 25, 27, 28, 43, 46, + 29, 30, 47, 48, 44, 45, 0, 26, 1, 21, + 31, 32, 34, 39, 12, 15, 17, 31, 49, 50, + 51, 54, 14, 31, 20, 25, 12, 31, 4, 35, + 52, 53, 55, 56, 14, 49, 20, 33, 6, 36, + 37, 31, 16, 31, 18, 22, 40, 42, 38, 39, + 36, 16, 18, 9, 6, 3, 41, 39, 40 +}; + + /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_int8 yyr1[] = +{ + 0, 23, 24, 26, 25, 25, 27, 27, 27, 29, + 28, 30, 28, 31, 31, 33, 32, 32, 35, 34, + 34, 36, 36, 37, 38, 38, 39, 41, 40, 40, + 42, 42, 44, 43, 45, 43, 47, 46, 48, 46, + 49, 49, 50, 50, 50, 52, 51, 53, 51, 55, + 54, 56, 54 +}; + + /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ +static const yytype_int8 yyr2[] = +{ + 0, 2, 1, 0, 3, 1, 1, 1, 1, 0, + 4, 0, 3, 2, 1, 0, 5, 1, 0, 3, + 1, 2, 1, 2, 1, 3, 1, 0, 4, 1, + 1, 0, 0, 4, 0, 3, 0, 4, 0, 3, + 2, 1, 1, 1, 1, 0, 4, 0, 3, 0, + 4, 0, 3 +}; + + +enum { YYENOMEM = -2 }; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ + do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ + while (0) + +/* Backward compatibility with an undocumented macro. + Use YYerror or YYUNDEF. */ +#define YYERRCODE YYUNDEF + + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + +/* This macro is provided for backward compatibility. */ +# ifndef YY_LOCATION_PRINT +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif + + +# define YY_SYMBOL_PRINT(Title, Kind, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Kind, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + + +/*-----------------------------------. +| Print this symbol's value on YYO. | +`-----------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyo, + yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep) +{ + FILE *yyoutput = yyo; + YYUSE (yyoutput); + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yykind < YYNTOKENS) + YYPRINT (yyo, yytoknum[yykind], *yyvaluep); +# endif + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YYUSE (yykind); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/*---------------------------. +| Print this symbol on YYO. | +`---------------------------*/ + +static void +yy_symbol_print (FILE *yyo, + yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep) +{ + YYFPRINTF (yyo, "%s %s (", + yykind < YYNTOKENS ? "token" : "nterm", yysymbol_name (yykind)); + + yy_symbol_value_print (yyo, yykind, yyvaluep); + YYFPRINTF (yyo, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +static void +yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop) +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +static void +yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, + int yyrule) +{ + int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, + YY_ACCESSING_SYMBOL (+yyssp[yyi + 1 - yynrhs]), + &yyvsp[(yyi + 1) - (yynrhs)]); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, Rule); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) ((void) 0) +# define YY_SYMBOL_PRINT(Title, Kind, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + + + + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +static void +yydestruct (const char *yymsg, + yysymbol_kind_t yykind, YYSTYPE *yyvaluep) +{ + YYUSE (yyvaluep); + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yykind, yyvaluep, yylocationp); + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YYUSE (yykind); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/* Lookahead token kind. */ +int yychar; + +/* The semantic value of the lookahead symbol. */ +YYSTYPE yylval; +/* Number of syntax errors so far. */ +int yynerrs; + + + + +/*----------. +| yyparse. | +`----------*/ + +int +yyparse (void) +{ + yy_state_fast_t yystate = 0; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus = 0; + + /* Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* Their size. */ + YYPTRDIFF_T yystacksize = YYINITDEPTH; + + /* The state stack: array, bottom, top. */ + yy_state_t yyssa[YYINITDEPTH]; + yy_state_t *yyss = yyssa; + yy_state_t *yyssp = yyss; + + /* The semantic value stack: array, bottom, top. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + YYSTYPE *yyvsp = yyvs; + + int yyn; + /* The return value of yyparse. */ + int yyresult; + /* Lookahead symbol kind. */ + yysymbol_kind_t yytoken = YYSYMBOL_YYEMPTY; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + + + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yychar = YYEMPTY; /* Cause a token to be read. */ + goto yysetstate; + + +/*------------------------------------------------------------. +| yynewstate -- push a new state, which is found in yystate. | +`------------------------------------------------------------*/ +yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + +/*--------------------------------------------------------------------. +| yysetstate -- set current state (the top of the stack) to yystate. | +`--------------------------------------------------------------------*/ +yysetstate: + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + YY_ASSERT (0 <= yystate && yystate < YYNSTATES); + YY_IGNORE_USELESS_CAST_BEGIN + *yyssp = YY_CAST (yy_state_t, yystate); + YY_IGNORE_USELESS_CAST_END + YY_STACK_PRINT (yyss, yyssp); + + if (yyss + yystacksize - 1 <= yyssp) +#if !defined yyoverflow && !defined YYSTACK_RELOCATE + goto yyexhaustedlab; +#else + { + /* Get the current used size of the three stacks, in elements. */ + YYPTRDIFF_T yysize = yyssp - yyss + 1; + +# if defined yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + yy_state_t *yyss1 = yyss; + YYSTYPE *yyvs1 = yyvs; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * YYSIZEOF (*yyssp), + &yyvs1, yysize * YYSIZEOF (*yyvsp), + &yystacksize); + yyss = yyss1; + yyvs = yyvs1; + } +# else /* defined YYSTACK_RELOCATE */ + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yy_state_t *yyss1 = yyss; + union yyalloc *yyptr = + YY_CAST (union yyalloc *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize)))); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + YY_IGNORE_USELESS_CAST_BEGIN + YYDPRINTF ((stderr, "Stack size increased to %ld\n", + YY_CAST (long, yystacksize))); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } +#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either empty, or end-of-input, or a valid lookahead. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token\n")); + yychar = yylex (); + } + + if (yychar <= YYEOF) + { + yychar = YYEOF; + yytoken = YYSYMBOL_YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else if (yychar == YYerror) + { + /* The scanner already issued an error message, process directly + to error recovery. But do not keep the error token as + lookahead, it is too special and may lead us to an endless + loop in error recovery. */ + yychar = YYUNDEF; + yytoken = YYSYMBOL_YYerror; + goto yyerrlab1; + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + /* Discard the shifted token. */ + yychar = YYEMPTY; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + '$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 3: /* $@1: %empty */ +#line 219 "vcc.y" + { addList(&vObjList, (yyvsp[0].vobj)); curObj = 0; } +#line 1425 "vcc.c" + break; + + case 5: /* vobjects: vobject */ +#line 222 "vcc.y" + { addList(&vObjList, (yyvsp[0].vobj)); curObj = 0; } +#line 1431 "vcc.c" + break; + + case 9: /* $@2: %empty */ +#line 232 "vcc.y" + { + lexPushMode(L_VCARD); + if (!pushVObject(VCCardProp)) YYERROR; + } +#line 1440 "vcc.c" + break; + + case 10: /* vcard: BEGIN_VCARD $@2 items END_VCARD */ +#line 237 "vcc.y" + { + lexPopMode(0); + (yyval.vobj) = popVObject(); + } +#line 1449 "vcc.c" + break; + + case 11: /* $@3: %empty */ +#line 242 "vcc.y" + { + lexPushMode(L_VCARD); + if (!pushVObject(VCCardProp)) YYERROR; + } +#line 1458 "vcc.c" + break; + + case 12: /* vcard: BEGIN_VCARD $@3 END_VCARD */ +#line 247 "vcc.y" + { + lexPopMode(0); + (yyval.vobj) = popVObject(); + } +#line 1467 "vcc.c" + break; + + case 15: /* $@4: %empty */ +#line 258 "vcc.y" + { + lexPushMode(L_VALUES); + } +#line 1475 "vcc.c" + break; + + case 16: /* item: prop COLON $@4 values LINESEP */ +#line 262 "vcc.y" + { + if (lexWithinMode(L_BASE64) || lexWithinMode(L_QUOTED_PRINTABLE)) + lexPopMode(0); + lexPopMode(0); + } +#line 1485 "vcc.c" + break; + + case 18: /* $@5: %empty */ +#line 271 "vcc.y" + { + enterProps((yyvsp[0].str)); + } +#line 1493 "vcc.c" + break; + + case 20: /* prop: name */ +#line 276 "vcc.y" + { + enterProps((yyvsp[0].str)); + } +#line 1501 "vcc.c" + break; + + case 24: /* attr: name */ +#line 289 "vcc.y" + { + enterAttr((yyvsp[0].str),0); + } +#line 1509 "vcc.c" + break; + + case 25: /* attr: name EQ name */ +#line 293 "vcc.y" + { + enterAttr((yyvsp[-2].str),(yyvsp[0].str)); + + } +#line 1518 "vcc.c" + break; + + case 27: /* $@6: %empty */ +#line 302 "vcc.y" + { appendValue((yyvsp[-1].str)); } +#line 1524 "vcc.c" + break; + + case 29: /* values: value */ +#line 304 "vcc.y" + { appendValue((yyvsp[0].str)); } +#line 1530 "vcc.c" + break; + + case 31: /* value: %empty */ +#line 308 "vcc.y" + { (yyval.str) = 0; } +#line 1536 "vcc.c" + break; + + case 32: /* $@7: %empty */ +#line 313 "vcc.y" + { if (!pushVObject(VCMemoProp)) YYERROR; } +#line 1542 "vcc.c" + break; + + case 33: /* vmemo: BEGIN_VMEMO $@7 items END_VMEMO */ +#line 316 "vcc.y" + { (yyval.vobj) = popVObject(); } +#line 1548 "vcc.c" + break; + + case 34: /* $@8: %empty */ +#line 318 "vcc.y" + { if (!pushVObject(VCMemoProp)) YYERROR; } +#line 1554 "vcc.c" + break; + + case 35: /* vmemo: BEGIN_VMEMO $@8 END_VMEMO */ +#line 320 "vcc.y" + { (yyval.vobj) = popVObject(); } +#line 1560 "vcc.c" + break; + + case 36: /* $@9: %empty */ +#line 325 "vcc.y" + { if (!pushVObject(VCCalProp)) YYERROR; } +#line 1566 "vcc.c" + break; + + case 37: /* vcal: BEGIN_VCAL $@9 calitems END_VCAL */ +#line 328 "vcc.y" + { (yyval.vobj) = popVObject(); } +#line 1572 "vcc.c" + break; + + case 38: /* $@10: %empty */ +#line 330 "vcc.y" + { if (!pushVObject(VCCalProp)) YYERROR; } +#line 1578 "vcc.c" + break; + + case 39: /* vcal: BEGIN_VCAL $@10 END_VCAL */ +#line 332 "vcc.y" + { (yyval.vobj) = popVObject(); } +#line 1584 "vcc.c" + break; + + case 45: /* $@11: %empty */ +#line 347 "vcc.y" + { + lexPushMode(L_VEVENT); + if (!pushVObject(VCEventProp)) YYERROR; + } +#line 1593 "vcc.c" + break; + + case 46: /* eventitem: BEGIN_VEVENT $@11 items END_VEVENT */ +#line 353 "vcc.y" + { + lexPopMode(0); + popVObject(); + } +#line 1602 "vcc.c" + break; + + case 47: /* $@12: %empty */ +#line 358 "vcc.y" + { + lexPushMode(L_VEVENT); + if (!pushVObject(VCEventProp)) YYERROR; + } +#line 1611 "vcc.c" + break; + + case 48: /* eventitem: BEGIN_VEVENT $@12 END_VEVENT */ +#line 363 "vcc.y" + { + lexPopMode(0); + popVObject(); + } +#line 1620 "vcc.c" + break; + + case 49: /* $@13: %empty */ +#line 371 "vcc.y" + { + lexPushMode(L_VTODO); + if (!pushVObject(VCTodoProp)) YYERROR; + } +#line 1629 "vcc.c" + break; + + case 50: /* todoitem: BEGIN_VTODO $@13 items END_VTODO */ +#line 377 "vcc.y" + { + lexPopMode(0); + popVObject(); + } +#line 1638 "vcc.c" + break; + + case 51: /* $@14: %empty */ +#line 382 "vcc.y" + { + lexPushMode(L_VTODO); + if (!pushVObject(VCTodoProp)) YYERROR; + } +#line 1647 "vcc.c" + break; + + case 52: /* todoitem: BEGIN_VTODO $@14 END_VTODO */ +#line 387 "vcc.y" + { + lexPopMode(0); + popVObject(); + } +#line 1656 "vcc.c" + break; + + +#line 1660 "vcc.c" + + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", YY_CAST (yysymbol_kind_t, yyr1[yyn]), &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + + *++yyvsp = yyval; + + /* Now 'shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + { + const int yylhs = yyr1[yyn] - YYNTOKENS; + const int yyi = yypgoto[yylhs] + *yyssp; + yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp + ? yytable[yyi] + : yydefgoto[yylhs]); + } + + goto yynewstate; + + +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYSYMBOL_YYEMPTY : YYTRANSLATE (yychar); + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; + yyerror (YY_("syntax error")); + } + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + /* Pacify compilers when the user code never invokes YYERROR and the + label yyerrorlab therefore never appears in user code. */ + if (0) + YYERROR; + + /* Do not reclaim the symbols of the rule whose action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + /* Pop stack until we find a state that shifts the error token. */ + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYSYMBOL_YYerror; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYSYMBOL_YYerror) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + YY_ACCESSING_SYMBOL (yystate), yyvsp); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", YY_ACCESSING_SYMBOL (yyn), yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + + +#if !defined yyoverflow +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (YY_("memory exhausted")); + yyresult = 2; + goto yyreturn; +#endif + + +/*-------------------------------------------------------. +| yyreturn -- parsing is finished, clean up and return. | +`-------------------------------------------------------*/ +yyreturn: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval); + } + /* Do not reclaim the symbols of the rule whose action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + YY_ACCESSING_SYMBOL (+*yyssp), yyvsp); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif + + return yyresult; +} + +#line 393 "vcc.y" + +/***************************************************************************/ +static int pushVObject(const char *prop) + { + VObject *newObj; + if (ObjStackTop == MAXLEVEL) + return FALSE; + + ObjStack[++ObjStackTop] = curObj; + + if (curObj) { + newObj = addProp(curObj,prop); + curObj = newObj; + } + else + curObj = newVObject(prop); + + return TRUE; + } + + +/***************************************************************************/ +/* This pops the recently built vCard off the stack and returns it. */ +static VObject* popVObject(void) + { + VObject *oldObj; + if (ObjStackTop < 0) { + yyerror("pop on empty Object Stack\n"); + return 0; + } + oldObj = curObj; + curObj = ObjStack[ObjStackTop--]; + + return oldObj; + } + +static void appendValue(const char *value) +{ + char *p1, *p2; + wchar_t *p3; + int i; + + if (fieldedProp && *fieldedProp) { + if (value) { + addPropValue(curProp, *fieldedProp, value); + } + /* else this field is empty, advance to next field */ + fieldedProp++; + } else { + if (value) { + if (vObjectUStringZValue(curProp)) { + p1 = fakeCString(vObjectUStringZValue(curProp)); + p2 = malloc(sizeof(char *) * (strlen(p1)+strlen(value)+1)); + strcpy(p2, p1); + deleteStr(p1); + + i = strlen(p2); + p2[i] = ';'; + p2[i+1] = '\0'; + p2 = strcat(p2, value); + p3 = (wchar_t *) vObjectUStringZValue(curProp); + free(p3); + setVObjectUStringZValue_(curProp,fakeUnicode(p2,0)); + deleteStr(p2); + } else { + setVObjectUStringZValue_(curProp,fakeUnicode(value,0)); + } + } + } + deleteStr(value); +} + +static void enterProps(const char *s) + { + curProp = addGroup(curObj,s); + deleteStr(s); + } + +static void enterAttr(const char *s1, const char *s2) + { + const char *p1, *p2; + p1 = lookupProp_(s1); + if (s2) { + VObject *a; + p2 = lookupProp_(s2); + a = addProp(curProp,p1); + setVObjectStringZValue(a,p2); + } + else { + p2 = NULL; + addProp(curProp,p1); + } + if (stricmp(p1,VCBase64Prop) == 0 || (s2 && stricmp(p2,VCBase64Prop)==0)) + lexPushMode(L_BASE64); + else if (stricmp(p1,VCQuotedPrintableProp) == 0 + || (s2 && stricmp(p2,VCQuotedPrintableProp)==0)) + lexPushMode(L_QUOTED_PRINTABLE); + deleteStr(s1); deleteStr(s2); + } + + +#define MAX_LEX_LOOKAHEAD_0 32 +#define MAX_LEX_LOOKAHEAD 64 +#define MAX_LEX_MODE_STACK_SIZE 10 +#define LEXMODE() (lexBuf.lexModeStack[lexBuf.lexModeStackTop]) + +struct LexBuf { + /* input */ +#ifdef INCLUDEMFC + CFile *inputFile; +#else + FILE *inputFile; +#endif + char *inputString; + unsigned long curPos; + unsigned long inputLen; + /* lookahead buffer */ + /* -- lookahead buffer is short instead of char so that EOF + / can be represented correctly. + */ + unsigned long len; + short buf[MAX_LEX_LOOKAHEAD]; + unsigned long getPtr; + /* context stack */ + unsigned long lexModeStackTop; + enum LexMode lexModeStack[MAX_LEX_MODE_STACK_SIZE]; + /* token buffer */ + unsigned long maxToken; + char *strs; + unsigned long strsLen; + } lexBuf; + +static void lexPushMode(enum LexMode mode) + { + if (lexBuf.lexModeStackTop == (MAX_LEX_MODE_STACK_SIZE-1)) + yyerror("lexical context stack overflow"); + else { + lexBuf.lexModeStack[++lexBuf.lexModeStackTop] = mode; + } + } + +static void lexPopMode(int top) + { + /* special case of pop for ease of error recovery -- this + version will never underflow */ + if (top) + lexBuf.lexModeStackTop = 0; + else + if (lexBuf.lexModeStackTop > 0) lexBuf.lexModeStackTop--; + } + +static int lexWithinMode(enum LexMode mode) { + unsigned long i; + for (i=0;iRead(&result, 1) == 1 ? result : EOF; +#else + return fgetc(lexBuf.inputFile); +#endif + } + } + +static int lexGeta(void) + { + ++lexBuf.len; + return (lexBuf.buf[lexBuf.getPtr] = lexGetc_()); + } + +static int lexGeta_(int i) + { + ++lexBuf.len; + return (lexBuf.buf[(lexBuf.getPtr+i)%MAX_LEX_LOOKAHEAD] = lexGetc_()); + } + +static void lexSkipLookahead(void) { + if (lexBuf.len > 0 && lexBuf.buf[lexBuf.getPtr]!=EOF) { + /* don't skip EOF. */ + lexBuf.getPtr = (lexBuf.getPtr + 1) % MAX_LEX_LOOKAHEAD; + lexBuf.len--; + } + } + +static int lexLookahead(void) { + int c = (lexBuf.len)? + lexBuf.buf[lexBuf.getPtr]: + lexGeta(); + /* do the \r\n -> \n or \r -> \n translation here */ + if (c == '\r') { + int a = (lexBuf.len>1)? + lexBuf.buf[(lexBuf.getPtr+1)%MAX_LEX_LOOKAHEAD]: + lexGeta_(1); + if (a == '\n') { + lexSkipLookahead(); + } + lexBuf.buf[lexBuf.getPtr] = c = '\n'; + setCRLFmode( 1 ); /* note CRLF found on input */ + } + else if (c == '\n') { + int a = (lexBuf.len>1)? + lexBuf.buf[lexBuf.getPtr+1]: + lexGeta_(1); + if (a == '\r') { + lexSkipLookahead(); + } + lexBuf.buf[lexBuf.getPtr] = '\n'; + } + return c; + } + +static int lexGetc(void) { + int c = lexLookahead(); + if (lexBuf.len > 0 && lexBuf.buf[lexBuf.getPtr]!=EOF) { + /* EOF will remain in lookahead buffer */ + lexBuf.getPtr = (lexBuf.getPtr + 1) % MAX_LEX_LOOKAHEAD; + lexBuf.len--; + } + return c; + } + +static void lexSkipLookaheadWord(void) { + if (lexBuf.strsLen <= lexBuf.len) { + lexBuf.len -= lexBuf.strsLen; + lexBuf.getPtr = (lexBuf.getPtr + lexBuf.strsLen) % MAX_LEX_LOOKAHEAD; + } + } + +static void lexClearToken(void) + { + lexBuf.strsLen = 0; + } + +static void lexAppendc(int c) + { + lexBuf.strs[lexBuf.strsLen] = c; + /* append up to zero termination */ + if (c == 0) return; + lexBuf.strsLen++; + if (lexBuf.strsLen >= lexBuf.maxToken) { + /* double the token string size */ + lexBuf.maxToken <<= 1; + lexBuf.strs = (char*) realloc(lexBuf.strs,(size_t)lexBuf.maxToken); + } + } + +static char* lexStr(void) { + return dupStr(lexBuf.strs,(size_t)lexBuf.strsLen+1); + } + +static void lexSkipWhite(void) { + int c = lexLookahead(); + while (c == ' ' || c == '\t') { + lexSkipLookahead(); + c = lexLookahead(); + } + } + +static char* lexGetWord(void) { + int c; + lexSkipWhite(); + lexClearToken(); + c = lexLookahead(); + while (c != EOF && !strchr("\t\n ;:=",c)) { + lexAppendc(c); + lexSkipLookahead(); + c = lexLookahead(); + } + lexAppendc(0); + return lexStr(); + } + +static void lexPushLookaheadc(int c) { + int putptr; + /* can't putback EOF, because it never leaves lookahead buffer */ + if (c == EOF) return; + putptr = (int)lexBuf.getPtr - 1; + if (putptr < 0) putptr += MAX_LEX_LOOKAHEAD; + lexBuf.getPtr = putptr; + lexBuf.buf[putptr] = c; + lexBuf.len += 1; + } + +static char* lexLookaheadWord(void) { + /* this function can lookahead word with max size of MAX_LEX_LOOKAHEAD_0 + / and thing bigger than that will stop the lookahead and return 0; + / leading white spaces are not recoverable. + */ + int c; + int len = 0; + int curgetptr = 0; + lexSkipWhite(); + lexClearToken(); + curgetptr = (int)lexBuf.getPtr; /* remember! */ + while (len < (MAX_LEX_LOOKAHEAD_0)) { + c = lexGetc(); + len++; + if (c == EOF || strchr("\t\n ;:=", c)) { + lexAppendc(0); + /* restore lookahead buf. */ + lexBuf.len += len; + lexBuf.getPtr = curgetptr; + return lexStr(); + } + else + lexAppendc(c); + } + lexBuf.len += len; /* char that has been moved to lookahead buffer */ + lexBuf.getPtr = curgetptr; + return 0; + } + +#ifdef _SUPPORT_LINE_FOLDING +static void handleMoreRFC822LineBreak(int c) { + /* suport RFC 822 line break in cases like + * ADR: foo; + * morefoo; + * more foo; + */ + if (c == ';') { + int a; + lexSkipLookahead(); + /* skip white spaces */ + a = lexLookahead(); + while (a == ' ' || a == '\t') { + lexSkipLookahead(); + a = lexLookahead(); + } + if (a == '\n') { + lexSkipLookahead(); + a = lexLookahead(); + if (a == ' ' || a == '\t') { + /* continuation, throw away all the \n and spaces read so + * far + */ + lexSkipWhite(); + lexPushLookaheadc(';'); + } + else { + lexPushLookaheadc('\n'); + lexPushLookaheadc(';'); + } + } + else { + lexPushLookaheadc(';'); + } + } + } + +static char* lexGet1Value(void) { + int c; + lexSkipWhite(); + c = lexLookahead(); + lexClearToken(); + while (c != EOF && c != ';') { + if (c == '\n') { + int a; + lexSkipLookahead(); + a = lexLookahead(); + if (a == ' ' || a == '\t') { + lexAppendc(' '); + lexSkipLookahead(); + } + else { + lexPushLookaheadc('\n'); + break; + } + } + else { + lexAppendc(c); + lexSkipLookahead(); + } + c = lexLookahead(); + } + lexAppendc(0); + handleMoreRFC822LineBreak(c); + return c==EOF?0:lexStr(); + } +#endif + +#ifndef _SUPPORT_LINE_FOLDING +static char* lexGetStrUntil(char *termset) { + int c = lexLookahead(); + lexClearToken(); + while (c != EOF && !strchr(termset,c)) { + lexAppendc(c); + lexSkipLookahead(); + c = lexLookahead(); + } + lexAppendc(0); + return c==EOF?0:lexStr(); + } +#endif + +static int match_begin_name(int end) { + char *n = lexLookaheadWord(); + int token = ID; + if (n) { + if (!stricmp(n,"vcard")) token = end?END_VCARD:BEGIN_VCARD; + else if (!stricmp(n,"vcalendar")) token = end?END_VCAL:BEGIN_VCAL; + else if (!stricmp(n,"vevent")) token = end?END_VEVENT:BEGIN_VEVENT; + else if (!stricmp(n,"vtodo")) token = end?END_VTODO:BEGIN_VTODO; + else if (!stricmp(n,"vmemo")) token = end?END_VMEMO:BEGIN_VMEMO; + deleteStr(n); + return token; + } + return 0; + } + + +#ifdef INCLUDEMFC +void initLex(const char *inputstring, unsigned long inputlen, CFile *inputfile) +#else +static void initLex(const char *inputstring, unsigned long inputlen, FILE *inputfile) +#endif + { + /* initialize lex mode stack */ + lexBuf.lexModeStack[lexBuf.lexModeStackTop=0] = L_NORMAL; + + /* iniatialize lex buffer. */ + lexBuf.inputString = (char*) inputstring; + lexBuf.inputLen = inputlen; + lexBuf.curPos = 0; + lexBuf.inputFile = inputfile; + + lexBuf.len = 0; + lexBuf.getPtr = 0; + + lexBuf.maxToken = MAXTOKEN; + lexBuf.strs = (char*)malloc(MAXTOKEN); + lexBuf.strsLen = 0; + + } + +static void finiLex(void) { + free(lexBuf.strs); + } + + +/***************************************************************************/ +/* This parses and converts the base64 format for binary encoding into + * a decoded buffer (allocated with new). See RFC 1521. + */ +static char * lexGetDataFromBase64(void) + { + unsigned long bytesLen = 0, bytesMax = 0; + int quadIx = 0, pad = 0; + unsigned long trip = 0; + unsigned char b; + int c; + unsigned char *bytes = NULL; + unsigned char *oldBytes = NULL; + + DBG_(("db: lexGetDataFromBase64\n")); + while (1) { + c = lexGetc(); + if (c == '\n') { + ++mime_lineNum; + if (lexLookahead() == '\n') { + /* a '\n' character by itself means end of data */ + break; + } + else continue; /* ignore '\n' */ + } + else { + if ((c >= 'A') && (c <= 'Z')) + b = (unsigned char)(c - 'A'); + else if ((c >= 'a') && (c <= 'z')) + b = (unsigned char)(c - 'a') + 26; + else if ((c >= '0') && (c <= '9')) + b = (unsigned char)(c - '0') + 52; + else if (c == '+') + b = 62; + else if (c == '/') + b = 63; + else if (c == '=') { + b = 0; + pad++; + } else if ((c == ' ') || (c == '\t')) { + continue; + } else { /* error condition */ + if (bytes) free(bytes); + else if (oldBytes) free(oldBytes); + /* error recovery: skip until 2 adjacent newlines. */ + DBG_(("db: invalid character 0x%x '%c'\n", c,c)); + if (c != EOF) { + c = lexGetc(); + while (c != EOF) { + if (c == '\n' && lexLookahead() == '\n') { + ++mime_lineNum; + break; + } + c = lexGetc(); + } + } + return NULL; + } + trip = (trip << 6) | b; + if (++quadIx == 4) { + unsigned char outBytes[3]; + int numOut; + int i; + for (i = 0; i < 3; i++) { + outBytes[2-i] = (unsigned char)(trip & 0xFF); + trip >>= 8; + } + numOut = 3 - pad; + if (bytesLen + numOut > bytesMax) { + if (!bytes) { + bytesMax = 1024; + bytes = (unsigned char*)malloc((size_t)bytesMax); + } + else { + bytesMax <<= 2; + oldBytes = bytes; + bytes = (unsigned char*)realloc(bytes,(size_t)bytesMax); + } + if (bytes == 0) { + mime_error("out of memory while processing BASE64 data\n"); + } + } + if (bytes) { + memcpy(bytes + bytesLen, outBytes, numOut); + bytesLen += numOut; + } + trip = 0; + quadIx = 0; + } + } + } /* while */ + DBG_(("db: bytesLen = %d\n", bytesLen)); + /* kludge: all this won't be necessary if we have tree form + representation */ + if (bytes) { + setValueWithSize(curProp,bytes,(unsigned int)bytesLen); + free(bytes); + } + else if (oldBytes) { + setValueWithSize(curProp,oldBytes,(unsigned int)bytesLen); + free(oldBytes); + } + return 0; + } + +static int match_begin_end_name(int end) { + int token; + lexSkipWhite(); + if (lexLookahead() != ':') return ID; + lexSkipLookahead(); + lexSkipWhite(); + token = match_begin_name(end); + if (token == ID) { + lexPushLookaheadc(':'); + DBG_(("db: ID '%s'\n", yylval.str)); + return ID; + } + else if (token != 0) { + lexSkipLookaheadWord(); + deleteStr(yylval.str); + DBG_(("db: begin/end %d\n", token)); + return token; + } + return 0; + } + +static char* lexGetQuotedPrintable(void) + { + char cur; + + lexClearToken(); + do { + cur = lexGetc(); + switch (cur) { + case '=': { + int c = 0; + int next[2]; + int i; + for (i = 0; i < 2; i++) { + next[i] = lexGetc(); + if (next[i] >= '0' && next[i] <= '9') + c = c * 16 + next[i] - '0'; + else if (next[i] >= 'A' && next[i] <= 'F') + c = c * 16 + next[i] - 'A' + 10; + else + break; + } + if (i == 0) { + /* single '=' follow by LINESEP is continuation sign? */ + if (next[0] == '\n') { + ++mime_lineNum; + } + else { + lexPushLookaheadc('='); + goto EndString; + } + } + else if (i == 1) { + lexPushLookaheadc(next[1]); + lexPushLookaheadc(next[0]); + lexAppendc('='); + } else { + lexAppendc(c); + } + break; + } /* '=' */ + case '\n': { + lexPushLookaheadc('\n'); + goto EndString; + } + case (char)EOF: + break; + default: + lexAppendc(cur); + break; + } /* switch */ + } while (cur != (char)EOF); + +EndString: + lexAppendc(0); + return lexStr(); + } /* LexQuotedPrintable */ + +static int yylex(void) { + int lexmode = LEXMODE(); + + if (lexmode == L_VALUES) { + int c = lexGetc(); + if (c == ';') { + DBG_(("db: SEMICOLON\n")); + lexPushLookaheadc(c); +#ifdef _SUPPORT_LINE_FOLDING + handleMoreRFC822LineBreak(c); +#endif + lexSkipLookahead(); + return SEMICOLON; + } + else if (strchr("\n",c)) { + ++mime_lineNum; + /* consume all line separator(s) adjacent to each other */ + c = lexLookahead(); + while (strchr("\n",c)) { + lexSkipLookahead(); + c = lexLookahead(); + ++mime_lineNum; + } + DBG_(("db: LINESEP\n")); + return LINESEP; + } + else { + char *p = 0; + lexPushLookaheadc(c); + if (lexWithinMode(L_BASE64)) { + /* get each char and convert to bin on the fly... */ + p = lexGetDataFromBase64(); + yylval.str = p; + return STRING; + } + else if (lexWithinMode(L_QUOTED_PRINTABLE)) { + p = lexGetQuotedPrintable(); + } + else { +#ifdef _SUPPORT_LINE_FOLDING + p = lexGet1Value(); +#else + p = lexGetStrUntil(";\n"); +#endif + } + if (p) { + DBG_(("db: STRING: '%s'\n", p)); + yylval.str = p; + return STRING; + } + else return 0; + } + } + else { + /* normal mode */ + while (1) { + int c = lexGetc(); + switch(c) { + case ':': { + /* consume all line separator(s) adjacent to each other */ + /* ignoring linesep immediately after colon. */ +/* c = lexLookahead(); + while (strchr("\n",c)) { + lexSkipLookahead(); + c = lexLookahead(); + ++mime_lineNum; + }*/ + DBG_(("db: COLON\n")); + return COLON; + } + case ';': + DBG_(("db: SEMICOLON\n")); + return SEMICOLON; + case '=': + DBG_(("db: EQ\n")); + return EQ; + /* ignore whitespace in this mode */ + case '\t': + case ' ': continue; + case '\n': { + ++mime_lineNum; + continue; + } + case EOF: return 0; + break; + default: { + lexPushLookaheadc(c); + if (isalpha(c)) { + char *t = lexGetWord(); + yylval.str = t; + if (!stricmp(t, "begin")) { + return match_begin_end_name(0); + } + else if (!stricmp(t,"end")) { + return match_begin_end_name(1); + } + else { + DBG_(("db: ID '%s'\n", t)); + return ID; + } + } + else { + /* unknow token */ + return 0; + } + break; + } + } + } + } + return 0; + } + + +/***************************************************************************/ +/*** Public Functions ****/ +/***************************************************************************/ + +static VObject* Parse_MIMEHelper(void) + { + ObjStackTop = -1; + mime_numErrors = 0; + mime_lineNum = 1; + vObjList = 0; + curObj = 0; + + if (yyparse() != 0) { + finiLex(); + return 0; + } + + finiLex(); + return vObjList; + } + +/***************************************************************************/ +DLLEXPORT(VObject*) Parse_MIME(const char *input, unsigned long len) + { + initLex(input, len, 0); + return Parse_MIMEHelper(); + } + + +#if INCLUDEMFC + +DLLEXPORT(VObject*) Parse_MIME_FromFile(CFile *file) + { + unsigned long startPos; + VObject *result; + + initLex(0,-1,file); + startPos = file->GetPosition(); + if (!(result = Parse_MIMEHelper())) + file->Seek(startPos, CFile::begin); + return result; + } + +#else + +VObject* Parse_MIME_FromFile(FILE *file) + { + VObject *result; + long startPos; + + initLex(0,(unsigned long)-1,file); + startPos = ftell(file); + setCRLFmode( 0 ); /* lexLookahead() will detect CRLF/NL input */ + if (!(result = Parse_MIMEHelper())) { + fseek(file,startPos,SEEK_SET); + } + return result; + } + +DLLEXPORT(VObject*) Parse_MIME_FromFileName(char *fname) + { + FILE *fp = fopen(fname,"r"); + if (fp) { + VObject* o = Parse_MIME_FromFile(fp); + fclose(fp); + return o; + } + else { + char msg[17+256+14+1]; + sprintf(msg, "can't open file '%.256s' for reading\n", fname); + mime_error_(msg); + return 0; + } + } + +#endif + +/***************************************************************************/ +#if 0 +static void YYDebug(const char *s) +{ + Parse_Debug(s); +} +#endif + + +static MimeErrorHandler mimeErrorHandler; + +DLLEXPORT(void) registerMimeErrorHandler(MimeErrorHandler me) + { + mimeErrorHandler = me; + } + +static void mime_error(char *s) + { + char msg[256]; + if (mimeErrorHandler) { + sprintf(msg,"%s at line %d", s, mime_lineNum); + mimeErrorHandler(msg); + } + } + +static void mime_error_(char *s) + { + if (mimeErrorHandler) { + mimeErrorHandler(s); + } + } + + diff --git a/src/vcc.h b/src/vcc.h new file mode 100644 index 0000000..2725e8c --- /dev/null +++ b/src/vcc.h @@ -0,0 +1,83 @@ +/*************************************************************************** +(C) Copyright 1996 Apple Computer, Inc., AT&T Corp., International +Business Machines Corporation and Siemens Rolm Communications Inc. + +modified 2000 by Thomas Schulz: +$Id: vcc.h,v 1.2 2000/11/19 18:14:45 tsch Rel $ + +For purposes of this license notice, the term Licensors shall mean, +collectively, Apple Computer, Inc., AT&T Corp., International +Business Machines Corporation and Siemens Rolm Communications Inc. +The term Licensor shall mean any of the Licensors. + +Subject to acceptance of the following conditions, permission is hereby +granted by Licensors without the need for written agreement and without +license or royalty fees, to use, copy, modify and distribute this +software for any purpose. + +The above copyright notice and the following four paragraphs must be +reproduced in all copies of this software and any software including +this software. + +THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS AND NO LICENSOR SHALL HAVE +ANY OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS OR +MODIFICATIONS. + +IN NO EVENT SHALL ANY LICENSOR BE LIABLE TO ANY PARTY FOR DIRECT, +INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT +OF THE USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +EACH LICENSOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO ANY WARRANTY OF NONINFRINGEMENT OR THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. + +The software is provided with RESTRICTED RIGHTS. Use, duplication, or +disclosure by the government are subject to restrictions set forth in +DFARS 252.227-7013 or 48 CFR 52.227-19, as applicable. + +***************************************************************************/ + +#ifndef __VCC_H__ +#define __VCC_H__ 1 + +#include "vobject.h" + + +#if defined(__CPLUSPLUS__) || defined(__cplusplus) +extern "C" { +#endif + +typedef void (*MimeErrorHandler)(char *); + +extern DLLEXPORT(void) registerMimeErrorHandler(MimeErrorHandler); + +extern DLLEXPORT(VObject*) Parse_MIME(const char *input, unsigned long len); +extern DLLEXPORT(VObject*) Parse_MIME_FromFileName(char* fname); + + +/* NOTE regarding Parse_MIME_FromFile +The function above, Parse_MIME_FromFile, comes in two flavors, +neither of which is exported from the DLL. Each version takes +a CFile or FILE* as a parameter, neither of which can be +passed across a DLL interface (at least that is my experience). +If you are linking this code into your build directly then +you may find them a more convenient API that the other flavors +that take a file name. If you use them with the DLL LIB you +will get a link error. +*/ + + +#if INCLUDEMFC +extern VObject* Parse_MIME_FromFile(CFile *file); +#else +extern VObject* Parse_MIME_FromFile(FILE *file); +#endif + +#if defined(__CPLUSPLUS__) || defined(__cplusplus) +} +#endif + +#endif /* __VCC_H__ */ + diff --git a/src/vcc.y b/src/vcc.y new file mode 100644 index 0000000..de775b2 --- /dev/null +++ b/src/vcc.y @@ -0,0 +1,1249 @@ +%{ + +/*************************************************************************** +(C) Copyright 1996 Apple Computer, Inc., AT&T Corp., International +Business Machines Corporation and Siemens Rolm Communications Inc. + +modified 2000 by Thomas Schulz: +$Id: vcc.y,v 1.7 2001/02/10 03:09:24 tsch Rel $ + +For purposes of this license notice, the term Licensors shall mean, +collectively, Apple Computer, Inc., AT&T Corp., International +Business Machines Corporation and Siemens Rolm Communications Inc. +The term Licensor shall mean any of the Licensors. + +Subject to acceptance of the following conditions, permission is hereby +granted by Licensors without the need for written agreement and without +license or royalty fees, to use, copy, modify and distribute this +software for any purpose. + +The above copyright notice and the following four paragraphs must be +reproduced in all copies of this software and any software including +this software. + +THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS AND NO LICENSOR SHALL HAVE +ANY OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS OR +MODIFICATIONS. + +IN NO EVENT SHALL ANY LICENSOR BE LIABLE TO ANY PARTY FOR DIRECT, +INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT +OF THE USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +EACH LICENSOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO ANY WARRANTY OF NONINFRINGEMENT OR THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. + +The software is provided with RESTRICTED RIGHTS. Use, duplication, or +disclosure by the government are subject to restrictions set forth in +DFARS 252.227-7013 or 48 CFR 52.227-19, as applicable. + +***************************************************************************/ + +/* + * src: vcc.c + * doc: Parser for vCard and vCalendar. Note that this code is + * generated by a yacc parser generator. Generally it should not + * be edited by hand. The real source is vcc.y. The #line directives + * can be commented out here to make it easier to trace through + * in a debugger. However, if a bug is found it should + * be fixed in vcc.y and this file regenerated. + */ + + +/* debugging utilities */ +#if __DEBUG +#define DBG_(x) printf x +#else +#define DBG_(x) +#endif + +/**** External Functions ****/ + +/* assign local name to parser variables and functions so that + we can use more than one yacc based parser. +*/ + +#define yyparse mime_parse +#define yylex mime_lex +#define yyerror mime_error +#define yychar mime_char +/* #define p_yyval p_mime_val */ +#undef yyval +#define yyval mime_yyval +/* #define p_yylval p_mime_lval */ +#undef yylval +#define yylval mime_yylval +#define yydebug mime_debug +#define yynerrs mime_nerrs +#define yyerrflag mime_errflag +#define yyss mime_ss +#define yyssp mime_ssp +#define yyvs mime_vs +#define yyvsp mime_vsp +#define yylhs mime_lhs +#define yylen mime_len +#define yydefred mime_defred +#define yydgoto mime_dgoto +#define yysindex mime_sindex +#define yyrindex mime_rindex +#define yygindex mime_gindex +#define yytable mime_table +#define yycheck mime_check +#define yyname mime_name +#define yyrule mime_rule +#define YYPREFIX "mime_" + + +#ifndef _NO_LINE_FOLDING +#define _SUPPORT_LINE_FOLDING 1 +#endif + +/* undef below if compile with MFC */ +/* #define INCLUDEMFC 1 */ + +#if defined(WIN32) || defined(_WIN32) +#ifdef INCLUDEMFC +#include +#endif +#endif + +#include +#ifndef __MWERKS__ +#include +#endif +#include +#include +#include +#include "vcc.h" + +/**** Types, Constants ****/ + +#define YYDEBUG 0 /* 1 to compile in some debugging code */ +#define YYERROR_VERBOSE 1 /* verbose parse error reports */ +#define MAXTOKEN 256 /* maximum token (line) length */ +#define YYSTACKSIZE 50 /* ~unref ? */ +#define MAXLEVEL 10 /* max # of nested objects parseable */ + /* (includes outermost) */ + + +/**** Global Variables ****/ +int mime_lineNum, mime_numErrors; /* yyerror() can use these */ +static VObject* vObjList; +static VObject *curProp; +static VObject *curObj; +static VObject* ObjStack[MAXLEVEL]; +static int ObjStackTop; + + +/* A helpful utility for the rest of the app. */ +#if __CPLUSPLUS__ +extern "C" { +#endif + + extern void Parse_Debug(const char *s); + static void yyerror(char *s); + +#if __CPLUSPLUS__ + }; +#endif + +int yyparse(void); +static int yylex(void); + +enum LexMode { + L_NORMAL, + L_VCARD, + L_VCAL, + L_VEVENT, + L_VTODO, + L_VALUES, + L_BASE64, + L_QUOTED_PRINTABLE + }; + +/**** Private Forward Declarations ****/ +static int pushVObject(const char *prop); +static VObject* popVObject(void); +static char* lexGetDataFromBase64(void); +static void lexPopMode(int top); +static int lexWithinMode(enum LexMode mode); +static void lexPushMode(enum LexMode mode); +static void enterProps(const char *s); +static void enterAttr(const char *s1, const char *s2); +static void appendValue(const char *value); +static void mime_error_(char *s); + +%} + +/***************************************************************************/ +/*** The grammar ****/ +/***************************************************************************/ + +%union { + char *str; + VObject *vobj; + } + +%token + EQ COLON DOT SEMICOLON SPACE HTAB LINESEP NEWLINE + BEGIN_VCARD END_VCARD BEGIN_VCAL END_VCAL + BEGIN_VEVENT END_VEVENT BEGIN_VTODO END_VTODO + BEGIN_VMEMO END_VMEMO + ID + +/* + * NEWLINE is the token that would occur outside a vCard, + * while LINESEP is the token that would occur inside a vCard. + */ + +%token + STRING ID + +%type name value + +%type vcard vcal vmemo vobject + +%start mime + +%expect 2 /* 2 shift/reduce conflicts for "items" */ + +%% + + +mime: vobjects + ; + +vobjects: vobject + { addList(&vObjList, $1); curObj = 0; } + vobjects + | vobject + { addList(&vObjList, $1); curObj = 0; } + ; + +vobject: vcard + | vcal + | vmemo + ; + +vcard: + BEGIN_VCARD + { + lexPushMode(L_VCARD); + if (!pushVObject(VCCardProp)) YYERROR; + } + items END_VCARD + { + lexPopMode(0); + $$ = popVObject(); + } + | BEGIN_VCARD + { + lexPushMode(L_VCARD); + if (!pushVObject(VCCardProp)) YYERROR; + } + END_VCARD + { + lexPopMode(0); + $$ = popVObject(); + } + ; + +items: item items + | item + ; + +item: prop COLON + { + lexPushMode(L_VALUES); + } + values LINESEP + { + if (lexWithinMode(L_BASE64) || lexWithinMode(L_QUOTED_PRINTABLE)) + lexPopMode(0); + lexPopMode(0); + } + | error + ; + +prop: name + { + enterProps($1); + } + attr_params + | name + { + enterProps($1); + } + ; + +attr_params: attr_param attr_params + | attr_param + ; + +attr_param: SEMICOLON attr + ; + +attr: name + { + enterAttr($1,0); + } + | name EQ name + { + enterAttr($1,$3); + + } + ; + +name: ID + ; + +values: value SEMICOLON { appendValue($1); } values + | value + { appendValue($1); } + ; + +value: STRING + | { $$ = 0; } + ; + +vmemo: + BEGIN_VMEMO + { if (!pushVObject(VCMemoProp)) YYERROR; } + items + END_VMEMO + { $$ = popVObject(); } + | BEGIN_VMEMO + { if (!pushVObject(VCMemoProp)) YYERROR; } + END_VMEMO + { $$ = popVObject(); } + ; + +vcal: + BEGIN_VCAL + { if (!pushVObject(VCCalProp)) YYERROR; } + calitems + END_VCAL + { $$ = popVObject(); } + | BEGIN_VCAL + { if (!pushVObject(VCCalProp)) YYERROR; } + END_VCAL + { $$ = popVObject(); } + ; + +calitems: calitem calitems + | calitem + ; + +calitem: + eventitem + | todoitem + | items + ; + +eventitem: + BEGIN_VEVENT + { + lexPushMode(L_VEVENT); + if (!pushVObject(VCEventProp)) YYERROR; + } + items + END_VEVENT + { + lexPopMode(0); + popVObject(); + } + | BEGIN_VEVENT + { + lexPushMode(L_VEVENT); + if (!pushVObject(VCEventProp)) YYERROR; + } + END_VEVENT + { + lexPopMode(0); + popVObject(); + } + ; + +todoitem: + BEGIN_VTODO + { + lexPushMode(L_VTODO); + if (!pushVObject(VCTodoProp)) YYERROR; + } + items + END_VTODO + { + lexPopMode(0); + popVObject(); + } + | BEGIN_VTODO + { + lexPushMode(L_VTODO); + if (!pushVObject(VCTodoProp)) YYERROR; + } + END_VTODO + { + lexPopMode(0); + popVObject(); + } + ; + +%% +/***************************************************************************/ +static int pushVObject(const char *prop) + { + VObject *newObj; + if (ObjStackTop == MAXLEVEL) + return FALSE; + + ObjStack[++ObjStackTop] = curObj; + + if (curObj) { + newObj = addProp(curObj,prop); + curObj = newObj; + } + else + curObj = newVObject(prop); + + return TRUE; + } + + +/***************************************************************************/ +/* This pops the recently built vCard off the stack and returns it. */ +static VObject* popVObject(void) + { + VObject *oldObj; + if (ObjStackTop < 0) { + yyerror("pop on empty Object Stack\n"); + return 0; + } + oldObj = curObj; + curObj = ObjStack[ObjStackTop--]; + + return oldObj; + } + +static void appendValue(const char *value) +{ + char *p1, *p2; + wchar_t *p3; + int i; + + if (fieldedProp && *fieldedProp) { + if (value) { + addPropValue(curProp, *fieldedProp, value); + } + /* else this field is empty, advance to next field */ + fieldedProp++; + } else { + if (value) { + if (vObjectUStringZValue(curProp)) { + p1 = fakeCString(vObjectUStringZValue(curProp)); + p2 = malloc(sizeof(char *) * (strlen(p1)+strlen(value)+1)); + strcpy(p2, p1); + deleteStr(p1); + + i = strlen(p2); + p2[i] = ';'; + p2[i+1] = '\0'; + p2 = strcat(p2, value); + p3 = (wchar_t *) vObjectUStringZValue(curProp); + free(p3); + setVObjectUStringZValue_(curProp,fakeUnicode(p2,0)); + deleteStr(p2); + } else { + setVObjectUStringZValue_(curProp,fakeUnicode(value,0)); + } + } + } + deleteStr(value); +} + +static void enterProps(const char *s) + { + curProp = addGroup(curObj,s); + deleteStr(s); + } + +static void enterAttr(const char *s1, const char *s2) + { + const char *p1, *p2; + p1 = lookupProp_(s1); + if (s2) { + VObject *a; + p2 = lookupProp_(s2); + a = addProp(curProp,p1); + setVObjectStringZValue(a,p2); + } + else { + p2 = NULL; + addProp(curProp,p1); + } + if (stricmp(p1,VCBase64Prop) == 0 || (s2 && stricmp(p2,VCBase64Prop)==0)) + lexPushMode(L_BASE64); + else if (stricmp(p1,VCQuotedPrintableProp) == 0 + || (s2 && stricmp(p2,VCQuotedPrintableProp)==0)) + lexPushMode(L_QUOTED_PRINTABLE); + deleteStr(s1); deleteStr(s2); + } + + +#define MAX_LEX_LOOKAHEAD_0 32 +#define MAX_LEX_LOOKAHEAD 64 +#define MAX_LEX_MODE_STACK_SIZE 10 +#define LEXMODE() (lexBuf.lexModeStack[lexBuf.lexModeStackTop]) + +struct LexBuf { + /* input */ +#ifdef INCLUDEMFC + CFile *inputFile; +#else + FILE *inputFile; +#endif + char *inputString; + unsigned long curPos; + unsigned long inputLen; + /* lookahead buffer */ + /* -- lookahead buffer is short instead of char so that EOF + / can be represented correctly. + */ + unsigned long len; + short buf[MAX_LEX_LOOKAHEAD]; + unsigned long getPtr; + /* context stack */ + unsigned long lexModeStackTop; + enum LexMode lexModeStack[MAX_LEX_MODE_STACK_SIZE]; + /* token buffer */ + unsigned long maxToken; + char *strs; + unsigned long strsLen; + } lexBuf; + +static void lexPushMode(enum LexMode mode) + { + if (lexBuf.lexModeStackTop == (MAX_LEX_MODE_STACK_SIZE-1)) + yyerror("lexical context stack overflow"); + else { + lexBuf.lexModeStack[++lexBuf.lexModeStackTop] = mode; + } + } + +static void lexPopMode(int top) + { + /* special case of pop for ease of error recovery -- this + version will never underflow */ + if (top) + lexBuf.lexModeStackTop = 0; + else + if (lexBuf.lexModeStackTop > 0) lexBuf.lexModeStackTop--; + } + +static int lexWithinMode(enum LexMode mode) { + unsigned long i; + for (i=0;iRead(&result, 1) == 1 ? result : EOF; +#else + return fgetc(lexBuf.inputFile); +#endif + } + } + +static int lexGeta(void) + { + ++lexBuf.len; + return (lexBuf.buf[lexBuf.getPtr] = lexGetc_()); + } + +static int lexGeta_(int i) + { + ++lexBuf.len; + return (lexBuf.buf[(lexBuf.getPtr+i)%MAX_LEX_LOOKAHEAD] = lexGetc_()); + } + +static void lexSkipLookahead(void) { + if (lexBuf.len > 0 && lexBuf.buf[lexBuf.getPtr]!=EOF) { + /* don't skip EOF. */ + lexBuf.getPtr = (lexBuf.getPtr + 1) % MAX_LEX_LOOKAHEAD; + lexBuf.len--; + } + } + +static int lexLookahead(void) { + int c = (lexBuf.len)? + lexBuf.buf[lexBuf.getPtr]: + lexGeta(); + /* do the \r\n -> \n or \r -> \n translation here */ + if (c == '\r') { + int a = (lexBuf.len>1)? + lexBuf.buf[(lexBuf.getPtr+1)%MAX_LEX_LOOKAHEAD]: + lexGeta_(1); + if (a == '\n') { + lexSkipLookahead(); + } + lexBuf.buf[lexBuf.getPtr] = c = '\n'; + setCRLFmode( 1 ); /* note CRLF found on input */ + } + else if (c == '\n') { + int a = (lexBuf.len>1)? + lexBuf.buf[lexBuf.getPtr+1]: + lexGeta_(1); + if (a == '\r') { + lexSkipLookahead(); + } + lexBuf.buf[lexBuf.getPtr] = '\n'; + } + return c; + } + +static int lexGetc(void) { + int c = lexLookahead(); + if (lexBuf.len > 0 && lexBuf.buf[lexBuf.getPtr]!=EOF) { + /* EOF will remain in lookahead buffer */ + lexBuf.getPtr = (lexBuf.getPtr + 1) % MAX_LEX_LOOKAHEAD; + lexBuf.len--; + } + return c; + } + +static void lexSkipLookaheadWord(void) { + if (lexBuf.strsLen <= lexBuf.len) { + lexBuf.len -= lexBuf.strsLen; + lexBuf.getPtr = (lexBuf.getPtr + lexBuf.strsLen) % MAX_LEX_LOOKAHEAD; + } + } + +static void lexClearToken(void) + { + lexBuf.strsLen = 0; + } + +static void lexAppendc(int c) + { + lexBuf.strs[lexBuf.strsLen] = c; + /* append up to zero termination */ + if (c == 0) return; + lexBuf.strsLen++; + if (lexBuf.strsLen >= lexBuf.maxToken) { + /* double the token string size */ + lexBuf.maxToken <<= 1; + lexBuf.strs = (char*) realloc(lexBuf.strs,(size_t)lexBuf.maxToken); + } + } + +static char* lexStr(void) { + return dupStr(lexBuf.strs,(size_t)lexBuf.strsLen+1); + } + +static void lexSkipWhite(void) { + int c = lexLookahead(); + while (c == ' ' || c == '\t') { + lexSkipLookahead(); + c = lexLookahead(); + } + } + +static char* lexGetWord(void) { + int c; + lexSkipWhite(); + lexClearToken(); + c = lexLookahead(); + while (c != EOF && !strchr("\t\n ;:=",c)) { + lexAppendc(c); + lexSkipLookahead(); + c = lexLookahead(); + } + lexAppendc(0); + return lexStr(); + } + +static void lexPushLookaheadc(int c) { + int putptr; + /* can't putback EOF, because it never leaves lookahead buffer */ + if (c == EOF) return; + putptr = (int)lexBuf.getPtr - 1; + if (putptr < 0) putptr += MAX_LEX_LOOKAHEAD; + lexBuf.getPtr = putptr; + lexBuf.buf[putptr] = c; + lexBuf.len += 1; + } + +static char* lexLookaheadWord(void) { + /* this function can lookahead word with max size of MAX_LEX_LOOKAHEAD_0 + / and thing bigger than that will stop the lookahead and return 0; + / leading white spaces are not recoverable. + */ + int c; + int len = 0; + int curgetptr = 0; + lexSkipWhite(); + lexClearToken(); + curgetptr = (int)lexBuf.getPtr; /* remember! */ + while (len < (MAX_LEX_LOOKAHEAD_0)) { + c = lexGetc(); + len++; + if (c == EOF || strchr("\t\n ;:=", c)) { + lexAppendc(0); + /* restore lookahead buf. */ + lexBuf.len += len; + lexBuf.getPtr = curgetptr; + return lexStr(); + } + else + lexAppendc(c); + } + lexBuf.len += len; /* char that has been moved to lookahead buffer */ + lexBuf.getPtr = curgetptr; + return 0; + } + +#ifdef _SUPPORT_LINE_FOLDING +static void handleMoreRFC822LineBreak(int c) { + /* suport RFC 822 line break in cases like + * ADR: foo; + * morefoo; + * more foo; + */ + if (c == ';') { + int a; + lexSkipLookahead(); + /* skip white spaces */ + a = lexLookahead(); + while (a == ' ' || a == '\t') { + lexSkipLookahead(); + a = lexLookahead(); + } + if (a == '\n') { + lexSkipLookahead(); + a = lexLookahead(); + if (a == ' ' || a == '\t') { + /* continuation, throw away all the \n and spaces read so + * far + */ + lexSkipWhite(); + lexPushLookaheadc(';'); + } + else { + lexPushLookaheadc('\n'); + lexPushLookaheadc(';'); + } + } + else { + lexPushLookaheadc(';'); + } + } + } + +static char* lexGet1Value(void) { + int c; + lexSkipWhite(); + c = lexLookahead(); + lexClearToken(); + while (c != EOF && c != ';') { + if (c == '\n') { + int a; + lexSkipLookahead(); + a = lexLookahead(); + if (a == ' ' || a == '\t') { + lexAppendc(' '); + lexSkipLookahead(); + } + else { + lexPushLookaheadc('\n'); + break; + } + } + else { + lexAppendc(c); + lexSkipLookahead(); + } + c = lexLookahead(); + } + lexAppendc(0); + handleMoreRFC822LineBreak(c); + return c==EOF?0:lexStr(); + } +#endif + +#ifndef _SUPPORT_LINE_FOLDING +static char* lexGetStrUntil(char *termset) { + int c = lexLookahead(); + lexClearToken(); + while (c != EOF && !strchr(termset,c)) { + lexAppendc(c); + lexSkipLookahead(); + c = lexLookahead(); + } + lexAppendc(0); + return c==EOF?0:lexStr(); + } +#endif + +static int match_begin_name(int end) { + char *n = lexLookaheadWord(); + int token = ID; + if (n) { + if (!stricmp(n,"vcard")) token = end?END_VCARD:BEGIN_VCARD; + else if (!stricmp(n,"vcalendar")) token = end?END_VCAL:BEGIN_VCAL; + else if (!stricmp(n,"vevent")) token = end?END_VEVENT:BEGIN_VEVENT; + else if (!stricmp(n,"vtodo")) token = end?END_VTODO:BEGIN_VTODO; + else if (!stricmp(n,"vmemo")) token = end?END_VMEMO:BEGIN_VMEMO; + deleteStr(n); + return token; + } + return 0; + } + + +#ifdef INCLUDEMFC +void initLex(const char *inputstring, unsigned long inputlen, CFile *inputfile) +#else +static void initLex(const char *inputstring, unsigned long inputlen, FILE *inputfile) +#endif + { + /* initialize lex mode stack */ + lexBuf.lexModeStack[lexBuf.lexModeStackTop=0] = L_NORMAL; + + /* iniatialize lex buffer. */ + lexBuf.inputString = (char*) inputstring; + lexBuf.inputLen = inputlen; + lexBuf.curPos = 0; + lexBuf.inputFile = inputfile; + + lexBuf.len = 0; + lexBuf.getPtr = 0; + + lexBuf.maxToken = MAXTOKEN; + lexBuf.strs = (char*)malloc(MAXTOKEN); + lexBuf.strsLen = 0; + + } + +static void finiLex(void) { + free(lexBuf.strs); + } + + +/***************************************************************************/ +/* This parses and converts the base64 format for binary encoding into + * a decoded buffer (allocated with new). See RFC 1521. + */ +static char * lexGetDataFromBase64(void) + { + unsigned long bytesLen = 0, bytesMax = 0; + int quadIx = 0, pad = 0; + unsigned long trip = 0; + unsigned char b; + int c; + unsigned char *bytes = NULL; + unsigned char *oldBytes = NULL; + + DBG_(("db: lexGetDataFromBase64\n")); + while (1) { + c = lexGetc(); + if (c == '\n') { + ++mime_lineNum; + if (lexLookahead() == '\n') { + /* a '\n' character by itself means end of data */ + break; + } + else continue; /* ignore '\n' */ + } + else { + if ((c >= 'A') && (c <= 'Z')) + b = (unsigned char)(c - 'A'); + else if ((c >= 'a') && (c <= 'z')) + b = (unsigned char)(c - 'a') + 26; + else if ((c >= '0') && (c <= '9')) + b = (unsigned char)(c - '0') + 52; + else if (c == '+') + b = 62; + else if (c == '/') + b = 63; + else if (c == '=') { + b = 0; + pad++; + } else if ((c == ' ') || (c == '\t')) { + continue; + } else { /* error condition */ + if (bytes) free(bytes); + else if (oldBytes) free(oldBytes); + /* error recovery: skip until 2 adjacent newlines. */ + DBG_(("db: invalid character 0x%x '%c'\n", c,c)); + if (c != EOF) { + c = lexGetc(); + while (c != EOF) { + if (c == '\n' && lexLookahead() == '\n') { + ++mime_lineNum; + break; + } + c = lexGetc(); + } + } + return NULL; + } + trip = (trip << 6) | b; + if (++quadIx == 4) { + unsigned char outBytes[3]; + int numOut; + int i; + for (i = 0; i < 3; i++) { + outBytes[2-i] = (unsigned char)(trip & 0xFF); + trip >>= 8; + } + numOut = 3 - pad; + if (bytesLen + numOut > bytesMax) { + if (!bytes) { + bytesMax = 1024; + bytes = (unsigned char*)malloc((size_t)bytesMax); + } + else { + bytesMax <<= 2; + oldBytes = bytes; + bytes = (unsigned char*)realloc(bytes,(size_t)bytesMax); + } + if (bytes == 0) { + mime_error("out of memory while processing BASE64 data\n"); + } + } + if (bytes) { + memcpy(bytes + bytesLen, outBytes, numOut); + bytesLen += numOut; + } + trip = 0; + quadIx = 0; + } + } + } /* while */ + DBG_(("db: bytesLen = %d\n", bytesLen)); + /* kludge: all this won't be necessary if we have tree form + representation */ + if (bytes) { + setValueWithSize(curProp,bytes,(unsigned int)bytesLen); + free(bytes); + } + else if (oldBytes) { + setValueWithSize(curProp,oldBytes,(unsigned int)bytesLen); + free(oldBytes); + } + return 0; + } + +static int match_begin_end_name(int end) { + int token; + lexSkipWhite(); + if (lexLookahead() != ':') return ID; + lexSkipLookahead(); + lexSkipWhite(); + token = match_begin_name(end); + if (token == ID) { + lexPushLookaheadc(':'); + DBG_(("db: ID '%s'\n", yylval.str)); + return ID; + } + else if (token != 0) { + lexSkipLookaheadWord(); + deleteStr(yylval.str); + DBG_(("db: begin/end %d\n", token)); + return token; + } + return 0; + } + +static char* lexGetQuotedPrintable(void) + { + char cur; + + lexClearToken(); + do { + cur = lexGetc(); + switch (cur) { + case '=': { + int c = 0; + int next[2]; + int i; + for (i = 0; i < 2; i++) { + next[i] = lexGetc(); + if (next[i] >= '0' && next[i] <= '9') + c = c * 16 + next[i] - '0'; + else if (next[i] >= 'A' && next[i] <= 'F') + c = c * 16 + next[i] - 'A' + 10; + else + break; + } + if (i == 0) { + /* single '=' follow by LINESEP is continuation sign? */ + if (next[0] == '\n') { + ++mime_lineNum; + } + else { + lexPushLookaheadc('='); + goto EndString; + } + } + else if (i == 1) { + lexPushLookaheadc(next[1]); + lexPushLookaheadc(next[0]); + lexAppendc('='); + } else { + lexAppendc(c); + } + break; + } /* '=' */ + case '\n': { + lexPushLookaheadc('\n'); + goto EndString; + } + case (char)EOF: + break; + default: + lexAppendc(cur); + break; + } /* switch */ + } while (cur != (char)EOF); + +EndString: + lexAppendc(0); + return lexStr(); + } /* LexQuotedPrintable */ + +static int yylex(void) { + int lexmode = LEXMODE(); + + if (lexmode == L_VALUES) { + int c = lexGetc(); + if (c == ';') { + DBG_(("db: SEMICOLON\n")); + lexPushLookaheadc(c); +#ifdef _SUPPORT_LINE_FOLDING + handleMoreRFC822LineBreak(c); +#endif + lexSkipLookahead(); + return SEMICOLON; + } + else if (strchr("\n",c)) { + ++mime_lineNum; + /* consume all line separator(s) adjacent to each other */ + c = lexLookahead(); + while (strchr("\n",c)) { + lexSkipLookahead(); + c = lexLookahead(); + ++mime_lineNum; + } + DBG_(("db: LINESEP\n")); + return LINESEP; + } + else { + char *p = 0; + lexPushLookaheadc(c); + if (lexWithinMode(L_BASE64)) { + /* get each char and convert to bin on the fly... */ + p = lexGetDataFromBase64(); + yylval.str = p; + return STRING; + } + else if (lexWithinMode(L_QUOTED_PRINTABLE)) { + p = lexGetQuotedPrintable(); + } + else { +#ifdef _SUPPORT_LINE_FOLDING + p = lexGet1Value(); +#else + p = lexGetStrUntil(";\n"); +#endif + } + if (p) { + DBG_(("db: STRING: '%s'\n", p)); + yylval.str = p; + return STRING; + } + else return 0; + } + } + else { + /* normal mode */ + while (1) { + int c = lexGetc(); + switch(c) { + case ':': { + /* consume all line separator(s) adjacent to each other */ + /* ignoring linesep immediately after colon. */ +/* c = lexLookahead(); + while (strchr("\n",c)) { + lexSkipLookahead(); + c = lexLookahead(); + ++mime_lineNum; + }*/ + DBG_(("db: COLON\n")); + return COLON; + } + case ';': + DBG_(("db: SEMICOLON\n")); + return SEMICOLON; + case '=': + DBG_(("db: EQ\n")); + return EQ; + /* ignore whitespace in this mode */ + case '\t': + case ' ': continue; + case '\n': { + ++mime_lineNum; + continue; + } + case EOF: return 0; + break; + default: { + lexPushLookaheadc(c); + if (isalpha(c)) { + char *t = lexGetWord(); + yylval.str = t; + if (!stricmp(t, "begin")) { + return match_begin_end_name(0); + } + else if (!stricmp(t,"end")) { + return match_begin_end_name(1); + } + else { + DBG_(("db: ID '%s'\n", t)); + return ID; + } + } + else { + /* unknow token */ + return 0; + } + break; + } + } + } + } + return 0; + } + + +/***************************************************************************/ +/*** Public Functions ****/ +/***************************************************************************/ + +static VObject* Parse_MIMEHelper(void) + { + ObjStackTop = -1; + mime_numErrors = 0; + mime_lineNum = 1; + vObjList = 0; + curObj = 0; + + if (yyparse() != 0) { + finiLex(); + return 0; + } + + finiLex(); + return vObjList; + } + +/***************************************************************************/ +DLLEXPORT(VObject*) Parse_MIME(const char *input, unsigned long len) + { + initLex(input, len, 0); + return Parse_MIMEHelper(); + } + + +#if INCLUDEMFC + +DLLEXPORT(VObject*) Parse_MIME_FromFile(CFile *file) + { + unsigned long startPos; + VObject *result; + + initLex(0,-1,file); + startPos = file->GetPosition(); + if (!(result = Parse_MIMEHelper())) + file->Seek(startPos, CFile::begin); + return result; + } + +#else + +VObject* Parse_MIME_FromFile(FILE *file) + { + VObject *result; + long startPos; + + initLex(0,(unsigned long)-1,file); + startPos = ftell(file); + setCRLFmode( 0 ); /* lexLookahead() will detect CRLF/NL input */ + if (!(result = Parse_MIMEHelper())) { + fseek(file,startPos,SEEK_SET); + } + return result; + } + +DLLEXPORT(VObject*) Parse_MIME_FromFileName(char *fname) + { + FILE *fp = fopen(fname,"r"); + if (fp) { + VObject* o = Parse_MIME_FromFile(fp); + fclose(fp); + return o; + } + else { + char msg[17+256+14+1]; + sprintf(msg, "can't open file '%.256s' for reading\n", fname); + mime_error_(msg); + return 0; + } + } + +#endif + +/***************************************************************************/ +#if 0 +static void YYDebug(const char *s) +{ + Parse_Debug(s); +} +#endif + + +static MimeErrorHandler mimeErrorHandler; + +DLLEXPORT(void) registerMimeErrorHandler(MimeErrorHandler me) + { + mimeErrorHandler = me; + } + +static void mime_error(char *s) + { + char msg[256]; + if (mimeErrorHandler) { + sprintf(msg,"%s at line %d", s, mime_lineNum); + mimeErrorHandler(msg); + } + } + +static void mime_error_(char *s) + { + if (mimeErrorHandler) { + mimeErrorHandler(s); + } + } + + diff --git a/src/vcutil.c b/src/vcutil.c new file mode 100644 index 0000000..8105831 --- /dev/null +++ b/src/vcutil.c @@ -0,0 +1,762 @@ +/************************************************************************ +* Copyright (C) 2000 Thomas Schulz * +* */ + static char rcsid[] = + "$Id: vcutil.c,v 1.44 2000/12/28 02:26:31 tsch Rel $"; /* +* * +* IC35 synchronize data import/export: vCard,vCalendar utilities * +* * +************************************************************************* +* * +* ??? is "fixme" mark: sections of code needing fixes * +* * +* vca_type determine vCard,vCal record type * +* dupSubst substitute in string, dupStr()'d result * +* dupSubstNL translate NL to CRLF, dupStr()'d result * +* dupSubstCRLF translate CRLF to NL, dupStr()'d result * +* utilities for IC35 to/from vCard,vCal * +* clr_vobjdirty clear vCard,vCal record modified status +* SetModtimeIfdirty modified date+time to vCard,vCal record +* _ChangeString set property string, note if changed +* SetProp +* SetString +* StringValue +* SetLong set property long integer value +* LongValue +* _NoteId NOTE/DESCRIPTION for vCard/vCal +* _NotePropsLen +* SetNotes +* NotesValue +* SetNoteProp +* NotePropValue +* _is_stdCategory standard categories Personal,Business.. +* SetCategory +* CategoryValue +* SetTel set telephone HOME/WORK/CELL/FAX +* SetEmail set Email1,Email2 +* utilities for date+time conversion * +* isodtstr_to_unixtime +* isodtstr_to_ymd_or_today +* isodtime_to_unixtime +* isodtime_to_ymd +* isodtime_to_ymd_or_today +* isodtime_to_hms_or_now +* * +************************************************************************/ + +#include /* sprintf(), .. */ +#include /* atol() */ +#include /* strcpy(), .. */ +#include /* isprint(), .. */ +#include /* size_t, .. */ +#include /* struct tm, time() .. */ + +#include "util.h" /* bool */ +#include "dataio.h" /* newic35dt() */ +#include "vcc.h" /* VObject, .. */ +#include "vcutil.h" +NOTUSED(rcsid); + + +/* map vCard,vCal record name to numeric type +* ------------------------------------------ +*/ +int +vca_type( VObject * vobj ) +{ + const char * name; + + if ( vobj == NULL ) + return 0; + name = vObjectName( vobj ); + if ( strcasecmp( name, VCCardProp ) == 0 ) return VCARD; + if ( strcasecmp( name, VCMemoProp ) == 0 ) return VMEMO; + if ( strcasecmp( name, VCCalProp ) == 0 ) return VCAL; + if ( strcasecmp( name, VCEventProp ) == 0 ) return VEVENT; + if ( strcasecmp( name, VCTodoProp ) == 0 ) return VTODO; + return 0; +} + +/* string substitutions +* -------------------- +*/ + /* substitutes 'new' for sub-string 'old' in 'str' */ +char * /* and returns dupStr()'sd resulting string */ +dupSubst( char * str, char * old, char * new ) +{ /* substitutes 'new' for sub-string 'old' in 'str' */ + char *match, *nstr; + char *pdst, *psrc; + + if ( str == NULL ) + return NULL; /* marginal .. */ + if ( ! (old && *old && new ) /* .. parameters */ + || (match = strstr( str, old )) == NULL ) /* or no match */ + return dupStr( str, 0 ); + nstr = dupStr( "", strlen(str) + strlen(new) - strlen(old) ); + pdst = nstr; psrc = str; + while ( *psrc ) + if ( psrc == match ) { + strcpy( pdst, new ); + psrc += strlen( old ); + pdst += strlen( new ); + } else + *pdst++ = *psrc++; + return nstr; +} +char * /* substitute vCard,vCal NewLine with IC35 CRLF */ +dupSubstNL( char * src ) +{ + char *psrc, *dst, *pdst; + size_t len; + + len = strlen( src ); + for ( psrc = src; *psrc; ++psrc ) + if ( *psrc == '\n' ) + ++len; + pdst = dst = dupStr( "", len ); + psrc = src, pdst = dst; + while ( *psrc ) { + if ( *psrc == '\n' + && !( psrc > src && *(psrc-1) == '\r' ) ) + *pdst++ = '\r'; + *pdst++ = *psrc++; + } + *pdst = '\0'; + return dst; +} +char * /* substitute IC35 CRLF with vCard,vCal NewLine */ +dupSubstCRLF( char * src ) +{ + + char *dst, *pdst; + + dst = pdst = dupStr( src, 0 ); + while ( *src ) + if ( (*pdst = *src++) != '\r' && *src != '\n' ) + ++pdst; + *pdst = '\0'; + return dst; +} + + +/* ============================================ */ +/* utilities for IC35 to/from vCard,vCal */ +/* ============================================ */ + +static bool _vobj_dirty; /* VObject changed in ic35xxx_to_vyyy() */ +static char ic35fld[511+1]; /* field buffer used by SetXxx() */ + +/* clear vCard,vCal modified status +* -------------------------------- +*/ +void +clr_vobjdirty( void ) +{ + _vobj_dirty = FALSE; +} + +/* revised date+time for vCard,vCal records +* ---------------------------------------- +*/ +static char * +_iso_ic35dt( const char * format ) +{ + time_t ic35dt; + struct tm * ptm; + static char isodtime[24]; + + ic35dt = newic35dt(); + ptm = localtime( &ic35dt ); + strftime( isodtime, sizeof(isodtime), format, ptm ); + return isodtime; +} +void +SetModtimeIfdirty( VObject * vobj ) +{ + if ( ! _vobj_dirty ) + return; + switch ( vca_type( vobj ) ) { + case VCARD: /* yyyy-mm-ddThh:mm:ss for VCARRD */ + SetString( vobj, VCLastRevisedProp, _iso_ic35dt("%Y-%m-%dT%H:%M:%S") ); + break; + case VEVENT: + case VTODO: /* also create-time for VEVENT,VTODO */ + if ( ! isAPropertyOf( vobj, VCDCreatedProp ) ) + SetString( vobj, VCDCreatedProp, _iso_ic35dt("%Y%m%dT%H%M%S") ); + /* fall through */ + case VMEMO: /* yyyymmddThhmmss for VEVENT,VTODO,memo */ + SetString( vobj, VCLastModifiedProp, _iso_ic35dt("%Y%m%dT%H%M%S") ); + break; + } +} + +/* set,get property string value +* ----------------------------- +* handle VObject value types StringZ, UStringZ, Integer, Long +* determine need for QUOTED-PRINTABLE and CHARSET +* note if property / string changed in _vobj_dirty +*/ +static void /* set property string, check and note if changed */ +_ChangeString( VObject * vobj, char * str ) +{ + const char * oldstr; + + switch ( vObjectValueType( vobj ) ) { + case VCVT_STRINGZ: + oldstr = dupStr( vObjectStringZValue( vobj ), 0 ); + break; + case VCVT_USTRINGZ: + oldstr = fakeCString( vObjectUStringZValue( vobj ) ); + break; + default: + oldstr = NULL; + } + if ( oldstr == NULL || strcmp( oldstr, str ) != 0 ) { + _vobj_dirty = TRUE; + setVObjectStringZValue( vobj, str ); + } + deleteStr( oldstr ); +} +VObject * /* add property if not yet there */ +SetProp( VObject * vobj, const char * id ) +{ + VObject * vprop; + + if ( (vprop = isAPropertyOf( vobj, id )) ) + return vprop; + _vobj_dirty = TRUE; + return addProp( vobj, id ); +} +void /* delete property */ +DelProp( VObject * vobj, const char * id ) +{ + VObject * vprop; + + if ( vobj && id && *id + && (vprop = isAPropertyOf( vobj, id )) ) { + vprop = delProp( vobj, vprop ); + cleanVObject( vprop ); + _vobj_dirty = TRUE; + } +} +VObject * /* set string, check if need QUOTED-PRINTABLE or CHARSET */ +SetString( VObject * vobj, const char * id, char * str ) +{ + VObject * vprop; + char * pstr; + bool prop_charset; + bool prop_quoted; + VObject * vpropchars; + + if ( !( vobj && id && *id ) ) + return NULL; + if ( !( str && *str ) ) { + DelProp( vobj, id ); + return NULL; + } + if ( (vprop = isAPropertyOf( vobj, id )) == NULL ) + vprop = addProp( vobj, id ); + prop_quoted = prop_charset = FALSE; + for ( pstr = str; *pstr; ++pstr ) { + if ( *pstr & 0x80 ) + prop_charset = TRUE; + if ( ! isprint( *pstr ) ) + prop_quoted = TRUE; + } + if ( prop_quoted || prop_charset ) { + /* + * single-field property like VCARD:FN needs charset,quoted property + * attached to the property 'vprop' + * multi-field sub-property like VCARD:ADR:VCCityProp needs charset + * prop attached to the toplevel property 'vobj' + * multi-field property must not use QUOTED-PRINTABLE due to problem + * with vcc.y parser. fixing the parser would not help, because other + * programs using the vCard,vCal output do not have the fixed parser. + */ + switch ( vca_type( vobj ) ) { + case VCARD: + case VMEMO: + case VEVENT: + case VTODO: + vpropchars = vprop; /* single-field property */ + break; + default: + vpropchars = vobj; /* multi-field sub-property */ + } + if ( prop_quoted /* QUOTED-PRINTABLE must not */ + && vpropchars != vobj ) /* be used with multi-field */ + SetProp( vpropchars, VCQuotedPrintableProp ); + if ( prop_charset ) + SetString( vpropchars, VCCharacterSetProp, "ISO-8859-1" ); + } + _ChangeString( vprop, str ); + return vprop; +} +char * /* dupStr()'d property's string value or NULL */ +dupStringValue( VObject * vobj, const char * id ) +{ + VObject * vprop; + char vtext[1+10+1]; + + if ( vobj == NULL ) + return NULL; + if ( id && *id ) { + if ( (vprop = isAPropertyOf( vobj, id )) == NULL ) + return NULL; + } else + vprop = vobj; + switch ( vObjectValueType( vprop ) ) { + case VCVT_STRINGZ: + return dupStr( vObjectStringZValue( vprop ), 0 ); + case VCVT_USTRINGZ: + return fakeCString( vObjectUStringZValue( vprop ) ); + case VCVT_UINT: + sprintf( vtext, "%+010d", vObjectIntegerValue( vprop ) ); + return dupStr( vtext, 0 ); + case VCVT_ULONG: + sprintf( vtext, "%+010ld", vObjectLongValue( vprop ) ); + return dupStr( vtext, 0 ); + default: + return NULL; + } +} +char * /* property's string value or "" */ +StringValue( VObject * vobj, const char * id ) +{ + char * strval; + + if ( (strval = dupStringValue( vobj, id )) == NULL ) + return ""; + strncat( strcpy( ic35fld, "" ), strval, sizeof(ic35fld)-1 ); + deleteStr( strval ); + return ic35fld; +} + +/* set,get property long integer value +* ----------------------------------- +*/ +void +SetLong( VObject * vobj, const char * id, long value ) +{ + VObject * vprop; + + if ( (vprop = isAPropertyOf( vobj, id )) == NULL ) + vprop = addProp( vobj, id ); + setVObjectLongValue( vprop, value ); +} +long /* property's long value or -1 if unknown */ +LongValue( VObject * vobj, const char * id ) +{ + VObject * vprop; + char * strval; + long value; + + if ( !( vobj && id && *id ) + || (vprop = isAPropertyOf( vobj, id )) == NULL ) + return -1; + switch ( vObjectValueType( vprop ) ) { + case VCVT_STRINGZ: + return atol( vObjectStringZValue( vprop ) ); + case VCVT_USTRINGZ: + strval = fakeCString( vObjectUStringZValue( vprop ) ); + value = atol( strval ); + deleteStr( strval ); + return value; + case VCVT_UINT: + return vObjectIntegerValue( vprop ); + case VCVT_ULONG: + return vObjectLongValue( vprop ); + } + return -1; +} + +/* set,get VObject NOTE/DESCRIPTION +* -------------------------------- +* some properties are not supported by e.g. korganizer, gnomecal, +* as workaround they are kept is marked lines in NOTE/DESCRIPTION. +*/ +static const char * +_NoteId( VObject * vobj ) +{ + switch( vca_type( vobj ) ) { + case VCARD: + case VMEMO: return VCNoteProp; + case VEVENT: + case VTODO: return VCDescriptionProp; + } + return NULL; +} +static size_t +_NotePropsLen( char * str, int vtype ) +{ + static struct { + int type; + const char * id; + } proptab[] = { + { VCARD, DEF1PROP }, + { VCARD, DEF2PROP }, + { VTODO, VCDTstartProp }, + { VTODO, VCDueProp }, + { VTODO, VCCategoriesProp }, + { 0, NULL } + }, + *ptab; + char * pline; + size_t lid; + + for ( pline = str; pline; pline = strchr( pline, '\n' ) ) { + if ( *pline == '\n' ) + ++pline; + for ( ptab = proptab; ptab->id != NULL; ++ptab ) + if ( ptab->type == vtype ) { + lid = strlen( ptab->id ); + if ( strncmp( pline, ptab->id, lid ) == 0 + && *(pline+lid) == ':' ) + break; + } + if ( ptab->id == NULL ) + break; + } + return pline ? pline - str : 0; +} +void +SetNotes( VObject * vobj, char * value ) +{ + /* preserve property values stored in NOTE,DESCRIPTION */ + /* translate CRLF to NL to avoid confusing korganizer */ + char * onote; + char * nnote; + size_t lprops; + const char *id; + + if ( (id = _NoteId( vobj )) == NULL ) + return; + value = dupSubstCRLF( value ? value : "" ); + if ( (onote = StringValue( vobj, id )) != NULL && *onote ) { + lprops = _NotePropsLen( onote, vca_type( vobj ) ); + nnote = dupStr( "", lprops + strlen(value) ); + strcat( strncat( strcpy( nnote, "" ), onote, lprops ), value ); + deleteStr( value ); + } else { + nnote = value; + } + SetString( vobj, id, nnote ); + deleteStr( nnote ); +} +char * +NotesValue( VObject * vobj ) +{ + /* skip property values stored in NOTE,DESCRIPTION */ + /* translate NL to CRLF to avoid confusing IC35 */ + char * note; + size_t lprops; + + if ( (note = StringValue( vobj, _NoteId( vobj ) )) == NULL + || strlen( note ) == 0 ) + return ""; + lprops = _NotePropsLen( note, vca_type( vobj ) ); + note = dupSubstNL( note+lprops ); + strcat( strcpy( ic35fld, "" ), note ); + deleteStr( note ); + return ic35fld; +} +void +SetNoteProp( VObject * vobj, const char * id, char * value ) +{ + /* replace or prepend property value in NOTE/DESCRIPTION */ + /* as marked line ":\n" */ + char *onote, *oprop, *oldstr; + char *nnote, *nprop; + const char *noteid; + + if ( !((noteid = _NoteId( vobj )) && id && *id ) ) + return; + onote = dupSubstNL( StringValue( vobj, noteid ) ); + if ( value && *value ) { + nprop = dupStr( "", strlen(id)+1+strlen(value)+2 ); + sprintf( nprop, "%s:%s\r\n", id, value ); + } else + nprop = dupStr( "", 0 ); + if ( (oldstr = NotePropValue( vobj, id )) != NULL && *oldstr ) { + oprop = dupStr( "", strlen(id)+1+strlen(oldstr)+2 ); + sprintf( oprop, "%s:%s\r\n", id, oldstr ); + nnote = dupSubst( onote, oprop, nprop ); + deleteStr( oprop ); + } else { + nnote = dupStr( "", strlen(nprop)+strlen(onote) ); + strcat( strcpy( nnote, nprop ), onote ); + } + deleteStr( onote ); + deleteStr( nprop ); + nnote = dupSubstCRLF( oldstr = nnote ); deleteStr( oldstr ); + SetString( vobj, noteid, nnote ); + deleteStr( nnote ); +} +char * +NotePropValue( VObject * vobj, const char * id ) +{ + /* retrieve property value stored in NOTE/DESCRIPTION */ + /* from marked line ":\n" */ + char * note; + char * linetag; + char * pprop; + char * ptr; + + if ( (note = StringValue( vobj, _NoteId( vobj ) )) == NULL + || strlen( note ) == 0 ) { + return ""; + } + sprintf( ptr = dupStr( "", strlen(note)+1 ), "\n%s", note ); + note = dupSubstNL( ptr ); + deleteStr( ptr ); + sprintf( linetag = dupStr( "", strlen(id)+3 ), "\r\n%s:", id ); + if ( (pprop = strstr( note, linetag )) != NULL ) { + pprop += strlen( linetag ); + if ( (ptr = strstr( pprop, "\r\n" )) != NULL ) + *ptr = '\0'; + strncat( strcpy( ic35fld, "" ), pprop, sizeof(ic35fld) ); + } + deleteStr( linetag ); + deleteStr( note ); + return pprop ? ic35fld : ""; +} + +/* categories +* ---------- +*/ +static char * +_is_stdCategory( char * category ) +{ + char * Address = "Address"; + char * Business = "Business"; + char * Personal = "Personal"; + char * Unfiled = "Unfiled"; + + if ( strcasecmp( category, Address ) == 0 ) + return Address; + else if ( strcasecmp( category, Business ) == 0 ) + return Business; + else if ( strcasecmp( category, Personal ) == 0 ) + return Personal; + else if ( strcasecmp( category, Unfiled ) == 0 ) + return Unfiled; + return NULL; +} +VObject * +SetCategory( VObject * vobj, char * newcategory ) +{ + char * categories; + char * oldcategories; + char * category; + VObject * vprop; + + if ( (oldcategories = StringValue( vobj, VCCategoriesProp )) == NULL + || strlen( oldcategories ) == 0 /* no categories or */ + || strchr( oldcategories, ';' ) == NULL ) /* single category */ + return SetString( vobj, VCCategoriesProp, newcategory ); + categories = dupStr( oldcategories, strlen( oldcategories ) + + 1 + strlen( newcategory ) ); + for ( category = strtok( categories, ";" ); + category != NULL; category = strtok( NULL, ";" ) ) { + if ( strcmp( category, newcategory ) == 0 ) + return isAPropertyOf( vobj, VCCategoriesProp ); + if ( _is_stdCategory( category ) ) + break; + } + if ( category != NULL ) { /* replace standard category */ + category = dupStr( category, 0 ); + deleteStr( categories ); + categories = dupSubst( oldcategories, category, newcategory ); + deleteStr( category ); + } else /* else prepend new category */ + sprintf( categories, "%s;%s", newcategory, oldcategories ); + vprop = SetString( vobj, VCCategoriesProp, categories ); + deleteStr( categories ); + return vprop; +} +char * /* single, first standard or "Unfiled" */ +CategoryValue( VObject * vobj ) +{ + char * Unfiled = "Unfiled"; + char * categories; + char * category; + char * firstcategory; + char * firststdcategory; + + if ( (categories = StringValue( vobj, VCCategoriesProp )) == NULL + || strlen( categories ) == 0 ) + return Unfiled; + firstcategory = firststdcategory = NULL; + for ( category = strtok( categories, ";" ); + category; category = strtok( NULL, ";" ) ) { + if ( firstcategory == NULL ) + firstcategory = dupStr( category, 0 ); + if ( firststdcategory == NULL ) + firststdcategory = _is_stdCategory( category ); + } + if ( (category = firststdcategory) == NULL ) { + if ( firstcategory ) + category = strncat( strcpy( ic35fld, "" ), + firstcategory, sizeof(ic35fld)-1 ); + else + category = Unfiled; + } + deleteStr( firstcategory ); + return category; +} + +/* telephone numbers +* ----------------- +*/ +VObject * +SetTel( VObject * vobj, const char * type, char * str ) +{ + VObject * vprop; + VObjectIterator iter; + + initPropIterator( &iter, vobj ); + while ( moreIteration( &iter ) ) { + vprop = nextVObject( &iter ); + if ( strcmp( vObjectName( vprop ), VCTelephoneProp ) == 0 + && isAPropertyOf( vprop, type ) ) { + if ( str && *str ) { + _ChangeString( vprop, str ); + return vprop; + } else { + cleanVObject( delProp( vobj, vprop ) ); + _vobj_dirty = TRUE; + return NULL; + } + } + } + if ( !( str && *str ) ) + return NULL; + _vobj_dirty = TRUE; + vprop = addPropValue( vobj, VCTelephoneProp, str ); + addProp( vprop, type ); + return vprop; +} + +/* email addresses +* --------------- +*/ +VObject * +SetEmail( VObject * vobj, int num, char * str ) +{ + VObject * vprop; + VObjectIterator iter; + int index; + + index = 0; + initPropIterator( &iter, vobj ); + while ( moreIteration( &iter ) ) { + vprop = nextVObject( &iter ); + if ( strcmp( vObjectName( vprop ), VCEmailAddressProp ) == 0 + && ++index == num ) { + if ( str && *str ) { + _ChangeString( vprop, str ); + return vprop; + } else { + cleanVObject( delProp( vobj, vprop ) ); + _vobj_dirty = TRUE; + return NULL; + } + } + } + if ( !( str && *str ) ) + return NULL; + _vobj_dirty = TRUE; + vprop = addPropValue( vobj, VCEmailAddressProp, str ); + addProp( vprop, VCInternetProp ); + return vprop; +} + + +/* ============================================ */ +/* ISO date+time conversions */ +/* ============================================ */ + +static time_t /* convert ISO date+time to Unix-time */ +isodtstr_to_unixtime( char * dtstr ) +{ + char * format; + struct tm dtm; + time_t dtime; + + if ( !( dtstr && *dtstr ) ) + return -1; + + if ( strspn( dtstr, "0123456789" ) == 8 ) + format = "%4d" "%2d" "%2d%*[^0-9]%2d" "%2d" "%2d"; + else + format = "%4d%*[^0-9]%2d%*[^0-9]%2d%*[^0-9]%2d%*[^0-9]%2d%*[^0-9]%2d"; + dtime = time( NULL ); + memcpy( &dtm, localtime( &dtime ), sizeof(dtm) ); /* default: today,now */ + sscanf( dtstr, format, &dtm.tm_year, &dtm.tm_mon, &dtm.tm_mday, + &dtm.tm_hour, &dtm.tm_min, &dtm.tm_sec ); + dtm.tm_year -= 1900; + dtm.tm_mon -= 1; + dtm.tm_isdst = -1; + dtime = mktime( &dtm ); +/*??? ymdhms_from_isodtime() does not respect timezone/Zulu in isodtime ???*/ + + return dtime; +} +char * +isodtstr_to_ymd_or_today( char * dtstr ) +{ + static char ymd[8+1]; + time_t utsec; + + if ( (utsec = isodtstr_to_unixtime( dtstr )) == -1 ) + utsec = time( NULL ); + strftime( ymd, sizeof(ymd), "%Y%m%d", localtime( &utsec ) ); + return ymd; +} +time_t /* convert ISO date+time to Unix-time */ +isodtime_to_unixtime( VObject * vobj, const char * id ) +{ + VObject * vprop; + char * dtstr; + time_t dtime; + + if ( (vprop = isAPropertyOf( vobj, id )) == NULL ) + return -1; + dtstr = fakeCString( vObjectUStringZValue( vprop ) ); + dtime = isodtstr_to_unixtime( dtstr ); + deleteStr( dtstr ); + return dtime; +} +char * +isodtime_to_ymd( VObject * vobj, const char * id ) +{ + static char ymd[8+1]; + time_t utsec; + + if ( (utsec = isodtime_to_unixtime( vobj, id )) == -1 ) + return ""; + strftime( ymd, sizeof(ymd), "%Y%m%d", localtime( &utsec ) ); + return ymd; +} +char * +isodtime_to_ymd_or_today( VObject * vobj, const char * id ) +{ + static char ymd[8+1]; + time_t utsec; + + if ( (utsec = isodtime_to_unixtime( vobj, id )) == -1 ) + utsec = time( NULL ); + strftime( ymd, sizeof(ymd), "%Y%m%d", localtime( &utsec ) ); + return ymd; +} +char * +isodtime_to_hms_or_now( VObject * vobj, const char * id ) +{ + static char hms[6+1]; + time_t utsec; + + if ( (utsec = isodtime_to_unixtime( vobj, id )) == -1 ) + utsec = time( NULL ); + strftime( hms, sizeof(hms), "%H%M%S", localtime( &utsec ) ); + return hms; +} diff --git a/src/vcutil.h b/src/vcutil.h new file mode 100644 index 0000000..80db336 --- /dev/null +++ b/src/vcutil.h @@ -0,0 +1,61 @@ +/************************************************************************ +* Copyright (C) 2000 Thomas Schulz * +* * +* $Id: vcutil.h,v 1.44 2000/12/27 22:26:52 tsch Rel $ * +* * +* header for IC35 data import/export: vCard,vCalendar utilities * +* * +************************************************************************/ +#ifndef _VCUTIL_H +#define _VCUTIL_H 1 + +#include "vcc.h" /* VObject, .. */ + + +/* vCard,vCal record types */ +#define VCARD 1 +#define VMEMO 2 +#define VCAL 3 +#define VEVENT 4 +#define VTODO 5 +/* markers for (def.) in IC35 address */ +#define DEF1PROP "(def1)" +#define DEF2PROP "(def2)" + + +int vca_type( VObject * vobj ); + +char * dupSubst( char * str, char * old, char * new ); +char * dupSubstNL( char * src ); +char * dupSubstCRLF( char * src ); + +void clr_vobjdirty( void ); +void SetModtimeIfdirty( VObject * vobj ); + +VObject * SetProp( VObject * vobj, const char * id ); +void DelProp( VObject * vobj, const char * id ); +VObject * SetString( VObject * vobj, const char * id, char * str ); +char * dupStringValue( VObject * vobj, const char * id ); +char * StringValue( VObject * vobj, const char * id ); + +void SetLong( VObject * vobj, const char * id, long value ); +long LongValue( VObject * vobj, const char * id ); + +void SetNotes( VObject * vobj, char * value ); +void SetNoteProp( VObject * vobj, const char * id, char * value ); +char * NotesValue( VObject * vobj ); +char * NotePropValue( VObject * vobj, const char * id ); + +VObject * SetCategory( VObject * vobj, char * newcategory ); +char * CategoryValue( VObject * vobj ); + +VObject * SetTel( VObject * vobj, const char * type, char * str ); +VObject * SetEmail( VObject * vobj, int num, char * str ); + +char * isodtstr_to_ymd_or_today( char * dtstr ); +time_t isodtime_to_unixtime( VObject * vobj, const char * id ); +char * isodtime_to_ymd( VObject * vobj, const char * id ); +char * isodtime_to_ymd_or_today( VObject * vobj, const char * id ); +char * isodtime_to_hms_or_now( VObject * vobj, const char * id ); + +#endif /*_VCUTIL_H*/ diff --git a/src/vobject.c b/src/vobject.c new file mode 100644 index 0000000..6797ed1 --- /dev/null +++ b/src/vobject.c @@ -0,0 +1,1511 @@ +/*************************************************************************** +(C) Copyright 1996 Apple Computer, Inc., AT&T Corp., International +Business Machines Corporation and Siemens Rolm Communications Inc. + +modified 2000 by Thomas Schulz: +$Id: vobject.c,v 1.9 2001/02/07 01:44:42 tsch Rel $ + +For purposes of this license notice, the term Licensors shall mean, +collectively, Apple Computer, Inc., AT&T Corp., International +Business Machines Corporation and Siemens Rolm Communications Inc. +The term Licensor shall mean any of the Licensors. + +Subject to acceptance of the following conditions, permission is hereby +granted by Licensors without the need for written agreement and without +license or royalty fees, to use, copy, modify and distribute this +software for any purpose. + +The above copyright notice and the following four paragraphs must be +reproduced in all copies of this software and any software including +this software. + +THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS AND NO LICENSOR SHALL HAVE +ANY OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS OR +MODIFICATIONS. + +IN NO EVENT SHALL ANY LICENSOR BE LIABLE TO ANY PARTY FOR DIRECT, +INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT +OF THE USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +EACH LICENSOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO ANY WARRANTY OF NONINFRINGEMENT OR THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. + +The software is provided with RESTRICTED RIGHTS. Use, duplication, or +disclosure by the government are subject to restrictions set forth in +DFARS 252.227-7013 or 48 CFR 52.227-19, as applicable. + +***************************************************************************/ + +/* + * src: vobject.c + * doc: vobject and APIs to construct vobject, APIs pretty print + * vobject, and convert a vobject into its textual representation. + */ + +#ifndef MWERKS +#include +#endif + +#include "vobject.h" +#include +#include +#include + + +#define NAME_OF(o) o->id +#define VALUE_TYPE(o) o->valType +#define STRINGZ_VALUE_OF(o) o->val.strs +#define USTRINGZ_VALUE_OF(o) o->val.ustrs +#define INTEGER_VALUE_OF(o) o->val.i +#define LONG_VALUE_OF(o) o->val.l +#define ANY_VALUE_OF(o) o->val.any +#define VOBJECT_VALUE_OF(o) o->val.vobj + +typedef union ValueItem { + const char *strs; + const wchar_t *ustrs; + unsigned int i; + unsigned long l; + void *any; + VObject *vobj; + } ValueItem; + +struct VObject { + VObject *next; + const char *id; + VObject *prop; + unsigned short valType; + ValueItem val; + }; + +typedef struct StrItem StrItem; + +struct StrItem { + StrItem *next; + const char *s; + unsigned int refCnt; + }; + +const char** fieldedProp; + + + +/*---------------------------------------------------------------------- + The following functions involve with memory allocation: + newVObject + deleteVObject + dupStr + deleteStr + newStrItem + deleteStrItem + ----------------------------------------------------------------------*/ + +static VObject* newVObject_(const char *id) +{ + VObject *p = (VObject*)malloc(sizeof(VObject)); + p->next = 0; + p->id = id; + p->prop = 0; + VALUE_TYPE(p) = 0; + ANY_VALUE_OF(p) = 0; + return p; +} + +DLLEXPORT(VObject*) newVObject(const char *id) +{ + return newVObject_(lookupStr(id)); +} + +DLLEXPORT(void) deleteVObject(VObject *p) +{ + unUseStr(p->id); + free(p); +} + +DLLEXPORT(char*) dupStr(const char *s, unsigned int size) +{ + char *t; + if (size == 0) { + size = strlen(s); + } + t = (char*)malloc(size+1); + if (t) { + memcpy(t,s,size); + t[size] = 0; + return t; + } + else { + return (char*)0; + } +} + +DLLEXPORT(void) deleteStr(const char *p) +{ + if (p) free((void*)p); +} + + +static StrItem* newStrItem(const char *s, StrItem *next) +{ + StrItem *p = (StrItem*)malloc(sizeof(StrItem)); + p->next = next; + p->s = s; + p->refCnt = 1; + return p; +} + +static void deleteStrItem(StrItem *p) +{ + free((void*)p); +} + + +/*---------------------------------------------------------------------- + The following function provide accesses to VObject's value. + ----------------------------------------------------------------------*/ + +DLLEXPORT(const char*) vObjectName(VObject *o) +{ + return NAME_OF(o); +} + +DLLEXPORT(void) setVObjectName(VObject *o, const char* id) +{ + NAME_OF(o) = id; +} + +DLLEXPORT(const char*) vObjectStringZValue(VObject *o) +{ + return STRINGZ_VALUE_OF(o); +} + +DLLEXPORT(void) setVObjectStringZValue(VObject *o, const char *s) +{ + STRINGZ_VALUE_OF(o) = dupStr(s,0); + VALUE_TYPE(o) = VCVT_STRINGZ; +} + +DLLEXPORT(void) setVObjectStringZValue_(VObject *o, const char *s) +{ + STRINGZ_VALUE_OF(o) = s; + VALUE_TYPE(o) = VCVT_STRINGZ; +} + +DLLEXPORT(const wchar_t*) vObjectUStringZValue(VObject *o) +{ + return USTRINGZ_VALUE_OF(o); +} + +DLLEXPORT(void) setVObjectUStringZValue(VObject *o, const wchar_t *s) +{ + USTRINGZ_VALUE_OF(o) = (wchar_t*) dupStr((char*)s,(uStrLen(s)+1)*2); + VALUE_TYPE(o) = VCVT_USTRINGZ; +} + +DLLEXPORT(void) setVObjectUStringZValue_(VObject *o, const wchar_t *s) +{ + USTRINGZ_VALUE_OF(o) = s; + VALUE_TYPE(o) = VCVT_USTRINGZ; +} + +DLLEXPORT(unsigned int) vObjectIntegerValue(VObject *o) +{ + return INTEGER_VALUE_OF(o); +} + +DLLEXPORT(void) setVObjectIntegerValue(VObject *o, unsigned int i) +{ + INTEGER_VALUE_OF(o) = i; + VALUE_TYPE(o) = VCVT_UINT; +} + +DLLEXPORT(unsigned long) vObjectLongValue(VObject *o) +{ + return LONG_VALUE_OF(o); +} + +DLLEXPORT(void) setVObjectLongValue(VObject *o, unsigned long l) +{ + LONG_VALUE_OF(o) = l; + VALUE_TYPE(o) = VCVT_ULONG; +} + +DLLEXPORT(void*) vObjectAnyValue(VObject *o) +{ + return ANY_VALUE_OF(o); +} + +DLLEXPORT(void) setVObjectAnyValue(VObject *o, void *t) +{ + ANY_VALUE_OF(o) = t; + VALUE_TYPE(o) = VCVT_RAW; +} + +DLLEXPORT(VObject*) vObjectVObjectValue(VObject *o) +{ + return VOBJECT_VALUE_OF(o); +} + +DLLEXPORT(void) setVObjectVObjectValue(VObject *o, VObject *p) +{ + VOBJECT_VALUE_OF(o) = p; + VALUE_TYPE(o) = VCVT_VOBJECT; +} + +DLLEXPORT(int) vObjectValueType(VObject *o) +{ + return VALUE_TYPE(o); +} + + +/*---------------------------------------------------------------------- + The following functions can be used to build VObject. + ----------------------------------------------------------------------*/ + +DLLEXPORT(VObject*) addVObjectProp(VObject *o, VObject *p) +{ + /* circular link list pointed to tail */ + /* + o {next,id,prop,val} + V + pn {next,id,prop,val} + V + ... + p1 {next,id,prop,val} + V + pn + --> + o {next,id,prop,val} + V + pn {next,id,prop,val} + V + p {next,id,prop,val} + ... + p1 {next,id,prop,val} + V + pn + */ + + VObject *tail = o->prop; + if (tail) { + p->next = tail->next; + o->prop = tail->next = p; + } + else { + o->prop = p->next = p; + } + return p; +} + +DLLEXPORT(VObject*) addProp(VObject *o, const char *id) +{ + return addVObjectProp(o,newVObject(id)); +} + +DLLEXPORT(VObject*) addProp_(VObject *o, const char *id) +{ + return addVObjectProp(o,newVObject_(id)); +} + +DLLEXPORT(void) addList(VObject **o, VObject *p) +{ + p->next = 0; + if (*o == 0) { + *o = p; + } + else { + VObject *t = *o; + while (t->next) { + t = t->next; + } + t->next = p; + } +} + +DLLEXPORT(VObject*) nextVObjectInList(VObject *o) +{ + return o->next; +} + +DLLEXPORT(VObject*) setValueWithSize_(VObject *prop, void *val, unsigned int size) +{ + VObject *sizeProp; + setVObjectAnyValue(prop, val); + sizeProp = addProp(prop,VCDataSizeProp); + setVObjectLongValue(sizeProp, size); + return prop; +} + +DLLEXPORT(VObject*) setValueWithSize(VObject *prop, void *val, unsigned int size) +{ + void *p = dupStr((const char *)val,size); + return setValueWithSize_(prop,p,p?size:0); +} + +DLLEXPORT(void) initPropIterator(VObjectIterator *i, VObject *o) +{ + i->start = o->prop; + i->next = 0; +} + +#if 0 /* not used, not in vobject.h */ +DLLEXPORT(void) initVObjectIterator(VObjectIterator *i, VObject *o) +{ + i->start = o->next; + i->next = 0; +} +#endif /* not used */ + +DLLEXPORT(int) moreIteration(VObjectIterator *i) +{ + return (i->start && (i->next==0 || i->next!=i->start)); +} + +DLLEXPORT(VObject*) nextVObject(VObjectIterator *i) +{ + if (i->start && i->next != i->start) { + if (i->next == 0) { + i->next = i->start->next; + return i->next; + } + else { + i->next = i->next->next; + return i->next; + } + } + else return (VObject*)0; +} + +DLLEXPORT(VObject*) isAPropertyOf(VObject *o, const char *id) +{ + VObjectIterator i; + initPropIterator(&i,o); + while (moreIteration(&i)) { + VObject *each = nextVObject(&i); + if (!stricmp(id,each->id)) + return each; + } + return (VObject*)0; +} + +DLLEXPORT(VObject*) addGroup(VObject *o, const char *g) +{ + /* + a.b.c + --> + prop(c) + prop(VCGrouping=b) + prop(VCGrouping=a) + */ + char *dot = strrchr(g,'.'); + if (dot) { + VObject *p, *t; + char *gs, *n = dot+1; + gs = dupStr(g,0); /* so we can write to it. */ + /* used to be + * t = p = addProp_(o,lookupProp_(n)); + */ + t = p = addProp_(o,lookupProp(n)); + dot = strrchr(gs,'.'); + *dot = 0; + do { + dot = strrchr(gs,'.'); + if (dot) { + n = dot+1; + *dot=0; + } + else + n = gs; + /* property(VCGroupingProp=n); + * and the value may have VCGrouping property + */ + t = addProp(t,VCGroupingProp); + setVObjectStringZValue(t,lookupProp_(n)); + } while (n != gs); + deleteStr(gs); + return p; + } + else + return addProp_(o,lookupProp(g)); +} + +DLLEXPORT(VObject*) addPropValue(VObject *o, const char *p, const char *v) +{ + VObject *prop; + prop = addProp(o,p); + setVObjectUStringZValue_(prop, fakeUnicode(v,0)); + return prop; +} + +DLLEXPORT(VObject*) addPropSizedValue_(VObject *o, const char *p, const char *v, + unsigned int size) +{ + VObject *prop; + prop = addProp(o,p); + setValueWithSize_(prop, (void*)v, size); + return prop; +} + +DLLEXPORT(VObject*) addPropSizedValue(VObject *o, const char *p, const char *v, + unsigned int size) +{ + return addPropSizedValue_(o,p,dupStr(v,size),size); +} + + + +/*---------------------------------------------------------------------- + The following pretty print a VObject + ----------------------------------------------------------------------*/ + +static void printVObject_(FILE *fp, VObject *o, int level); + +static void indent(FILE *fp, int level) +{ + int i; + for (i=0;i if CRLF-mode enabled */ + appendcOFile_(fp,0xd); + appendcOFile_(fp,0xa); + } + else + appendcOFile_(fp,c); +} + +static void appendsOFile(OFile *fp, const char *s) +{ + int i, slen; + slen = strlen(s); + for (i=0; ifp = ofp; + fp->s = 0; + fp->len = 0; + fp->limit = 0; + fp->alloc = 0; + fp->fail = 0; +} + +static void initMemOFile(OFile *fp, char *s, int len) +{ + fp->fp = 0; + fp->s = s; + fp->len = 0; + fp->limit = s?len:0; + fp->alloc = s?0:1; + fp->fail = 0; +} + + +static int writeBase64(OFile *fp, unsigned char *s, long len) +{ + long cur = 0; + int i, numQuads = 0; + unsigned long trip; + unsigned char b; + char quad[5]; +#define MAXQUADS 16 + + quad[4] = 0; + + while (cur < len) { + /* collect the triplet of bytes into 'trip' */ + trip = 0; + for (i = 0; i < 3; i++) { + b = (cur < len) ? *(s + cur) : 0; + cur++; + trip = trip << 8 | b; + } + /* fill in 'quad' with the appropriate four characters */ + for (i = 3; i >= 0; i--) { + b = (unsigned char)(trip & 0x3F); + trip = trip >> 6; + if ((3 - i) < (cur - len)) + quad[i] = '='; /* pad char */ + else if (b < 26) quad[i] = (char)b + 'A'; + else if (b < 52) quad[i] = (char)(b - 26) + 'a'; + else if (b < 62) quad[i] = (char)(b - 52) + '0'; + else if (b == 62) quad[i] = '+'; + else quad[i] = '/'; + } + /* now output 'quad' with appropriate whitespace and line ending */ + appendsOFile(fp, (numQuads == 0 ? " " : "")); + appendsOFile(fp, quad); + appendsOFile(fp, ((cur >= len)?"\n" :(numQuads==MAXQUADS-1?"\n" : ""))); + numQuads = (numQuads + 1) % MAXQUADS; + } + appendcOFile(fp,'\n'); + + return 1; +} + +static void writeString(OFile *fp, const char *s) +{ + appendsOFile(fp,s); +} + +static void writeQPString(OFile *fp, const char *s) +{ + char buf[4]; + int count=0; + const char *p = s; + + while (*p) { + /* break up lines biggger than 75 chars */ + if(count >=74){ + count=0; + appendsOFile(fp,"=\n"); + } + + /* escape any non ASCII characters and '=' as per rfc1521 */ + if (*p<= 0x1f || *p >=0x7f || *p == '=' ) { + sprintf(buf,"=%02X",(unsigned char)*p); + appendsOFile(fp,buf); + count+=3; + } else { + appendcOFile(fp,*p); + count++; + } + p++; + } +} + + + +static void writeVObject_(OFile *fp, VObject *o); + +static void writeValue(OFile *fp, VObject *o, unsigned long size,int quote) +{ + if (o == 0) return; + switch (VALUE_TYPE(o)) { + case VCVT_USTRINGZ: { + char *s = fakeCString(USTRINGZ_VALUE_OF(o)); + if(quote) writeQPString(fp, s); + else writeString(fp,s); + deleteStr(s); + break; + } + case VCVT_STRINGZ: { + if(quote) writeQPString(fp, STRINGZ_VALUE_OF(o)); + else writeString(fp,STRINGZ_VALUE_OF(o)); + break; + } + case VCVT_UINT: { + char buf[16]; + sprintf(buf,"%u", INTEGER_VALUE_OF(o)); + appendsOFile(fp,buf); + break; + } + case VCVT_ULONG: { + char buf[16]; + sprintf(buf,"%lu", LONG_VALUE_OF(o)); + appendsOFile(fp,buf); + break; + } + case VCVT_RAW: { + appendcOFile(fp,'\n'); + writeBase64(fp,(unsigned char*)(ANY_VALUE_OF(o)),size); + break; + } + case VCVT_VOBJECT: + appendcOFile(fp,'\n'); + writeVObject_(fp,VOBJECT_VALUE_OF(o)); + break; + } +} + +static void writeAttrValue(OFile *fp, VObject *o) +{ + if (NAME_OF(o)) { + struct PreDefProp *pi; + pi = lookupPropInfo(NAME_OF(o)); + if (pi && ((pi->flags & PD_INTERNAL) != 0)) return; + appendcOFile(fp,';'); + appendsOFile(fp,NAME_OF(o)); + } + else + appendcOFile(fp,';'); + if (VALUE_TYPE(o)) { + appendcOFile(fp,'='); + writeValue(fp,o,0,0); + } +} + +static void writeGroup(OFile *fp, VObject *o) +{ + char buf1[256]; + char buf2[256]; + strcpy(buf1,NAME_OF(o)); + while ((o=isAPropertyOf(o,VCGroupingProp)) != 0) { + strcpy(buf2,STRINGZ_VALUE_OF(o)); + strcat(buf2,"."); + strcat(buf2,buf1); + strcpy(buf1,buf2); + } + appendsOFile(fp,buf1); +} + +static int inList(const char **list, const char *s) +{ + if (list == 0) return 0; + while (*list) { + if (stricmp(*list,s) == 0) return 1; + list++; + } + return 0; +} + +static void writeProp(OFile *fp, VObject *o) +{ + int isQuoted=0; + if (NAME_OF(o)) { + struct PreDefProp *pi; + VObjectIterator t; + const char **fields_ = 0; + pi = lookupPropInfo(NAME_OF(o)); + if (pi && ((pi->flags & PD_BEGIN) != 0)) { + writeVObject_(fp,o); + return; + } + if (isAPropertyOf(o,VCGroupingProp)) + writeGroup(fp,o); + else + appendsOFile(fp,NAME_OF(o)); + if (pi) fields_ = pi->fields; + initPropIterator(&t,o); + while (moreIteration(&t)) { + const char *s; + VObject *eachProp = nextVObject(&t); + s = NAME_OF(eachProp); + if (stricmp(VCGroupingProp,s) && !inList(fields_,s)) + writeAttrValue(fp,eachProp); + if (stricmp(VCQPProp,s)==0 || stricmp(VCQuotedPrintableProp,s)==0) + isQuoted=1; + } + if (fields_) { + int i = 0, n = 0; + const char** fields = fields_; + /* output prop as fields */ + appendcOFile(fp,':'); + while (*fields) { + VObject *t = isAPropertyOf(o,*fields); + i++; + if (t) n = i; + fields++; + } + fields = fields_; + for (i=0;iflags & PD_BEGIN) != 0)) { + VObjectIterator t; + const char *begin = NAME_OF(o); + appendsOFile(fp,"BEGIN:"); + appendsOFile(fp,begin); + appendcOFile(fp,'\n'); + initPropIterator(&t,o); + while (moreIteration(&t)) { + VObject *eachProp = nextVObject(&t); + writeProp(fp, eachProp); + } + appendsOFile(fp,"END:"); + appendsOFile(fp,begin); + appendsOFile(fp,"\n\n"); + } + } +} + +void writeVObject(FILE *fp, VObject *o) +{ + OFile ofp; + initOFile(&ofp,fp); + writeVObject_(&ofp,o); +} + +DLLEXPORT(void) writeVObjectToFile(char *fname, VObject *o) +{ + FILE *fp = fopen(fname,"w"); + if (fp) { + writeVObject(fp,o); + fclose(fp); + } +} + +DLLEXPORT(void) writeVObjectsToFile(char *fname, VObject *list) +{ + FILE *fp = fopen(fname,"w"); + if (fp) { + while (list) { + writeVObject(fp,list); + list = nextVObjectInList(list); + } + fclose(fp); + } +} + +DLLEXPORT(char*) writeMemVObject(char *s, int *len, VObject *o) +{ + OFile ofp; + initMemOFile(&ofp,s,len?*len:0); + writeVObject_(&ofp,o); + if (len) *len = ofp.len; + appendcOFile(&ofp,0); + return ofp.s; +} + +DLLEXPORT(char*) writeMemVObjects(char *s, int *len, VObject *list) +{ + OFile ofp; + initMemOFile(&ofp,s,len?*len:0); + while (list) { + writeVObject_(&ofp,list); + list = nextVObjectInList(list); + } + if (len) *len = ofp.len; + appendcOFile(&ofp,0); + return ofp.s; +} + +/*---------------------------------------------------------------------- + APIs to do fake Unicode stuff. + ----------------------------------------------------------------------*/ +DLLEXPORT(wchar_t*) fakeUnicode(const char *ps, int *bytes) +{ + wchar_t *r, *pw; + int len = strlen(ps)+1; + + pw = r = (wchar_t*)malloc(sizeof(wchar_t)*len); + if (bytes) + *bytes = len * sizeof(wchar_t); + + while (*ps) { + if (*ps == '\n') + *pw = (wchar_t)0x2028; + else if (*ps == '\r') + *pw = (wchar_t)0x2029; + else + *pw = (wchar_t)(unsigned char)*ps; + ps++; pw++; + } + *pw = (wchar_t)0; + + return r; +} + +DLLEXPORT(int) uStrLen(const wchar_t *u) +{ + int i = 0; + while (*u != (wchar_t)0) { u++; i++; } + return i; +} + +DLLEXPORT(char*) fakeCString(const wchar_t *u) +{ + char *s, *t; + int len = uStrLen(u) + 1; + t = s = (char*)malloc(len); + while (*u) { + if (*u == (wchar_t)0x2028) + *t = '\n'; + else if (*u == (wchar_t)0x2029) + *t = '\r'; + else + *t = (char)*u; + u++; t++; + } + *t = 0; + return s; +} + +/* end of source file vobject.c */ diff --git a/src/vobject.h b/src/vobject.h new file mode 100644 index 0000000..2ff71b7 --- /dev/null +++ b/src/vobject.h @@ -0,0 +1,380 @@ +/*************************************************************************** +(C) Copyright 1996 Apple Computer, Inc., AT&T Corp., International +Business Machines Corporation and Siemens Rolm Communications Inc. + +modified 2000 by Thomas Schulz: +$Id: vobject.h,v 1.6 2000/12/21 23:01:45 tsch Rel $ + +For purposes of this license notice, the term Licensors shall mean, +collectively, Apple Computer, Inc., AT&T Corp., International +Business Machines Corporation and Siemens Rolm Communications Inc. +The term Licensor shall mean any of the Licensors. + +Subject to acceptance of the following conditions, permission is hereby +granted by Licensors without the need for written agreement and without +license or royalty fees, to use, copy, modify and distribute this +software for any purpose. + +The above copyright notice and the following four paragraphs must be +reproduced in all copies of this software and any software including +this software. + +THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS AND NO LICENSOR SHALL HAVE +ANY OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS OR +MODIFICATIONS. + +IN NO EVENT SHALL ANY LICENSOR BE LIABLE TO ANY PARTY FOR DIRECT, +INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT +OF THE USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +EACH LICENSOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO ANY WARRANTY OF NONINFRINGEMENT OR THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. + +The software is provided with RESTRICTED RIGHTS. Use, duplication, or +disclosure by the government are subject to restrictions set forth in +DFARS 252.227-7013 or 48 CFR 52.227-19, as applicable. + +***************************************************************************/ + +/* + +The vCard/vCalendar C interface is implemented in the set +of files as follows: + +vcc.y, yacc source, and vcc.c, the yacc output you will use +implements the core parser + +vobject.c implements an API that insulates the caller from +the parser and changes in the vCard/vCalendar BNF + +port.h defines compilation environment dependent stuff + +vcc.h and vobject.h are header files for their .c counterparts + +vcaltmp.h and vcaltmp.c implement vCalendar "macro" functions +which you may find useful. + +test.c is a standalone test driver that exercises some of +the features of the APIs provided. Invoke test.exe on a +VCARD/VCALENDAR input text file and you will see the pretty +print output of the internal representation (this pretty print +output should give you a good idea of how the internal +representation looks like -- there is one such output in the +following too). Also, a file with the .out suffix is generated +to show that the internal representation can be written back +in the original text format. + +For more information on this API see the readme.txt file +which accompanied this distribution. + + Also visit: + + http://www.versit.com + http://www.ralden.com + +*/ + + +#ifndef __VOBJECT_H__ +#define __VOBJECT_H__ 1 + + +#include "port.h" +#include +#include + +#if defined(__CPLUSPLUS__) || defined(__cplusplus) +extern "C" { +#endif + + +#define VC7bitProp "7BIT" +#define VC8bitProp "8BIT" +#define VCAAlarmProp "AALARM" +#define VCAdditionalNamesProp "ADDN" +#define VCAdrProp "ADR" +#define VCAgentProp "AGENT" +#define VCAIFFProp "AIFF" +#define VCAOLProp "AOL" +#define VCAppleLinkProp "APPLELINK" +#define VCAttachProp "ATTACH" +#define VCAttendeeProp "ATTENDEE" +#define VCATTMailProp "ATTMAIL" +#define VCAudioContentProp "AUDIOCONTENT" +#define VCAVIProp "AVI" +#define VCBase64Prop "BASE64" +#define VCBBSProp "BBS" +#define VCBirthDateProp "BDAY" +#define VCBMPProp "BMP" +#define VCBodyProp "BODY" +#define VCBusinessRoleProp "ROLE" +#define VCCalProp "VCALENDAR" +#define VCCaptionProp "CAP" +#define VCCardProp "VCARD" +#define VCCarProp "CAR" +#define VCCategoriesProp "CATEGORIES" +#define VCCellularProp "CELL" +#define VCCGMProp "CGM" +#define VCCharSetProp "CS" +#define VCCIDProp "CID" +#define VCCISProp "CIS" +#define VCCityProp "L" +#define VCClassProp "CLASS" +#define VCCommentProp "NOTE" +#define VCCompletedProp "COMPLETED" +#define VCContentIDProp "CONTENT-ID" +#define VCCountryNameProp "C" +#define VCDAlarmProp "DALARM" +#define VCDataSizeProp "DATASIZE" +#define VCDayLightProp "DAYLIGHT" +#define VCDCreatedProp "DCREATED" +#define VCDeliveryLabelProp "LABEL" +#define VCDescriptionProp "DESCRIPTION" +#define VCDIBProp "DIB" +#define VCDisplayStringProp "DISPLAYSTRING" +#define VCDomesticProp "DOM" +#define VCDTendProp "DTEND" +#define VCDTstartProp "DTSTART" +#define VCDueProp "DUE" +#define VCEmailAddressProp "EMAIL" +#define VCEncodingProp "ENCODING" +#define VCEndProp "END" +#define VCEventProp "VEVENT" +#define VCEWorldProp "EWORLD" +#define VCExNumProp "EXNUM" +#define VCExpDateProp "EXDATE" +#define VCExpectProp "EXPECT" +#define VCExtAddressProp "EXT ADD" +#define VCFamilyNameProp "F" +#define VCFaxProp "FAX" +#define VCFullNameProp "FN" +#define VCGeoProp "GEO" +#define VCGeoLocationProp "GEO" +#define VCGIFProp "GIF" +#define VCGivenNameProp "G" +#define VCGroupingProp "Grouping" +#define VCHomeProp "HOME" +#define VCIBMMailProp "IBMMail" +#define VCInlineProp "INLINE" +#define VCInternationalProp "INTL" +#define VCInternetProp "INTERNET" +#define VCISDNProp "ISDN" +#define VCJPEGProp "JPEG" +#define VCLanguageProp "LANG" +#define VCLastModifiedProp "LAST-MODIFIED" +#define VCLastRevisedProp "REV" +#define VCLocationProp "LOCATION" +#define VCLogoProp "LOGO" +#define VCMailerProp "MAILER" +#define VCMAlarmProp "MALARM" +#define VCMCIMailProp "MCIMAIL" +#define VCMessageProp "MSG" +#define VCMETProp "MET" +#define VCModemProp "MODEM" +#define VCMPEG2Prop "MPEG2" +#define VCMPEGProp "MPEG" +#define VCMSNProp "MSN" +#define VCNamePrefixesProp "NPRE" +#define VCNameProp "N" +#define VCNameSuffixesProp "NSUF" +#define VCNoteProp "NOTE" +#define VCOrgNameProp "ORGNAME" +#define VCOrgProp "ORG" +#define VCOrgUnit2Prop "OUN2" +#define VCOrgUnit3Prop "OUN3" +#define VCOrgUnit4Prop "OUN4" +#define VCOrgUnitProp "OUN" +#define VCPagerProp "PAGER" +#define VCPAlarmProp "PALARM" +#define VCParcelProp "PARCEL" +#define VCPartProp "PART" +#define VCPCMProp "PCM" +#define VCPDFProp "PDF" +#define VCPGPProp "PGP" +#define VCPhotoProp "PHOTO" +#define VCPICTProp "PICT" +#define VCPMBProp "PMB" +#define VCPostalBoxProp "BOX" +#define VCPostalCodeProp "PC" +#define VCPostalProp "POSTAL" +#define VCPowerShareProp "POWERSHARE" +#define VCPreferredProp "PREF" +#define VCPriorityProp "PRIORITY" +#define VCProcedureNameProp "PROCEDURENAME" +#define VCProdIdProp "PRODID" +#define VCProdigyProp "PRODIGY" +#define VCPronunciationProp "SOUND" +#define VCPSProp "PS" +#define VCPublicKeyProp "KEY" +#define VCQPProp "QP" +#define VCQuickTimeProp "QTIME" +#define VCQuotedPrintableProp "QUOTED-PRINTABLE" +#define VCRDateProp "RDATE" +#define VCRegionProp "R" +#define VCRelatedToProp "RELATED-TO" +#define VCRepeatCountProp "REPEATCOUNT" +#define VCResourcesProp "RESOURCES" +#define VCRNumProp "RNUM" +#define VCRoleProp "ROLE" +#define VCRRuleProp "RRULE" +#define VCRSVPProp "RSVP" +#define VCRunTimeProp "RUNTIME" +#define VCSequenceProp "SEQUENCE" +#define VCSnoozeTimeProp "SNOOZETIME" +#define VCStartProp "START" +#define VCStatusProp "STATUS" +#define VCStreetAddressProp "STREET" +#define VCSubTypeProp "SUBTYPE" +#define VCSummaryProp "SUMMARY" +#define VCTelephoneProp "TEL" +#define VCTIFFProp "TIFF" +#define VCTimeZoneProp "TZ" +#define VCTitleProp "TITLE" +#define VCTLXProp "TLX" +#define VCTodoProp "VTODO" +#define VCTranspProp "TRANSP" +#define VCUniqueStringProp "UID" +#define VCURLProp "URL" +#define VCURLValueProp "URLVAL" +#define VCValueProp "VALUE" +#define VCVersionProp "VERSION" +#define VCVideoProp "VIDEO" +#define VCVoiceProp "VOICE" +#define VCWAVEProp "WAVE" +#define VCWMFProp "WMF" +#define VCWorkProp "WORK" +#define VCX400Prop "X400" +#define VCX509Prop "X509" +#define VCXRuleProp "XRULE" + +/* missing, although described in "vCard v2.1" vcard-21.ps */ + +#define VCCharacterSetProp "CHARSET" + +/* Extensions */ + +#define XPilotIdProp "X-PILOTID" +#define XPilotStatusProp "X-PILOTSTAT" + +/* non-standard for Memo/Note */ +#define VCMemoProp "VMEMO" + +typedef struct VObject VObject; + +typedef struct VObjectIterator { + VObject* start; + VObject* next; + } VObjectIterator; + +extern DLLEXPORT(VObject*) newVObject(const char *id); +extern DLLEXPORT(void) deleteVObject(VObject *p); +extern DLLEXPORT(char*) dupStr(const char *s, unsigned int size); +extern DLLEXPORT(void) deleteStr(const char *p); +extern DLLEXPORT(void) unUseStr(const char *s); + +extern DLLEXPORT(void) setVObjectName(VObject *o, const char* id); +extern DLLEXPORT(void) setVObjectStringZValue(VObject *o, const char *s); +extern DLLEXPORT(void) setVObjectStringZValue_(VObject *o, const char *s); +extern DLLEXPORT(void) setVObjectUStringZValue(VObject *o, const wchar_t *s); +extern DLLEXPORT(void) setVObjectUStringZValue_(VObject *o, const wchar_t *s); +extern DLLEXPORT(void) setVObjectIntegerValue(VObject *o, unsigned int i); +extern DLLEXPORT(void) setVObjectLongValue(VObject *o, unsigned long l); +extern DLLEXPORT(void) setVObjectAnyValue(VObject *o, void *t); +extern DLLEXPORT(VObject*) setValueWithSize(VObject *prop, void *val, unsigned int size); +extern DLLEXPORT(VObject*) setValueWithSize_(VObject *prop, void *val, unsigned int size); + +extern DLLEXPORT(const char*) vObjectName(VObject *o); +extern DLLEXPORT(const char*) vObjectStringZValue(VObject *o); +extern DLLEXPORT(const wchar_t*) vObjectUStringZValue(VObject *o); +extern DLLEXPORT(unsigned int) vObjectIntegerValue(VObject *o); +extern DLLEXPORT(unsigned long) vObjectLongValue(VObject *o); +extern DLLEXPORT(void*) vObjectAnyValue(VObject *o); +extern DLLEXPORT(VObject*) vObjectVObjectValue(VObject *o); +extern DLLEXPORT(void) setVObjectVObjectValue(VObject *o, VObject *p); + +extern DLLEXPORT(VObject*) addVObjectProp(VObject *o, VObject *p); +extern DLLEXPORT(VObject*) addProp(VObject *o, const char *id); +extern DLLEXPORT(VObject*) addProp_(VObject *o, const char *id); +extern DLLEXPORT(VObject*) addPropValue(VObject *o, const char *p, const char *v); +extern DLLEXPORT(VObject*) addPropSizedValue_(VObject *o, const char *p, const char *v, unsigned int size); +extern DLLEXPORT(VObject*) addPropSizedValue(VObject *o, const char *p, const char *v, unsigned int size); +extern DLLEXPORT(VObject*) addGroup(VObject *o, const char *g); +extern DLLEXPORT(void) addList(VObject **o, VObject *p); + +extern DLLEXPORT(VObject*) isAPropertyOf(VObject *o, const char *id); + +extern DLLEXPORT(VObject*) nextVObjectInList(VObject *o); +extern DLLEXPORT(void) initPropIterator(VObjectIterator *i, VObject *o); +extern DLLEXPORT(int) moreIteration(VObjectIterator *i); +extern DLLEXPORT(VObject*) nextVObject(VObjectIterator *i); + +extern DLLEXPORT(char*) writeMemVObject(char *s, int *len, VObject *o); +extern DLLEXPORT(char*) writeMemVObjects(char *s, int *len, VObject *list); + +extern DLLEXPORT(const char*) lookupStr(const char *s); +extern DLLEXPORT(void) cleanStrTbl(void); + +extern DLLEXPORT(void) cleanVObject(VObject *o); +extern DLLEXPORT(void) cleanVObjects(VObject *list); +extern DLLEXPORT(VObject*) delList( VObject ** list, VObject * vobj ); +extern DLLEXPORT(VObject*) delProp( VObject * vobj, VObject * prop ); + +extern DLLEXPORT(const char*) lookupProp(const char* str); +extern DLLEXPORT(const char*) lookupProp_(const char* str); + +extern DLLEXPORT(wchar_t*) fakeUnicode(const char *ps, int *bytes); +extern DLLEXPORT(int) uStrLen(const wchar_t *u); +extern DLLEXPORT(char*) fakeCString(const wchar_t *u); + +extern DLLEXPORT(void) printVObjectToFile(char *fname,VObject *o); +extern DLLEXPORT(void) printVObjectsToFile(char *fname,VObject *list); +extern DLLEXPORT(void) writeVObjectToFile(char *fname, VObject *o); +extern DLLEXPORT(void) writeVObjectsToFile(char *fname, VObject *list); +extern DLLEXPORT(void) setCRLFmode( int mode ); +extern DLLEXPORT(int) CRLFmode( void ); + +extern DLLEXPORT(int) vObjectValueType(VObject *o); + +/* return type of vObjectValueType: */ +#define VCVT_NOVALUE 0 + /* if the VObject has no value associated with it. */ +#define VCVT_STRINGZ 1 + /* if the VObject has value set by setVObjectStringZValue. */ +#define VCVT_USTRINGZ 2 + /* if the VObject has value set by setVObjectUStringZValue. */ +#define VCVT_UINT 3 + /* if the VObject has value set by setVObjectIntegerValue. */ +#define VCVT_ULONG 4 + /* if the VObject has value set by setVObjectLongValue. */ +#define VCVT_RAW 5 + /* if the VObject has value set by setVObjectAnyValue. */ +#define VCVT_VOBJECT 6 + /* if the VObject has value set by setVObjectVObjectValue. */ + +extern const char** fieldedProp; + +/* NOTE regarding printVObject and writeVObject + +The functions below are not exported from the DLL because they +take a FILE* as a parameter, which cannot be passed across a DLL +interface (at least that is my experience). Instead you can use +their companion functions which take file names or pointers +to memory. However, if you are linking this code into +your build directly then you may find them a more convenient API +and you can go ahead and use them. If you try to use them with +the DLL LIB you will get a link error. +*/ +extern void printVObject(FILE *fp,VObject *o); +extern void writeVObject(FILE *fp, VObject *o); + + +#if defined(__CPLUSPLUS__) || defined(__cplusplus) +} +#endif + +#endif /* __VOBJECT_H__ */ + + diff --git a/stamp-h1 b/stamp-h1 new file mode 100644 index 0000000..4547fe1 --- /dev/null +++ b/stamp-h1 @@ -0,0 +1 @@ +timestamp for config.h diff --git a/ylwrap b/ylwrap new file mode 100755 index 0000000..d153336 --- /dev/null +++ b/ylwrap @@ -0,0 +1,247 @@ +#! /bin/sh +# ylwrap - wrapper for lex/yacc invocations. + +scriptversion=2018-03-07.03; # UTC + +# Copyright (C) 1996-2020 Free Software Foundation, Inc. +# +# Written by Tom Tromey . +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to or send patches to +# . + +get_dirname () +{ + case $1 in + */*|*\\*) printf '%s\n' "$1" | sed -e 's|\([\\/]\)[^\\/]*$|\1|';; + # Otherwise, we want the empty string (not "."). + esac +} + +# guard FILE +# ---------- +# The CPP macro used to guard inclusion of FILE. +guard () +{ + printf '%s\n' "$1" \ + | sed \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' \ + -e 's/[^ABCDEFGHIJKLMNOPQRSTUVWXYZ]/_/g' \ + -e 's/__*/_/g' +} + +# quote_for_sed [STRING] +# ---------------------- +# Return STRING (or stdin) quoted to be used as a sed pattern. +quote_for_sed () +{ + case $# in + 0) cat;; + 1) printf '%s\n' "$1";; + esac \ + | sed -e 's|[][\\.*]|\\&|g' +} + +case "$1" in + '') + echo "$0: No files given. Try '$0 --help' for more information." 1>&2 + exit 1 + ;; + --basedir) + basedir=$2 + shift 2 + ;; + -h|--h*) + cat <<\EOF +Usage: ylwrap [--help|--version] INPUT [OUTPUT DESIRED]... -- PROGRAM [ARGS]... + +Wrapper for lex/yacc invocations, renaming files as desired. + + INPUT is the input file + OUTPUT is one file PROG generates + DESIRED is the file we actually want instead of OUTPUT + PROGRAM is program to run + ARGS are passed to PROG + +Any number of OUTPUT,DESIRED pairs may be used. + +Report bugs to . +EOF + exit $? + ;; + -v|--v*) + echo "ylwrap $scriptversion" + exit $? + ;; +esac + + +# The input. +input=$1 +shift +# We'll later need for a correct munging of "#line" directives. +input_sub_rx=`get_dirname "$input" | quote_for_sed` +case $input in + [\\/]* | ?:[\\/]*) + # Absolute path; do nothing. + ;; + *) + # Relative path. Make it absolute. + input=`pwd`/$input + ;; +esac +input_rx=`get_dirname "$input" | quote_for_sed` + +# Since DOS filename conventions don't allow two dots, +# the DOS version of Bison writes out y_tab.c instead of y.tab.c +# and y_tab.h instead of y.tab.h. Test to see if this is the case. +y_tab_nodot=false +if test -f y_tab.c || test -f y_tab.h; then + y_tab_nodot=true +fi + +# The parser itself, the first file, is the destination of the .y.c +# rule in the Makefile. +parser=$1 + +# A sed program to s/FROM/TO/g for all the FROM/TO so that, for +# instance, we rename #include "y.tab.h" into #include "parse.h" +# during the conversion from y.tab.c to parse.c. +sed_fix_filenames= + +# Also rename header guards, as Bison 2.7 for instance uses its header +# guard in its implementation file. +sed_fix_header_guards= + +while test $# -ne 0; do + if test x"$1" = x"--"; then + shift + break + fi + from=$1 + # Handle y_tab.c and y_tab.h output by DOS + if $y_tab_nodot; then + case $from in + "y.tab.c") from=y_tab.c;; + "y.tab.h") from=y_tab.h;; + esac + fi + shift + to=$1 + shift + sed_fix_filenames="${sed_fix_filenames}s|"`quote_for_sed "$from"`"|$to|g;" + sed_fix_header_guards="${sed_fix_header_guards}s|"`guard "$from"`"|"`guard "$to"`"|g;" +done + +# The program to run. +prog=$1 +shift +# Make any relative path in $prog absolute. +case $prog in + [\\/]* | ?:[\\/]*) ;; + *[\\/]*) prog=`pwd`/$prog ;; +esac + +dirname=ylwrap$$ +do_exit="cd '`pwd`' && rm -rf $dirname > /dev/null 2>&1;"' (exit $ret); exit $ret' +trap "ret=129; $do_exit" 1 +trap "ret=130; $do_exit" 2 +trap "ret=141; $do_exit" 13 +trap "ret=143; $do_exit" 15 +mkdir $dirname || exit 1 + +cd $dirname + +case $# in + 0) "$prog" "$input" ;; + *) "$prog" "$@" "$input" ;; +esac +ret=$? + +if test $ret -eq 0; then + for from in * + do + to=`printf '%s\n' "$from" | sed "$sed_fix_filenames"` + if test -f "$from"; then + # If $2 is an absolute path name, then just use that, + # otherwise prepend '../'. + case $to in + [\\/]* | ?:[\\/]*) target=$to;; + *) target=../$to;; + esac + + # Do not overwrite unchanged header files to avoid useless + # recompilations. Always update the parser itself: it is the + # destination of the .y.c rule in the Makefile. Divert the + # output of all other files to a temporary file so we can + # compare them to existing versions. + if test $from != $parser; then + realtarget=$target + target=tmp-`printf '%s\n' "$target" | sed 's|.*[\\/]||g'` + fi + + # Munge "#line" or "#" directives. Don't let the resulting + # debug information point at an absolute srcdir. Use the real + # output file name, not yy.lex.c for instance. Adjust the + # include guards too. + sed -e "/^#/!b" \ + -e "s|$input_rx|$input_sub_rx|" \ + -e "$sed_fix_filenames" \ + -e "$sed_fix_header_guards" \ + "$from" >"$target" || ret=$? + + # Check whether files must be updated. + if test "$from" != "$parser"; then + if test -f "$realtarget" && cmp -s "$realtarget" "$target"; then + echo "$to is unchanged" + rm -f "$target" + else + echo "updating $to" + mv -f "$target" "$realtarget" + fi + fi + else + # A missing file is only an error for the parser. This is a + # blatant hack to let us support using "yacc -d". If -d is not + # specified, don't fail when the header file is "missing". + if test "$from" = "$parser"; then + ret=1 + fi + fi + done +fi + +# Remove the directory. +cd .. +rm -rf $dirname + +exit $ret + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC0" +# time-stamp-end: "; # UTC" +# End: