commit 8c52e5dba8046d3ead0910f6238e1953f1b177ab Author: crt0mega Date: Tue Sep 15 21:16:28 2020 +0200 Initial commit 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: