diff options
author | Guido Günther <agx@sigxcpu.org> | 2014-02-21 15:57:11 +0100 |
---|---|---|
committer | Guido Günther <agx@sigxcpu.org> | 2014-02-24 08:39:05 +0100 |
commit | 891b34e2ef64f354474c4c6bec8e35f905e3c1db (patch) | |
tree | 9ff4373f980caa5eab84c4f3abaff6640783d817 |
Initial commit
52 files changed, 5734 insertions, 0 deletions
diff --git a/.dir-locals.el b/.dir-locals.el new file mode 100644 index 0000000..7c483d2 --- /dev/null +++ b/.dir-locals.el @@ -0,0 +1,8 @@ +( + (c-mode . ( + (c-file-style . "K&R") + (indent-tabs-mode . nil) + (c-indent-level . 4) + (c-basic-offset . 4) + )) + ) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1cfdc74 --- /dev/null +++ b/.gitignore @@ -0,0 +1,44 @@ +aclocal.m4 +config.h +config.status +config.log +configure +config.h.in +libtool +libplanfahr-*.tar.gz +libplanfahr-*.tar.xz +Makefile +Makefile.in +run +stamp-* +TAGS +*.la +*.lo +*.log +*.o +*.pc +*.gir +*.typelib +*~ +.deps/ +.libs/ +autom4te.cache/ +build-aux/ +docs/reference/*.stamp +docs/reference/*.bak +docs/reference/html +docs/reference/libplanfahr-*-decl-list.txt +docs/reference/libplanfahr-*-decl.txt +docs/reference/libplanfahr-*-undeclared.txt +docs/reference/libplanfahr-*-undocumented.txt +docs/reference/libplanfahr-*-unused.txt +docs/reference/libplanfahr-*.args +docs/reference/libplanfahr-*.hierarchy +docs/reference/libplanfahr-*.interfaces +docs/reference/libplanfahr-*.prerequisites +docs/reference/libplanfahr-*.signals +docs/reference/libplanfahr-*.types +docs/reference/tmpl +docs/reference/xml +m4/ +src/providers/tests/*.trs @@ -0,0 +1 @@ +Guido Günther <agx@sigxcpu.org> @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +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 and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, 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 library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete 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 distribute a copy of this License along with the +Library. + + 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 Library or any portion +of it, thus forming a work based on the Library, 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) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +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 Library, 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 Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you 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. + + If distribution of 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 satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be 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. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library 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. + + 9. 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 Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +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 with +this License. + + 11. 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 Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library 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 Library. + +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. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library 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. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser 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 Library +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 Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +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 + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "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 +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. 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 LIBRARY 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 +LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. 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. + + <one line to give the library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,27 @@ +# Tests # + +Running the tests + + make check + +To run a single test in more detail use e.g. + + gtester --verbose src/providers/tests/de-db + +# Debugging # +You can use the LPF_DEBUG variable to make libplanfahr write out the querry +results. E.g. + + LPF_DEBUG=provider ./run python examples/trip-query.py Gelsenkirchen Essen + +will put all queries to ~/.cache/libplanfahr/<provider>/. + +To build with extensive debugging information use + + ./configure --enable-debug + +# Coding Style # +* Use spaces not tabs +* indentation is 4 spaces by default +* Private methods don't use <obj_name> as prefix +* Public methods have "self" as first argument @@ -0,0 +1,370 @@ +Installation Instructions +************************* + +Copyright (C) 1994-1996, 1999-2002, 2004-2013 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 `<wchar.h>' 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..a47ea70 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,3 @@ +SUBDIRS = src data docs + +ACLOCAL_AMFLAGS = -I m4 @@ -0,0 +1,30 @@ +Libplanfahr is a GObject based library to query public transport inforation. It +(in principle) supports different providers although at the moment only one is +implemented. It features GObject introspection so you can use it from languages +like JavaScript and Python. + +The API is by no means stable yet. + +It depends on glib, libsoup, libxml2: + + apt-get install libsoup2.4-dev libglib2.0-dev libxml2-dev gobject-introspection + +To build from source use: + + ./autogen.sh + ./configure + make + +To install use + + make install + +To run from the compiled source code use + + ./run <program> + +for example + + ./run python examples/trip-query.py Gelsenkirchen Essen + +If you want to contribute to libplanfahr see the HACKING document. diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..74d59fe --- /dev/null +++ b/autogen.sh @@ -0,0 +1,21 @@ +#!/bin/sh +# Run this to generate all the initial makefiles, etc. + +srcdir=`dirname $0` +test -z "$srcdir" && srcdir=. +REQUIRED_AUTOMAKE_VERSION=1.9 +REQUIRED_INTLTOOL_VERSION=0.35.0 +PKG_NAME=planfahr + +(test -f $srcdir/configure.ac \ + && test -f $srcdir/src/libplanfahr.c) || { + echo -n "**Error**: Directory "\`$srcdir\'" does not look like the" + echo " top-level $PKG_NAME directory" + exit 1 +} + +which gnome-autogen.sh || { + echo "You need to install gnome-common from the GNOME git" + exit 1 +} +USE_GNOME2_MACROS=1 . gnome-autogen.sh diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..917607c --- /dev/null +++ b/configure.ac @@ -0,0 +1,103 @@ +AC_INIT(libplanfahr, 0.0.0) +AC_CONFIG_SRCDIR(src/libplanfahr.c) +AC_CONFIG_AUX_DIR([build-aux]) +AM_CONFIG_HEADER(config.h) +AC_CONFIG_MACRO_DIR([m4]) +dnl Make automake keep quiet about wildcards & other GNUmake-isms +AM_INIT_AUTOMAKE([1.11.1 subdir-objects no-dist-gzip dist-xz -Wno-portability]) +AC_CANONICAL_HOST + +AM_SILENT_RULES([yes]) + +GLIB2_REQUIRED=2.22.0 +GOBJECT2_REQUIRED=2.10.0 +GIO_REQUIRED=2.10.0 +GOBJECT_INTROSPECTION_REQUIRED=0.10.8 +LIBXML2_REQUIRED=2.0.0 +LIBSOUP_REQUIRED=2.42 + +LIBPLANFAHR_MAJOR_VERSION=`echo $VERSION | awk -F. '{print $1}'` +LIBPLANFAHR_MINOR_VERSION=`echo $VERSION | awk -F. '{print $2}'` +LIBPLANFAHR_MICRO_VERSION=`echo $VERSION | awk -F. '{print $3}'` +LIBPLANFAHR_VERSION=$LIBPLANFAHR_MAJOR_VERSION.$LIBPLANFAHR_MINOR_VERSION.$LIBPLANFAHR_MICRO_VERSION$LIBPLANFAHR_MICRO_VERSION_SUFFIX +LIBPLANFAHR_VERSION_INFO=`expr $LIBPLANFAHR_MAJOR_VERSION + $LIBPLANFAHR_MINOR_VERSION`:$LIBPLANFAHR_MICRO_VERSION:$LIBPLANFAHR_MINOR_VERSION +LIBPLANFAHR_VERSION_NUMBER=`expr $LIBPLANFAHR_MAJOR_VERSION \* 1000000 + $LIBPLANFAHR_MINOR_VERSION \* 1000 + $LIBPLANFAHR_MICRO_VERSION` + +AC_SUBST([LIBPLANFAHR_MAJOR_VERSION]) +AC_SUBST([LIBPLANFAHR_MINOR_VERSION]) +AC_SUBST([LIBPLANFAHR_MICRO_VERSION]) +AC_SUBST([LIBPLANFAHR_VERSION]) +AC_SUBST([LIBPLANFAHR_VERSION_INFO]) +AC_SUBST([LIBPLANFAHR_VERSION_NUMBER]) + +AC_PROG_CC +AM_PROG_CC_C_O + +AC_PROG_LIBTOOL + +LIBPLANFAHR_COMPILE_WARNINGS + +AC_DEFINE([_GNU_SOURCE], [], [Enable GNU extensions]) + +PKG_CHECK_MODULES(GLIB2, glib-2.0 >= $GLIB2_REQUIRED) +PKG_CHECK_MODULES(GTHREAD2, gthread-2.0 >= $GLIB2_REQUIRED) +PKG_CHECK_MODULES(GOBJECT2, gobject-2.0 >= $GLIB2_REQUIRED) +PKG_CHECK_MODULES(GIO2, gio-2.0 >= $GLIB2_REQUIRED) +PKG_CHECK_MODULES(LIBXML2, libxml-2.0 >= $LIBXML2_REQUIRED) +PKG_CHECK_MODULES(LIBSOUP, libsoup-2.4 >= $LIBSOUP_REQUIRED) + +AC_ARG_ENABLE([introspection], + AS_HELP_STRING([--enable-introspection], [enable GObject introspection]), + [], [enable_introspection=check]) + +if test "x$enable_introspection" != "xno" ; then + PKG_CHECK_MODULES([GOBJECT_INTROSPECTION], + [gobject-introspection-1.0 >= $GOBJECT_INTROSPECTION_REQUIRED], + [enable_introspection=yes], + [ + if test "x$enable_introspection" = "xcheck"; then + enable_introspection=no + else + AC_MSG_ERROR([gobject-introspection is not available]) + fi + ]) + if test "x$enable_introspection" = "xyes" ; then + AC_DEFINE([WITH_GOBJECT_INTROSPECTION], [1], [enable GObject introspection support]) + AC_SUBST(GOBJECT_INTROSPECTION_CFLAGS) + AC_SUBST(GOBJECT_INTROSPECTION_LIBS) + AC_SUBST([G_IR_SCANNER], [$($PKG_CONFIG --variable=g_ir_scanner gobject-introspection-1.0)]) + AC_SUBST([G_IR_COMPILER], [$($PKG_CONFIG --variable=g_ir_compiler gobject-introspection-1.0)]) + fi +fi +AM_CONDITIONAL([WITH_GOBJECT_INTROSPECTION], [test "x$enable_introspection" = "xyes"]) + + +GTK_DOC_CHECK([1.10]) + +dnl --enable-debug=(yes|no) +AC_ARG_ENABLE(debug, + AS_HELP_STRING([--enable-debug=no/yes],[enable debugging output]),[],[enable_debug=no]) +if test x"$enable_debug" = x"yes"; then + AC_DEFINE(ENABLE_DEBUG, 1, [whether debugging is enabled]) +fi + +# Setup GLIB_MKENUMS to use glib-mkenums even if GLib is uninstalled. +GLIB_MKENUMS=`$PKG_CONFIG --variable=glib_mkenums glib-2.0` +AC_SUBST(GLIB_MKENUMS) + +LPF_PROVIDERS_DIR="$libdir/libplanfahr/providers" +AC_SUBST(LPF_PROVIDERS_DIR) + +AC_CONFIG_FILES([run], + [chmod +x,-w run]) +AC_CONFIG_FILES(Makefile + src/Makefile + src/providers/Makefile + src/providers/tests/Makefile + data/Makefile + data/libplanfahr-0.0.pc + docs/Makefile + docs/reference/Makefile +) +AC_OUTPUT + diff --git a/data/Makefile.am b/data/Makefile.am new file mode 100644 index 0000000..76366ea --- /dev/null +++ b/data/Makefile.am @@ -0,0 +1,7 @@ +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = libplanfahr-0.0.pc + +EXTRA_DIST = $(pkgconfig_DATA:%.pc=%.pc.in) \ + $(NULL) + +DISTCLEAN_FILES = $(pkgconfig_DATA) diff --git a/data/libplanfahr-0.0.pc.in b/data/libplanfahr-0.0.pc.in new file mode 100644 index 0000000..67e27fa --- /dev/null +++ b/data/libplanfahr-0.0.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libplanfahr-0.0 +Version: @VERSION@ +Description: libplanfahr GObject library +Requires: gobject-2.0 libxml-2.0 +Libs: -L${libdir} -llibplanfahr-0.0 +Cflags: -I${includedir}/liblibplanfahr-0.0 diff --git a/docs/Makefile.am b/docs/Makefile.am new file mode 100644 index 0000000..f3ddc22 --- /dev/null +++ b/docs/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = reference diff --git a/docs/reference/Makefile.am b/docs/reference/Makefile.am new file mode 100644 index 0000000..0f77dbb --- /dev/null +++ b/docs/reference/Makefile.am @@ -0,0 +1,70 @@ +## Process this file with automake to produce Makefile.in +AUTOMAKE_OPTIONS = 1.6 + +# The name of the module +DOC_MODULE=libplanfahr-0.0 + +# The top-level SGML file. +DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.sgml + +# The directory containing the source code. Relative to $(srcdir). +# gtk-doc will search all .c & .h files beneath here for inline comments +# documenting functions and macros. +DOC_SOURCE_DIR=../../src + +# Extra options to supply to gtkdoc-scan. +SCAN_OPTIONS='--rebuild-types' + +# Extra options to supply to gtkdoc-scangobj. +SCANGOBJ_OPTIONS= + +# Extra options to supply to gtkdoc-mkdb. +MKDB_OPTIONS=--sgml-mode --output-format=xml + +# Extra options to supply to gtkdoc-fixref. +FIXXREF_OPTIONS= + +# Used for dependencies. +HFILE_GLOB= +CFILE_GLOB= + +# Header files to ignore when scanning. +IGNORE_HFILES = \ + planfahr.h \ + de-db.h \ + hafas-bin6.h \ + $(NULL) + +# Images to copy into HTML directory. +HTML_IMAGES = + +# Extra XML files that are included by $(DOC_MAIN_SGML_FILE). +content_files = \ + build-howto.xml \ + $(NULL) + +# Other files to distribute. +extra_files = + + +# CFLAGS and LDFLAGS for compiling scan program. Only needed +# if $(DOC_MODULE).types is non-empty. +GTKDOC_CFLAGS = \ + -I$(top_srcdir) \ + -I$(top_builddir) \ + $(GLIB_CFLAGS) \ + $(XML_CFLAGS) \ + $(NULL) + +GTKDOC_LIBS = \ + $(top_builddir)/src/libplanfahr-0.0.la \ + $(GLIB_LIBS) + +# include common portion ... +include $(top_srcdir)/gtk-doc.make + +# kludges +$(srcdir)/tmpl/*.sgml: + +clean: clean-am + rm -rf tmpl diff --git a/docs/reference/build-howto.xml b/docs/reference/build-howto.xml new file mode 100644 index 0000000..e0922a1 --- /dev/null +++ b/docs/reference/build-howto.xml @@ -0,0 +1,58 @@ +<?xml version="1.0"?> +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" + "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd"> +<refentry id="libplanfahr-build-howto"> +<refmeta> +<refentrytitle>Compiling with libplanfahr</refentrytitle> +<manvolnum>3</manvolnum> +<refmiscinfo>LIBPLANFAHR Library</refmiscinfo> +</refmeta> + +<refnamediv> +<refname>Compiling with libplanfahr</refname><refpurpose>Notes on compiling</refpurpose> +</refnamediv> + +<refsect2> +<title>Using pkg-config</title> + +<para> +Like other GNOME libraries, <application>libplanfahr</application> uses +<application>pkg-config</application> to provide compiler options. The +package name is "<literal>libplanfahr-0.0</literal>". So in your +<literal>configure</literal> script, you might specify something like: +</para> + +<informalexample><programlisting> +PKG_CHECK_MODULES(LIBPLANFAHR, [libplanfahr-0.0]) +AC_SUBST(LIBPLANFAHR_CFLAGS) +AC_SUBST(LIBPLANFAHR_LIBS) +</programlisting></informalexample> + +<para> +The "<literal>0.0</literal>" in the package name is the "API version" +(indicating "the version of the <application>libplanfahr</application> API +that first appeared in version 0.0") and is essentially just part of +the package name. +</para> + +</refsect2> + +<refsect2> +<title>Headers</title> + +<para> +Code using <application>libplanfahr</application> should do: +</para> + +<informalexample><programlisting> +#include <libplanfahr/planfahr.h> +</programlisting></informalexample> + +<para> +Including individual headers rather than <literal>planfahr.h</literal> is not +recommended. +</para> + +</refsect2> + +</refentry> diff --git a/docs/reference/libplanfahr-0.0-docs.sgml b/docs/reference/libplanfahr-0.0-docs.sgml new file mode 100644 index 0000000..db2b5f8 --- /dev/null +++ b/docs/reference/libplanfahr-0.0-docs.sgml @@ -0,0 +1,32 @@ +<?xml version="1.0"?> +<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" + "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd"> +<book id="index" xmlns:xi="http://www.w3.org/2001/XInclude"> + <bookinfo> + <title>libplanfahr Reference Manual</title> + </bookinfo> + + <chapter> + <title>Tutorial</title> + <xi:include href="build-howto.xml"/> + </chapter> + + <chapter> + <title>Core API</title> + <xi:include href="xml/lpf-loc.xml"/> + <xi:include href="xml/lpf-manager.xml"/> + <xi:include href="xml/lpf-stop.xml"/> + <xi:include href="xml/lpf-trip.xml"/> + <xi:include href="xml/lpf-trip-part.xml"/> + <xi:include href="xml/lpf-provider.xml"/> + </chapter> + + <index> + <title>Index</title> + </index> + + <xi:include href="xml/annotation-glossary.xml"> + <xi:fallback /> + </xi:include> + +</book> diff --git a/examples/trip-query.py b/examples/trip-query.py new file mode 100644 index 0000000..54b552f --- /dev/null +++ b/examples/trip-query.py @@ -0,0 +1,100 @@ +#!/usr/bin/python +# +# Querry current trips between the two locations +# given on the command line + +import sys + +from gi.repository import Lpf +from gi.repository import GObject +from gi.repository import GLib + +mainloop = None +start = None +end = None +provider = None + +def quit(error=None): + if error: + print("Error: %s" % error) + mainloop.quit() + + +def locs_cb(locs, userdata, err): + global start, end + + if err: + quit(err.message) + return + + if not start: + start = locs[0] + print("Start: %s" % start.props.name) + provider.get_locs(userdata, locs_cb, None) + else: + end = locs[0] + print("End: %s" % end.props.name) + now = GLib.DateTime.new_now_local() + provider.get_trips(start, end, now, 0, trips_cb, None) + + +def trips_cb(trips, userdata, err): + if err: + quit(err.message) + return + + if not trips: + raise Exception("Failed to find any trips") + i = 0 + for trip in trips: + i += 1 + j = 0 + print ('Trip #%d' % i) + for part in trip.props.parts: + j += 1 + print(' Part #%d' % j) + start = part.props.start + end = part.props.end + print(" Start: %s" % start.props.name) + print(" Departure: %s" % start.props.departure.format("%F %H:%M")) + if start.props.dep_plat: + print(" Platform: %s" % start.props.dep_plat) + print(" Delay: %s" % start.props.departure_delay) + print(" End: %s" % end.props.name) + print(" Arrival: %s" % end.props.arrival.format("%F %H:%M")) + if end.props.arr_plat: + print(" Platform: %s" % end.props.arr_plat) + print(" Line: %s" % part.props.line) + print(" Delay: %s" % end.props.arrival_delay) + if part.props.stops and len(part.props.stops) > 0: + print " %s Stops: %s" % (len(part.props.stops), + ", ".join([s.props.name for s in part.props.stops])) + else: + print " Stops: 0" + print("") + quit() + +def main(args): + global mainloop, provider + + if len(args) == 3: + start = args[1] + end = args[2] + else: + print("Usage: %s <from> <to>" % args[0]) + return 1 + + manager = Lpf.Manager() + provider = manager.activate_provider("de-db") + print("Loaded provider %s" % provider.props.name) + + provider.get_locs(start, locs_cb, end) + + mainloop = GObject.MainLoop() + GObject.timeout_add_seconds(20, quit, "timed out") + mainloop.run() + return 0 + +if __name__ == "__main__": + sys.exit(main(sys.argv)) + diff --git a/flymake.mk b/flymake.mk new file mode 100644 index 0000000..8228150 --- /dev/null +++ b/flymake.mk @@ -0,0 +1,12 @@ +get_cs_flags = $(foreach target,$(subst .,_,$(subst -,_,$($(2)))),$($(target)_$(1)FLAGS)) +get_cs_all_flags = $(foreach type,$(2),$(call get_cs_flags,$(1),$(type))) +get_cs_compile = $(if $(subst C,,$(1)),$($(1)COMPILE),$(COMPILE)) +get_cs_cmdline = $(call get_cs_compile,$(1)) $(call get_cs_all_flags,$(1),check_PROGRAMS bin_PROGRAMS lib_LTLIBRARIES) -fsyntax-only + +check-syntax: + s=$(suffix $(CHK_SOURCES));\ + if [ "$$s" = ".c" ]; then $(call get_cs_cmdline,C) $(CHK_SOURCES);\ + elif [ "$$s" = ".cpp" ]; then $(call get_cs_cmdline,CXX) $(CHK_SOURCES);\ + else exit 1; fi + +PHONY: check-syntax diff --git a/gtk-doc.make b/gtk-doc.make new file mode 100644 index 0000000..3b5bed0 --- /dev/null +++ b/gtk-doc.make @@ -0,0 +1,302 @@ +# -*- mode: makefile -*- + +#################################### +# Everything below here is generic # +#################################### + +if GTK_DOC_USE_LIBTOOL +GTKDOC_CC = $(LIBTOOL) --tag=CC --mode=compile $(CC) $(INCLUDES) $(GTKDOC_DEPS_CFLAGS) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +GTKDOC_LD = $(LIBTOOL) --tag=CC --mode=link $(CC) $(GTKDOC_DEPS_LIBS) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) +GTKDOC_RUN = $(LIBTOOL) --mode=execute +else +GTKDOC_CC = $(CC) $(INCLUDES) $(GTKDOC_DEPS_CFLAGS) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +GTKDOC_LD = $(CC) $(GTKDOC_DEPS_LIBS) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) +GTKDOC_RUN = +endif + +# We set GPATH here; this gives us semantics for GNU make +# which are more like other make's VPATH, when it comes to +# whether a source that is a target of one rule is then +# searched for in VPATH/GPATH. +# +GPATH = $(srcdir) + +TARGET_DIR=$(HTML_DIR)/$(DOC_MODULE) + +SETUP_FILES = \ + $(content_files) \ + $(DOC_MAIN_SGML_FILE) \ + $(DOC_MODULE)-sections.txt \ + $(DOC_MODULE)-overrides.txt + +EXTRA_DIST = \ + $(HTML_IMAGES) \ + $(SETUP_FILES) + +DOC_STAMPS=setup-build.stamp scan-build.stamp tmpl-build.stamp sgml-build.stamp \ + html-build.stamp pdf-build.stamp \ + tmpl.stamp sgml.stamp html.stamp pdf.stamp + +SCANOBJ_FILES = \ + $(DOC_MODULE).args \ + $(DOC_MODULE).hierarchy \ + $(DOC_MODULE).interfaces \ + $(DOC_MODULE).prerequisites \ + $(DOC_MODULE).signals + +REPORT_FILES = \ + $(DOC_MODULE)-undocumented.txt \ + $(DOC_MODULE)-undeclared.txt \ + $(DOC_MODULE)-unused.txt + +CLEANFILES = $(SCANOBJ_FILES) $(REPORT_FILES) $(DOC_STAMPS) + +if ENABLE_GTK_DOC +if GTK_DOC_BUILD_HTML +HTML_BUILD_STAMP=html-build.stamp +else +HTML_BUILD_STAMP= +endif +if GTK_DOC_BUILD_PDF +PDF_BUILD_STAMP=pdf-build.stamp +else +PDF_BUILD_STAMP= +endif + +all-local: $(HTML_BUILD_STAMP) $(PDF_BUILD_STAMP) +else +all-local: +endif + +docs: $(HTML_BUILD_STAMP) $(PDF_BUILD_STAMP) + +$(REPORT_FILES): sgml-build.stamp + +#### setup #### + +GTK_DOC_V_SETUP=$(GTK_DOC_V_SETUP_$(V)) +GTK_DOC_V_SETUP_=$(GTK_DOC_V_SETUP_$(AM_DEFAULT_VERBOSITY)) +GTK_DOC_V_SETUP_0=@echo " DOC Preparing build"; + +setup-build.stamp: + -$(GTK_DOC_V_SETUP)if test "$(abs_srcdir)" != "$(abs_builddir)" ; then \ + files=`echo $(SETUP_FILES) $(expand_content_files) $(DOC_MODULE).types`; \ + if test "x$$files" != "x" ; then \ + for file in $$files ; do \ + test -f $(abs_srcdir)/$$file && \ + cp -pu $(abs_srcdir)/$$file $(abs_builddir)/$$file || true; \ + done; \ + fi; \ + test -d $(abs_srcdir)/tmpl && \ + { cp -rp $(abs_srcdir)/tmpl $(abs_builddir)/; \ + chmod -R u+w $(abs_builddir)/tmpl; } \ + fi + $(AM_V_at)touch setup-build.stamp + +#### scan #### + +GTK_DOC_V_SCAN=$(GTK_DOC_V_SCAN_$(V)) +GTK_DOC_V_SCAN_=$(GTK_DOC_V_SCAN_$(AM_DEFAULT_VERBOSITY)) +GTK_DOC_V_SCAN_0=@echo " DOC Scanning header files"; + +GTK_DOC_V_INTROSPECT=$(GTK_DOC_V_INTROSPECT_$(V)) +GTK_DOC_V_INTROSPECT_=$(GTK_DOC_V_INTROSPECT_$(AM_DEFAULT_VERBOSITY)) +GTK_DOC_V_INTROSPECT_0=@echo " DOC Introspecting gobjects"; + +scan-build.stamp: $(HFILE_GLOB) $(CFILE_GLOB) + $(GTK_DOC_V_SCAN)_source_dir='' ; \ + for i in $(DOC_SOURCE_DIR) ; do \ + _source_dir="$${_source_dir} --source-dir=$$i" ; \ + done ; \ + gtkdoc-scan --module=$(DOC_MODULE) --ignore-headers="$(IGNORE_HFILES)" $${_source_dir} $(SCAN_OPTIONS) $(EXTRA_HFILES) + $(GTK_DOC_V_INTROSPECT)if grep -l '^..*$$' $(DOC_MODULE).types > /dev/null 2>&1 ; then \ + scanobj_options=""; \ + gtkdoc-scangobj 2>&1 --help | grep >/dev/null "\-\-verbose"; \ + if test "$(?)" = "0"; then \ + if test "x$(V)" = "x1"; then \ + scanobj_options="--verbose"; \ + fi; \ + fi; \ + CC="$(GTKDOC_CC)" LD="$(GTKDOC_LD)" RUN="$(GTKDOC_RUN)" CFLAGS="$(GTKDOC_CFLAGS) $(CFLAGS)" LDFLAGS="$(GTKDOC_LIBS) $(LDFLAGS)" \ + gtkdoc-scangobj $(SCANGOBJ_OPTIONS) $$scanobj_options --module=$(DOC_MODULE); \ + else \ + for i in $(SCANOBJ_FILES) ; do \ + test -f $$i || touch $$i ; \ + done \ + fi + $(AM_V_at)touch scan-build.stamp + +$(DOC_MODULE)-decl.txt $(SCANOBJ_FILES) $(DOC_MODULE)-sections.txt $(DOC_MODULE)-overrides.txt: scan-build.stamp + @true + +#### templates #### + +GTK_DOC_V_TMPL=$(GTK_DOC_V_TMPL_$(V)) +GTK_DOC_V_TMPL_=$(GTK_DOC_V_TMPL_$(AM_DEFAULT_VERBOSITY)) +GTK_DOC_V_TMPL_0=@echo " DOC Rebuilding template files"; + +tmpl-build.stamp: setup-build.stamp $(DOC_MODULE)-decl.txt $(SCANOBJ_FILES) $(DOC_MODULE)-sections.txt $(DOC_MODULE)-overrides.txt + $(GTK_DOC_V_TMPL)gtkdoc-mktmpl --module=$(DOC_MODULE) $(MKTMPL_OPTIONS) + $(AM_V_at)if test "$(abs_srcdir)" != "$(abs_builddir)" ; then \ + if test -w $(abs_srcdir) ; then \ + cp -rp $(abs_builddir)/tmpl $(abs_srcdir)/; \ + fi \ + fi + $(AM_V_at)touch tmpl-build.stamp + +tmpl.stamp: tmpl-build.stamp + @true + +$(srcdir)/tmpl/*.sgml: + @true + +#### xml #### + +GTK_DOC_V_XML=$(GTK_DOC_V_XML_$(V)) +GTK_DOC_V_XML_=$(GTK_DOC_V_XML_$(AM_DEFAULT_VERBOSITY)) +GTK_DOC_V_XML_0=@echo " DOC Building XML"; + +sgml-build.stamp: tmpl.stamp $(DOC_MODULE)-sections.txt $(srcdir)/tmpl/*.sgml $(expand_content_files) + $(GTK_DOC_V_XML)-chmod -R u+w $(srcdir) && _source_dir='' ; \ + for i in $(DOC_SOURCE_DIR) ; do \ + _source_dir="$${_source_dir} --source-dir=$$i" ; \ + done ; \ + gtkdoc-mkdb --module=$(DOC_MODULE) --output-format=xml --expand-content-files="$(expand_content_files)" --main-sgml-file=$(DOC_MAIN_SGML_FILE) $${_source_dir} $(MKDB_OPTIONS) + $(AM_V_at)touch sgml-build.stamp + +sgml.stamp: sgml-build.stamp + @true + +#### html #### + +GTK_DOC_V_HTML=$(GTK_DOC_V_HTML_$(V)) +GTK_DOC_V_HTML_=$(GTK_DOC_V_HTML_$(AM_DEFAULT_VERBOSITY)) +GTK_DOC_V_HTML_0=@echo " DOC Building HTML"; + +GTK_DOC_V_XREF=$(GTK_DOC_V_XREF_$(V)) +GTK_DOC_V_XREF_=$(GTK_DOC_V_XREF_$(AM_DEFAULT_VERBOSITY)) +GTK_DOC_V_XREF_0=@echo " DOC Fixing cross-references"; + +html-build.stamp: sgml.stamp $(DOC_MAIN_SGML_FILE) $(content_files) + $(GTK_DOC_V_HTML)rm -rf html && mkdir html && \ + mkhtml_options=""; \ + gtkdoc-mkhtml 2>&1 --help | grep >/dev/null "\-\-verbose"; \ + if test "$(?)" = "0"; then \ + if test "x$(V)" = "x1"; then \ + mkhtml_options="$$mkhtml_options --verbose"; \ + fi; \ + fi; \ + gtkdoc-mkhtml 2>&1 --help | grep >/dev/null "\-\-path"; \ + if test "$(?)" = "0"; then \ + mkhtml_options="$$mkhtml_options --path=\"$(abs_srcdir)\""; \ + fi; \ + cd html && gtkdoc-mkhtml $$mkhtml_options $(MKHTML_OPTIONS) $(DOC_MODULE) ../$(DOC_MAIN_SGML_FILE) + -@test "x$(HTML_IMAGES)" = "x" || \ + for file in $(HTML_IMAGES) ; do \ + if test -f $(abs_srcdir)/$$file ; then \ + cp $(abs_srcdir)/$$file $(abs_builddir)/html; \ + fi; \ + if test -f $(abs_builddir)/$$file ; then \ + cp $(abs_builddir)/$$file $(abs_builddir)/html; \ + fi; \ + done; + $(GTK_DOC_V_XREF)gtkdoc-fixxref --module=$(DOC_MODULE) --module-dir=html --html-dir=$(HTML_DIR) $(FIXXREF_OPTIONS) + $(AM_V_at)touch html-build.stamp + +#### pdf #### + +GTK_DOC_V_PDF=$(GTK_DOC_V_PDF_$(V)) +GTK_DOC_V_PDF_=$(GTK_DOC_V_PDF_$(AM_DEFAULT_VERBOSITY)) +GTK_DOC_V_PDF_0=@echo " DOC Building PDF"; + +pdf-build.stamp: sgml.stamp $(DOC_MAIN_SGML_FILE) $(content_files) + $(GTK_DOC_V_PDF)rm -f $(DOC_MODULE).pdf && \ + mkpdf_options=""; \ + gtkdoc-mkpdf 2>&1 --help | grep >/dev/null "\-\-verbose"; \ + if test "$(?)" = "0"; then \ + if test "x$(V)" = "x1"; then \ + mkpdf_options="$$mkpdf_options --verbose"; \ + fi; \ + fi; \ + if test "x$(HTML_IMAGES)" != "x"; then \ + for img in $(HTML_IMAGES); do \ + part=`dirname $$img`; \ + echo $$mkpdf_options | grep >/dev/null "\-\-imgdir=$$part "; \ + if test $$? != 0; then \ + mkpdf_options="$$mkpdf_options --imgdir=$$part"; \ + fi; \ + done; \ + fi; \ + gtkdoc-mkpdf --path="$(abs_srcdir)" $$mkpdf_options $(DOC_MODULE) $(DOC_MAIN_SGML_FILE) $(MKPDF_OPTIONS) + $(AM_V_at)touch pdf-build.stamp + +############## + +clean-local: + @rm -f *~ *.bak + @rm -rf .libs + +distclean-local: + @rm -rf xml html $(REPORT_FILES) $(DOC_MODULE).pdf \ + $(DOC_MODULE)-decl-list.txt $(DOC_MODULE)-decl.txt + @if test "$(abs_srcdir)" != "$(abs_builddir)" ; then \ + rm -f $(SETUP_FILES) $(expand_content_files) $(DOC_MODULE).types; \ + rm -rf tmpl; \ + fi + +maintainer-clean-local: + @rm -rf xml html + +install-data-local: + @installfiles=`echo $(builddir)/html/*`; \ + if test "$$installfiles" = '$(builddir)/html/*'; \ + then echo 1>&2 'Nothing to install' ; \ + else \ + if test -n "$(DOC_MODULE_VERSION)"; then \ + installdir="$(DESTDIR)$(TARGET_DIR)-$(DOC_MODULE_VERSION)"; \ + else \ + installdir="$(DESTDIR)$(TARGET_DIR)"; \ + fi; \ + $(mkinstalldirs) $${installdir} ; \ + for i in $$installfiles; do \ + echo ' $(INSTALL_DATA) '$$i ; \ + $(INSTALL_DATA) $$i $${installdir}; \ + done; \ + if test -n "$(DOC_MODULE_VERSION)"; then \ + mv -f $${installdir}/$(DOC_MODULE).devhelp2 \ + $${installdir}/$(DOC_MODULE)-$(DOC_MODULE_VERSION).devhelp2; \ + fi; \ + $(GTKDOC_REBASE) --relative --dest-dir=$(DESTDIR) --html-dir=$${installdir}; \ + fi + +uninstall-local: + @if test -n "$(DOC_MODULE_VERSION)"; then \ + installdir="$(DESTDIR)$(TARGET_DIR)-$(DOC_MODULE_VERSION)"; \ + else \ + installdir="$(DESTDIR)$(TARGET_DIR)"; \ + fi; \ + rm -rf $${installdir} + +# +# Require gtk-doc when making dist +# +if ENABLE_GTK_DOC +dist-check-gtkdoc: docs +else +dist-check-gtkdoc: + @echo "*** gtk-doc must be installed and enabled in order to make dist" + @false +endif + +dist-hook: dist-check-gtkdoc dist-hook-local + @mkdir $(distdir)/tmpl + @mkdir $(distdir)/html + @-cp ./tmpl/*.sgml $(distdir)/tmpl + @cp ./html/* $(distdir)/html + @-cp ./$(DOC_MODULE).pdf $(distdir)/ + @-cp ./$(DOC_MODULE).types $(distdir)/ + @-cp ./$(DOC_MODULE)-sections.txt $(distdir)/ + @cd $(distdir) && rm -f $(DISTCLEANFILES) + @$(GTKDOC_REBASE) --online --relative --html-dir=$(distdir)/html + +.PHONY : dist-hook-local docs diff --git a/m4/lpf-compile-warnings.m4 b/m4/lpf-compile-warnings.m4 new file mode 100644 index 0000000..626d380 --- /dev/null +++ b/m4/lpf-compile-warnings.m4 @@ -0,0 +1,132 @@ +dnl +dnl Enable all known GCC compiler warnings, except for those +dnl we can't yet cope with +dnl +dnl Copied from libvirt-glib +dnl +AC_DEFUN([LIBPLANFAHR_COMPILE_WARNINGS],[ + dnl ****************************** + dnl More compiler warnings + dnl ****************************** + + AC_ARG_ENABLE([werror], + AS_HELP_STRING([--enable-werror], [Use -Werror (if supported)]), + [set_werror="$enableval"], + [if test -d $srcdir/.git; then + is_git_version=true + set_werror=yes + else + set_werror=no + fi]) + + # List of warnings that are not relevant / wanted + + # Don't care about C++ compiler compat + dontwarn="$dontwarn -Wc++-compat" + dontwarn="$dontwarn -Wabi" + dontwarn="$dontwarn -Wdeprecated" + # Don't care about ancient C standard compat + dontwarn="$dontwarn -Wtraditional" + # Don't care about ancient C standard compat + dontwarn="$dontwarn -Wtraditional-conversion" + # Ignore warnings in /usr/include + dontwarn="$dontwarn -Wsystem-headers" + # Happy for compiler to add struct padding + dontwarn="$dontwarn -Wpadded" + # GCC very confused with -O2 + dontwarn="$dontwarn -Wunreachable-code" + # We explicitly need to remove const sometimes + dontwarn="$dontwarn -Wcast-qual" + # Allow vars decl in the middle of blocks + dontwarn="$dontwarn -Wdeclaration-after-statement" + # Using long long is fine + dontwarn="$dontwarn -Wlong-long" + # Unused macros are ok + dontwarn="$dontwarn -Wunused-macros" + + + # g_clear_object & G_ATOMIC_OP_USE_GCC_BUILTINS causes + # violations with this. XXX Fix glib ? + dontwarn="$dontwarn -Wbad-function-cast" + + # Get all possible GCC warnings + gl_MANYWARN_ALL_GCC([maybewarn]) + + # Remove the ones we don't want, blacklisted earlier + gl_MANYWARN_COMPLEMENT([wantwarn], [$maybewarn], [$dontwarn]) + + # Check for $CC support of each warning + for w in $wantwarn; do + gl_WARN_ADD([$w]) + done + + # GNULIB uses '-W' (aka -Wextra) which includes a bunch of stuff. + # Unfortunately, this means you can't simply use '-Wsign-compare' + # with gl_MANYWARN_COMPLEMENT + # So we have -W enabled, and then have to explicitly turn off... + gl_WARN_ADD([-Wno-sign-compare]) + + # Due to gutils.h bug in g_bit_storage + gl_WARN_ADD([-Wno-sign-conversion]) + gl_WARN_ADD([-Wno-conversion]) + gl_WARN_ADD([-Wno-unused-parameter]) + # We can't enable this due to horrible spice_usb_device_get_description + # signature + gl_WARN_ADD([-Wno-format-nonliteral]) + + + + # GNULIB expects this to be part of -Wc++-compat, but we turn + # that one off, so we need to manually enable this again + gl_WARN_ADD([-Wjump-misses-init]) + + # This should be < 256 really. Currently we're down to 4096, + # but using 1024 bytes sized buffers (mostly for virStrerror) + # stops us from going down further + gl_WARN_ADD([-Wframe-larger-than=4096]) + + # Use improved glibc headers + AH_VERBATIM([FORTIFY_SOURCE], + [/* Enable compile-time and run-time bounds-checking, and some warnings, + without upsetting newer glibc. */ + #if !defined _FORTIFY_SOURCE && defined __OPTIMIZE__ && __OPTIMIZE__ + # define _FORTIFY_SOURCE 2 + #endif + ]) + + # Extra special flags + dnl -fstack-protector stuff passes gl_WARN_ADD with gcc + dnl on Mingw32, but fails when actually used + case $host in + *-*-linux*) + dnl Fedora only uses -fstack-protector, but doesn't seem to + dnl be great overhead in adding -fstack-protector-all instead + dnl gl_WARN_ADD([-fstack-protector]) + gl_WARN_ADD([-fstack-protector-all]) + gl_WARN_ADD([--param=ssp-buffer-size=4]) + ;; + esac + gl_WARN_ADD([-fexceptions]) + gl_WARN_ADD([-fasynchronous-unwind-tables]) + gl_WARN_ADD([-fdiagnostics-show-option]) + gl_WARN_ADD([-funit-at-a-time]) + + # Need -fipa-pure-const in order to make -Wsuggest-attribute=pure + # fire even without -O. + gl_WARN_ADD([-fipa-pure-const]) + + # We should eventually enable this, but right now there are at + # least 75 functions triggering warnings. + gl_WARN_ADD([-Wno-suggest-attribute=pure]) + gl_WARN_ADD([-Wno-suggest-attribute=const]) + + + if test "$set_werror" = "yes" + then + gl_WARN_ADD([-Werror]) + fi + + WARN_LDFLAGS=$WARN_CFLAGS + AC_SUBST([WARN_CFLAGS]) + AC_SUBST([WARN_LDFLAGS]) +]) diff --git a/m4/manywarnings.m4 b/m4/manywarnings.m4 new file mode 100644 index 0000000..fd0e372 --- /dev/null +++ b/m4/manywarnings.m4 @@ -0,0 +1,184 @@ +# manywarnings.m4 serial 3 +dnl Copyright (C) 2008-2012 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Simon Josefsson + +# gl_MANYWARN_COMPLEMENT(OUTVAR, LISTVAR, REMOVEVAR) +# -------------------------------------------------- +# Copy LISTVAR to OUTVAR except for the entries in REMOVEVAR. +# Elements separated by whitespace. In set logic terms, the function +# does OUTVAR = LISTVAR \ REMOVEVAR. +AC_DEFUN([gl_MANYWARN_COMPLEMENT], +[ + gl_warn_set= + set x $2; shift + for gl_warn_item + do + case " $3 " in + *" $gl_warn_item "*) + ;; + *) + gl_warn_set="$gl_warn_set $gl_warn_item" + ;; + esac + done + $1=$gl_warn_set +]) + +# gl_MANYWARN_ALL_GCC(VARIABLE) +# ----------------------------- +# Add all documented GCC warning parameters to variable VARIABLE. +# Note that you need to test them using gl_WARN_ADD if you want to +# make sure your gcc understands it. +AC_DEFUN([gl_MANYWARN_ALL_GCC], +[ + dnl First, check if -Wno-missing-field-initializers is needed. + dnl -Wmissing-field-initializers is implied by -W, but that issues + dnl warnings with GCC version before 4.7, for the common idiom + dnl of initializing types on the stack to zero, using { 0, } + AC_REQUIRE([AC_PROG_CC]) + if test -n "$GCC"; then + + dnl First, check -W -Werror -Wno-missing-field-initializers is supported + dnl with the current $CC $CFLAGS $CPPFLAGS. + AC_MSG_CHECKING([whether -Wno-missing-field-initializers is supported]) + AC_CACHE_VAL([gl_cv_cc_nomfi_supported], [ + gl_save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -W -Werror -Wno-missing-field-initializers" + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[]], [[]])], + [gl_cv_cc_nomfi_supported=yes], + [gl_cv_cc_nomfi_supported=no]) + CFLAGS="$gl_save_CFLAGS"]) + AC_MSG_RESULT([$gl_cv_cc_nomfi_supported]) + + if test "$gl_cv_cc_nomfi_supported" = yes; then + dnl Now check whether -Wno-missing-field-initializers is needed + dnl for the { 0, } construct. + AC_MSG_CHECKING([whether -Wno-missing-field-initializers is needed]) + AC_CACHE_VAL([gl_cv_cc_nomfi_needed], [ + gl_save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -W -Werror" + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [[void f (void) + { + typedef struct { int a; int b; } s_t; + s_t s1 = { 0, }; + } + ]], + [[]])], + [gl_cv_cc_nomfi_needed=no], + [gl_cv_cc_nomfi_needed=yes]) + CFLAGS="$gl_save_CFLAGS" + ]) + AC_MSG_RESULT([$gl_cv_cc_nomfi_needed]) + fi + fi + + gl_manywarn_set= + for gl_manywarn_item in \ + -Wall \ + -W \ + -Wformat-y2k \ + -Wformat-nonliteral \ + -Wformat-security \ + -Winit-self \ + -Wmissing-include-dirs \ + -Wswitch-default \ + -Wswitch-enum \ + -Wunused \ + -Wunknown-pragmas \ + -Wstrict-aliasing \ + -Wstrict-overflow \ + -Wsystem-headers \ + -Wfloat-equal \ + -Wtraditional \ + -Wtraditional-conversion \ + -Wdeclaration-after-statement \ + -Wundef \ + -Wshadow \ + -Wunsafe-loop-optimizations \ + -Wpointer-arith \ + -Wbad-function-cast \ + -Wc++-compat \ + -Wcast-qual \ + -Wcast-align \ + -Wwrite-strings \ + -Wconversion \ + -Wsign-conversion \ + -Wlogical-op \ + -Waggregate-return \ + -Wstrict-prototypes \ + -Wold-style-definition \ + -Wmissing-prototypes \ + -Wmissing-declarations \ + -Wmissing-noreturn \ + -Wmissing-format-attribute \ + -Wpacked \ + -Wpadded \ + -Wredundant-decls \ + -Wnested-externs \ + -Wunreachable-code \ + -Winline \ + -Winvalid-pch \ + -Wlong-long \ + -Wvla \ + -Wvolatile-register-var \ + -Wdisabled-optimization \ + -Wstack-protector \ + -Woverlength-strings \ + -Wbuiltin-macro-redefined \ + -Wmudflap \ + -Wpacked-bitfield-compat \ + -Wsync-nand \ + ; do + gl_manywarn_set="$gl_manywarn_set $gl_manywarn_item" + done + # The following are not documented in the manual but are included in + # output from gcc --help=warnings. + for gl_manywarn_item in \ + -Wattributes \ + -Wcoverage-mismatch \ + -Wmultichar \ + -Wunused-macros \ + ; do + gl_manywarn_set="$gl_manywarn_set $gl_manywarn_item" + done + # More warnings from gcc 4.6.2 --help=warnings. + for gl_manywarn_item in \ + -Wabi \ + -Wcpp \ + -Wdeprecated \ + -Wdeprecated-declarations \ + -Wdiv-by-zero \ + -Wdouble-promotion \ + -Wendif-labels \ + -Wextra \ + -Wformat-contains-nul \ + -Wformat-extra-args \ + -Wformat-zero-length \ + -Wformat=2 \ + -Wmultichar \ + -Wnormalized=nfc \ + -Woverflow \ + -Wpointer-to-int-cast \ + -Wpragmas \ + -Wsuggest-attribute=const \ + -Wsuggest-attribute=noreturn \ + -Wsuggest-attribute=pure \ + -Wtrampolines \ + ; do + gl_manywarn_set="$gl_manywarn_set $gl_manywarn_item" + done + + # Disable the missing-field-initializers warning if needed + if test "$gl_cv_cc_nomfi_needed" = yes; then + gl_manywarn_set="$gl_manywarn_set -Wno-missing-field-initializers" + fi + + $1=$gl_manywarn_set +]) diff --git a/m4/warnings.m4 b/m4/warnings.m4 new file mode 100644 index 0000000..69d05a6 --- /dev/null +++ b/m4/warnings.m4 @@ -0,0 +1,37 @@ +# warnings.m4 serial 5 +dnl Copyright (C) 2008-2012 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Simon Josefsson + +# gl_AS_VAR_APPEND(VAR, VALUE) +# ---------------------------- +# Provide the functionality of AS_VAR_APPEND if Autoconf does not have it. +m4_ifdef([AS_VAR_APPEND], +[m4_copy([AS_VAR_APPEND], [gl_AS_VAR_APPEND])], +[m4_define([gl_AS_VAR_APPEND], +[AS_VAR_SET([$1], [AS_VAR_GET([$1])$2])])]) + +# gl_WARN_ADD(PARAMETER, [VARIABLE = WARN_CFLAGS]) +# ------------------------------------------------ +# Adds parameter to WARN_CFLAGS if the compiler supports it. For example, +# gl_WARN_ADD([-Wparentheses]). +AC_DEFUN([gl_WARN_ADD], +dnl FIXME: gl_Warn must be used unquoted until we can assume +dnl autoconf 2.64 or newer. +[AS_VAR_PUSHDEF([gl_Warn], [gl_cv_warn_$1])dnl +AC_CACHE_CHECK([whether compiler handles $1], m4_defn([gl_Warn]), [ + gl_save_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="${CPPFLAGS} $1" + AC_PREPROC_IFELSE([AC_LANG_PROGRAM([])], + [AS_VAR_SET(gl_Warn, [yes])], + [AS_VAR_SET(gl_Warn, [no])]) + CPPFLAGS="$gl_save_CPPFLAGS" +]) +AS_VAR_IF(gl_Warn, [yes], + [gl_AS_VAR_APPEND(m4_if([$2], [], [[WARN_CFLAGS]], [[$2]]), [" $1"])]) +AS_VAR_POPDEF([gl_Warn])dnl +m4_ifval([$2], [AS_LITERAL_IF([$2], [AC_SUBST([$2])], [])])dnl +]) @@ -0,0 +1,9 @@ +#!/bin/sh + +ABS_BUILDDIR='@abs_builddir@' + +export GI_TYPELIB_PATH="${ABS_BUILDDIR}/src/:$GI_TYPELIB_PATH" +export LD_LIBRARY_PATH="${ABS_BUILDDIR}/src/.libs:$LD_LIBRARY_PATH" +export LPF_PROVIDERS="${ABS_BUILDDIR}/src/providers/.libs" + +exec $@ diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..da5878c --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,100 @@ +include $(top_srcdir)/flymake.mk + +SUBDIRS = providers + +EXTRA_DIST = libplanfahr.sym + +lib_LTLIBRARIES = libplanfahr-0.0.la + +PLANFAHR_HEADER_FILES = \ + libplanfahr.h \ + lpf-manager.h \ + lpf-priv.h \ + lpf-provider.h \ + lpf-loc.h \ + lpf-stop.h \ + lpf-trip.h \ + lpf-trip-part.h \ + $(NULL) + +PLANFAHR_SOURCE_FILES = \ + libplanfahr.c \ + lpf-manager.c \ + lpf-provider.c \ + lpf-loc.c \ + lpf-stop.c \ + lpf-trip.c \ + lpf-trip-part.c \ + $(NULL) + +libplanfahr_0_0_ladir = $(includedir)/libplanfahr/ +libplanfahr_0_0_la_HEADERS = \ + $(PLANFAHR_HEADER_FILES) \ + $(NULL) + +libplanfahr_0_0_la_SOURCES = \ + $(PLANFAHR_SOURCE_FILES) \ + $(NULL) + +libplanfahr_0_0_la_CFLAGS = \ + -DG_LOG_DOMAIN="\"LibPlanFahr\"" \ + -DLPF_PROVIDERS_DIR="\"$(LPF_PROVIDERS_DIR)\"" \ + -I$(top_srcdir) \ + $(GIO2_CFLAGS) \ + $(GOBJECT2_CFLAGS) \ + $(GTHREAD2_CFLAGS) \ + $(WARN_CFLAGS) \ + $(NULL) + +libplanfahr_0_0_la_LIBADD = \ + $(GIO2_LIBS) \ + $(GOBJECT2_LIBS) \ + $(GTHREAD2_LIBS) + $(NULL) + +libplanfahr_0_0_la_DEPENDENCIES = \ + libplanfahr.sym \ + $(NULL) + +libplanfahr_0_0_la_LDFLAGS = \ + -Wl,--version-script=$(srcdir)/libplanfahr.sym \ + -version-info $(LIBPLANFAHR_VERSION_INFO) + +if WITH_GOBJECT_INTROSPECTION + +Lpf-0.0.gir: libplanfahr-0.0.la $(G_IR_SCANNER) Makefile.am + $(AM_V_GEN)$(G_IR_SCANNER) \ + --quiet \ + --warn-all \ + --namespace Lpf \ + --nsversion 0.0 \ + --include GObject-2.0 \ + --include Gio-2.0 \ + --symbol-prefix=lpf \ + --library=$(builddir)/libplanfahr-0.0.la \ + --output $@ \ + -I$(top_builddir) \ + -I$(top_srcdir) \ + --verbose \ + --c-include="libplanfahr/libplanfahr.h" \ + --pkg=gthread-2.0 \ + --pkg-export=libplanfahr-0.0 \ + $(srcdir)/libplanfahr.h \ + $(PLANFAHR_SOURCE_FILES:%=$(srcdir)/%) \ + $(PLANFAHR_HEADER_FILES:%=$(srcdir)/%) + +girdir = $(datadir)/gir-1.0 +gir_DATA = Lpf-0.0.gir + +typelibsdir = $(libdir)/girepository-1.0 +typelibs_DATA = Lpf-0.0.typelib + +%.typelib: %.gir + $(AM_V_GEN)$(G_IR_COMPILER) \ + --includedir=$(builddir) \ + --includedir=$(girdir) \ + -o $@ $< + +CLEANFILES = $(gir_DATA) $(typelibs_DATA) + +endif # WITH_GOBJECT_INTROSPECTION diff --git a/src/libplanfahr.c b/src/libplanfahr.c new file mode 100644 index 0000000..219f532 --- /dev/null +++ b/src/libplanfahr.c @@ -0,0 +1,29 @@ +/* + * libplanfahr.c: access public transport time tables + * + * Copyright (C) 2014 Guido Günther + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Guido Günther <agx@sigxcpu.org> + */ + +#ifndef _LIBPLANFAHR +#define _LIBPLANFAHR + +#include "lpf-manager.h" +#include "lpf-provider.h" + +#endif /* _LIBPLANFAHR */ diff --git a/src/libplanfahr.h b/src/libplanfahr.h new file mode 100644 index 0000000..8837115 --- /dev/null +++ b/src/libplanfahr.h @@ -0,0 +1,25 @@ +/* + * libplanfahr.h: access public transport time tables + * + * Copyright (C) 2014 Guido Günther + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Guido Günther <agx@sigxcpu.org> + */ + + +#include <config.h> + diff --git a/src/libplanfahr.sym b/src/libplanfahr.sym new file mode 100644 index 0000000..c9b3d47 --- /dev/null +++ b/src/libplanfahr.sym @@ -0,0 +1,7 @@ +LIBPLANFAHR_0.0.0 { + global: + lpf_*; + local: + *; +}; + diff --git a/src/lpf-loc.c b/src/lpf-loc.c new file mode 100644 index 0000000..3e2aaac --- /dev/null +++ b/src/lpf-loc.c @@ -0,0 +1,196 @@ +/* + * lpf-loc.c: a public transport location + * + * Copyright (C) 2014 Guido Günther + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Guido Günther <agx@sigxcpu.org> + */ + +#include <glob.h> +#include <string.h> +#include <math.h> + +#include <glib.h> +#include <glib/gprintf.h> +#include <gmodule.h> + +#include "lpf-loc.h" +#include "lpf-priv.h" + +enum { + LPF_LOC_PROP_0 = 0, + LPF_LOC_PROP_NAME, + LPF_LOC_PROP_LONG, + LPF_LOC_PROP_LAT, +}; + +/** + * SECTION:lpf-loc + * @short_description: Location + * + * A #LpfLoc represents a location (e.g. a station) + */ + +G_DEFINE_TYPE (LpfLoc, lpf_loc, G_TYPE_OBJECT) +#define GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), LPF_TYPE_LOC, LpfLocPrivate)) + +typedef struct _LpfLocPrivate LpfLocPrivate; +struct _LpfLocPrivate { + gchar *name; + gdouble long_; + gdouble lat; + gpointer opaque; +}; + +static void +lpf_loc_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + LpfLoc *self = LPF_LOC (object); + LpfLocPrivate *priv = GET_PRIVATE(self); + + switch (property_id) { + case LPF_LOC_PROP_NAME: + g_free (priv->name); + priv->name = g_value_dup_string (value); + break; + + case LPF_LOC_PROP_LONG: + priv->long_ = g_value_get_double(value); + break; + + case LPF_LOC_PROP_LAT: + priv->lat = g_value_get_double(value); + break; + + default: + /* We don't have any other property... */ + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + + +static void +lpf_loc_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + LpfLoc *self = LPF_LOC (object); + LpfLocPrivate *priv = GET_PRIVATE(self); + + switch (property_id) { + case LPF_LOC_PROP_NAME: + g_value_set_string (value, priv->name); + break; + + case LPF_LOC_PROP_LONG: + g_value_set_double (value, priv->long_); + break; + + case LPF_LOC_PROP_LAT: + g_value_set_double (value, priv->lat); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +lpf_loc_dispose(GObject *object) +{ + GObjectClass *parent_class = G_OBJECT_CLASS (lpf_loc_parent_class); + + if (parent_class->dispose != NULL) + parent_class->dispose (object); +} + + +static void +lpf_loc_finalize (GObject *object) +{ + LpfLoc *self = LPF_LOC (object); + LpfLocPrivate *priv = GET_PRIVATE (self); + GObjectClass *parent_class = G_OBJECT_CLASS (lpf_loc_parent_class); + + g_free (priv->name); + g_free (priv->opaque); + if (parent_class->finalize != NULL) + parent_class->finalize (object); +} + + +static void +lpf_loc_class_init (LpfLocClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GParamSpec *pspec; + + object_class->dispose = lpf_loc_dispose; + object_class->finalize = lpf_loc_finalize; + g_type_class_add_private (klass, sizeof (LpfLocPrivate)); + + object_class->set_property = lpf_loc_set_property; + object_class->get_property = lpf_loc_get_property; + + pspec = g_param_spec_string ("name", + "Name", + "Get Location name", + "", G_PARAM_CONSTRUCT | G_PARAM_READWRITE); + g_object_class_install_property (object_class, LPF_LOC_PROP_NAME, pspec); + + pspec = g_param_spec_double ("long", + "Longitude", + "Get Location Longitude", + -180.0, +180.0, 0, + G_PARAM_CONSTRUCT | G_PARAM_READWRITE); + g_object_class_install_property (object_class, LPF_LOC_PROP_LONG, pspec); + + pspec = g_param_spec_double ("lat", + "latitude", + "Get Location Latitude", + -90.0, +90.0, 0, + G_PARAM_CONSTRUCT | G_PARAM_READWRITE); + g_object_class_install_property (object_class, LPF_LOC_PROP_LAT, pspec); +} + +static void +lpf_loc_init (LpfLoc *self) +{ +} + +gpointer +lpf_loc_get_opaque(LpfLoc *self) +{ + LpfLocPrivate *priv = GET_PRIVATE (self); + + return priv->opaque; +} + +gpointer +lpf_loc_set_opaque(LpfLoc *self, gpointer opaque) +{ + LpfLocPrivate *priv = GET_PRIVATE (self); + + return priv->opaque = opaque; +} diff --git a/src/lpf-loc.h b/src/lpf-loc.h new file mode 100644 index 0000000..fa1b15f --- /dev/null +++ b/src/lpf-loc.h @@ -0,0 +1,62 @@ +/* + * lpf-loc.h: a public transport location + * + * Copyright (C) 2014 Guido Günther + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Guido Günther <agx@sigxcpu.org> + */ + +#ifndef _LPF_LOC_H +#define _LPF_LOC_H + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define LPF_TYPE_LOC lpf_loc_get_type() + +#define LPF_LOC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), LPF_TYPE_LOC, LpfLoc)) + +#define LPF_LOC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), LPF_TYPE_LOC, LpfLocClass)) + +#define LPF_IS_LOC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LPF_TYPE_LOC)) + +#define LPF_IS_LOC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), LPF_TYPE_LOC)) + +#define LPF_LOC_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), LPF_TYPE_LOC, LpfLocClass)) + +typedef struct { + GObject parent; +} LpfLoc; + +typedef struct { + GObjectClass parent_class; +} LpfLocClass; + +GType lpf_loc_get_type (void); +/* FIXME: Only used by providers, don't export symbol */ +gpointer lpf_loc_get_opaque (LpfLoc *self); +gpointer lpf_loc_set_opaque (LpfLoc *self, gpointer opaque); + +G_END_DECLS + +#endif /* _LPF_LOC_H */ diff --git a/src/lpf-manager.c b/src/lpf-manager.c new file mode 100644 index 0000000..e2692d2 --- /dev/null +++ b/src/lpf-manager.c @@ -0,0 +1,273 @@ +/* + * lpf-manager.h: manage planfahr providers + * + * Copyright (C) 2014 Guido Günther + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Guido Günther <agx@sigxcpu.org> + */ + +#include "lpf-provider.h" +#include "lpf-manager.h" +#include "lpf-priv.h" + +#include <glob.h> +#include <string.h> + +#include <glib.h> +#include <glib/gprintf.h> +#include <gmodule.h> + +#define PROVIDER_LIBBASE "libplanfahr-provider-" + + +/** + * SECTION:lpf-manager + * @short_description: Provider Manager + * @see_also: #LpfProvider + * + * A #LpfManager handles the different public transport information providers. + */ + + +G_DEFINE_TYPE (LpfManager, lpf_manager, G_TYPE_OBJECT) +#define GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), LPF_TYPE_MANAGER, LpfManagerPrivate)) + +typedef struct _LpfManagerPrivate LpfManagerPrivate; +struct _LpfManagerPrivate { + GSList *available; /* available providers */ + GSList *active; /* active providers */ +}; + + +GQuark +lpf_manager_error_quark (void) +{ + return g_quark_from_static_string ("lpf-manager-error-quark"); +} + + +static gchar *plugin_path(const char *name) +{ + gchar *dir = NULL; + gchar *prefix = NULL; + gchar *ret; + + dir = g_strdup(g_getenv(LPF_PROVIDERS_ENV)); + if (!dir) + dir = g_strdup(LPF_PROVIDERS_DIR); + prefix = g_strjoin("/", dir, PROVIDER_LIBBASE, NULL); + ret = g_strjoin(NULL, prefix, name, ".", G_MODULE_SUFFIX, NULL); + + g_free(dir); + g_free(prefix); + return ret; +} + +/** + * lpf_manager_get_available_providers: + * Returns: (transfer full): List of available providers + */ +GStrv lpf_manager_get_available_providers(void) +{ + gint i, prefix_len; + glob_t globbuf; + gchar *pattern = NULL; + GStrv providers = NULL; + gchar **tmp; + + if (!g_module_supported ()) { + g_warning ("GModules are not supported on your platform!"); + return NULL; + } + + pattern = plugin_path("*"); + prefix_len = 1 + g_strrstr(pattern, "/") + strlen(PROVIDER_LIBBASE) - pattern ; + LPF_DEBUG("Looking for providers in %s", pattern); + if (glob(pattern, GLOB_NOSORT, NULL, &globbuf)) + goto out; + + if (!(providers = g_try_malloc (sizeof(gchar) * globbuf.gl_pathc + 1))) { + goto out; + } + + for (i = 0; i < globbuf.gl_pathc; i++) { + tmp = g_strsplit(globbuf.gl_pathv[i] + prefix_len, ".", 2); + if (!tmp) { + providers[i] = NULL; + goto out; + } + providers[i] = g_strdup(tmp[0]); + g_strfreev(tmp); + } + providers[globbuf.gl_pathc] = NULL; + + out: + globfree(&globbuf); + g_free (pattern); + return providers; +} + +static LpfProvider* +load_provider (const char *path, GError **error) +{ + LpfProvider *provider = NULL; + GModule *module; + LpfProviderCreateFunc create_func; + int *major_provider_version, *minor_provider_version; + + module = g_module_open (path, G_MODULE_BIND_LAZY); + if (!module) { + g_set_error (error, + LPF_MANAGER_ERROR, + LPF_MANAGER_ERROR_ACTIVATION_FAILED, + "Could not load provider %s: %s", path, g_module_error ()); + return NULL; + } + + if (!g_module_symbol (module, "lpf_provider_major_version",(gpointer *) &major_provider_version)) { + g_set_error (error, + LPF_MANAGER_ERROR, + LPF_MANAGER_ERROR_ACTIVATION_FAILED, + "Could not load provider %s: Missing major version info", path); + goto out; + } + + if (*major_provider_version != LPF_PROVIDER_MAJOR_VERSION) { + g_set_error (error, + LPF_MANAGER_ERROR, + LPF_MANAGER_ERROR_ACTIVATION_FAILED, + "Could not load provider %s: Provider major version %d, %d is required", + path, *major_provider_version, LPF_PROVIDER_MAJOR_VERSION); + goto out; + } + + if (!g_module_symbol (module, "lpf_provider_minor_version", (gpointer *) &minor_provider_version)) { + g_set_error (error, + LPF_MANAGER_ERROR, + LPF_MANAGER_ERROR_ACTIVATION_FAILED, + "Could not load provider %s: Missing minor version info", path); + goto out; + } + + if (*minor_provider_version != LPF_PROVIDER_MINOR_VERSION) { + g_set_error (error, + LPF_MANAGER_ERROR, + LPF_MANAGER_ERROR_ACTIVATION_FAILED, + "Could not load provider %s: Provider minor version %d, %d is required", + path, *minor_provider_version, LPF_PROVIDER_MINOR_VERSION); + goto out; + } + + if (!g_module_symbol (module, "lpf_provider_create", (gpointer *) &create_func)) { + g_set_error (error, + LPF_MANAGER_ERROR, + LPF_MANAGER_ERROR_ACTIVATION_FAILED, + "Could not load provider %s: %s", path, g_module_error ()); + goto out; + } + + provider = (*create_func) (); + if (provider) { + g_object_weak_ref (G_OBJECT (provider), (GWeakNotify) g_module_close, module); + g_message ("Loaded provider %s", lpf_provider_get_name (provider)); + } else { + g_set_error (error, + LPF_MANAGER_ERROR, + LPF_MANAGER_ERROR_ACTIVATION_FAILED, + "Could not load provider %s: initialization failed", path); + } + out: + if (!provider) + g_module_close (module); + + return provider; +} + +/** + * lpf_manager_activate_provider: + * @self: a #LpfManager + * @name: the name of the provider to activate + * @error: a #GError for errorreporting or %NULL + * + * Activate the given provider + * + * Returns: (transfer none): The activated #LpfProvider + */ +LpfProvider *lpf_manager_activate_provider(LpfManager *self, + const gchar *name, + GError **error) +{ + gchar *path; + LpfProvider *provider; + LpfManagerPrivate *priv = GET_PRIVATE (self); + + path = plugin_path(name); + provider = load_provider (path, error); + if (provider) { + lpf_provider_activate(provider, G_OBJECT(self)); + priv->active = g_slist_prepend (priv->active, provider); + } + g_free(path); + return provider; +} + +static void +deactivate_provider(gpointer provider, gpointer user_data) +{ + LpfManager *manager = LPF_MANAGER (user_data); + lpf_manager_deactivate_provider (manager, provider); +} + +void +lpf_manager_deactivate_provider(LpfManager *self, LpfProvider *provider) +{ + LPF_DEBUG ("Deactivating provider %s", lpf_provider_get_name (provider)); + lpf_provider_deactivate (provider, G_OBJECT(self)); +} + +static void +lpf_manager_dispose(GObject *object) +{ + LpfManager *self = LPF_MANAGER(object); + LpfManagerPrivate *priv = GET_PRIVATE (self); + GObjectClass *parent_class = G_OBJECT_CLASS (lpf_manager_parent_class); + + /* We need to do this before dropping the ref on applet */ + g_slist_foreach (priv->active, deactivate_provider, self); + g_slist_free (priv->active); + + if (parent_class->dispose != NULL) + parent_class->dispose (object); +} + +static void +lpf_manager_class_init (LpfManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = lpf_manager_dispose; + g_type_class_add_private (klass, sizeof (LpfManagerPrivate)); +} + +static void +lpf_manager_init (LpfManager *self) +{ + LpfManagerPrivate *priv = GET_PRIVATE (self); + priv->active = NULL; + priv->available = NULL; +} diff --git a/src/lpf-manager.h b/src/lpf-manager.h new file mode 100644 index 0000000..8ee1b13 --- /dev/null +++ b/src/lpf-manager.h @@ -0,0 +1,85 @@ +/* + * lpf-manager.h: manage planfahr providers + * + * Copyright (C) 2014 Guido Günther + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Guido Günther <agx@sigxcpu.org> + */ + +#ifndef _LPF_MANAGER_H +#define _LPF_MANAGER_H + +#include <glib-object.h> +#include "lpf-provider.h" + +#define LPF_PROVIDERS_ENV "LPF_PROVIDERS" + +G_BEGIN_DECLS + +/** + * LPF_MANAGER_ERROR: + * + * Error domain for the provider manager + */ +#define LPF_MANAGER_ERROR g_spawn_error_quark () + +/** + * LpfManagerError + * @LPF_MANAGER_ERROR_ACTIVATION_FAILED: activation of the provider failed + * + * Error codes returned by manager + */ +typedef enum { + LPF_MANAGER_ERROR_ACTIVATION_FAILED, +} LpfManagerError; + +#define LPF_TYPE_MANAGER lpf_manager_get_type() + +#define LPF_MANAGER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), LPF_TYPE_MANAGER, LpfManager)) + +#define LPF_MANAGER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), LPF_TYPE_MANAGER, LpfManagerClass)) + +#define LPF_IS_MANAGER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LPF_TYPE_MANAGER)) + +#define LPF_IS_MANAGER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), LPF_TYPE_MANAGER)) + +#define LPF_MANAGER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), LPF_TYPE_MANAGER, LpfManagerClass)) + +typedef struct { + GObject parent; +} LpfManager; + +typedef struct { + GObjectClass parent_class; +} LpfManagerClass; + +GType lpf_manager_get_type (void); +GQuark lpf_manager_error_quark (void); +GStrv lpf_manager_get_available_providers(void); +LpfProvider *lpf_manager_activate_provider(LpfManager *self, + const gchar *name, + GError **error); +void lpf_manager_deactivate_provider(LpfManager *self, LpfProvider *provider); + +G_END_DECLS + +#endif /* _LPF_MANAGER_H */ diff --git a/src/lpf-priv.h b/src/lpf-priv.h new file mode 100644 index 0000000..c84180a --- /dev/null +++ b/src/lpf-priv.h @@ -0,0 +1,40 @@ +/* + * lpf-priv.h: internal functions and macros + * + * Copyright (C) 2014 Guido Günther + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Guido Günther <agx@sigxcpu.org> + */ + +#ifndef _LPF_PRIV_H +#define _LPF_PRIV_H + +#include <config.h> + +#ifdef ENABLE_DEBUG + +#include <glib.h> +#include <glib/gprintf.h> + +#define LPF_DEBUG(fmt,...) \ + g_printf ("DEBUG: %s: " fmt "\n", __func__, ##__VA_ARGS__) +#else +#define LPF_DEBUG(fmt,...) \ + do { } while (0) +#endif /* !ENABLE_DEBUG */ + +#endif diff --git a/src/lpf-provider.c b/src/lpf-provider.c new file mode 100644 index 0000000..871037b --- /dev/null +++ b/src/lpf-provider.c @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2014 Guido Guenther <agx@sigxcpu.org> + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include "lpf-provider.h" + +G_DEFINE_TYPE (LpfProvider, lpf_provider, G_TYPE_OBJECT) + +#define GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), LPF_TYPE_PROVIDER, LpfProviderPrivate)) + +/** + * SECTION:lpf-provider + * @short_description: Public transport information provider + * @see_also: #LpfProvider + * + * A #LpfProvider represents a provider of public transport + * information such as timetables or location information + */ +/** + * LpfProviderGotLocsNotify: + * @locs: (element-type Lpf.Loc): List of found locations + * @user_data: userdata + * @err: (transfer full): #GError + * + * Callback invoked after the locations matching the query were + * received. In case of an error @locs is #NULL. + */ +/** + * LpfProviderGotTripsNotify: + * @trips: (element-type Lpf.Loc): List of found trips + * @user_data: userdata + * @err: (transfer full): #GError + * + * Callback invoked after the trips matching the query were + * received. In case of an error @trips is #NULL. + */ + +enum { + PROP_0, + PROP_NAME, + LAST_PROP +}; + +typedef struct _LpfProviderPrivate LpfProviderPrivate; +struct _LpfProviderPrivate { + char *name; +}; + +GQuark +lpf_provider_error_quark (void) +{ + return g_quark_from_static_string ("lpf-provider-error-quark"); +} + +/** + * lpf_provider_name: + * @LpfProvider: a #LpfProvider + * + * Returns: transfer-none: the providers name + */ +const char* +lpf_provider_get_name (LpfProvider *self) +{ + g_return_val_if_fail (LPF_IS_PROVIDER (self), NULL); + LpfProviderPrivate *priv = GET_PRIVATE (self); + + return priv->name; +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + LpfProviderPrivate *priv = GET_PRIVATE (object); + + switch (prop_id) { + case PROP_NAME: + /* construct only */ + priv->name = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + LpfProviderPrivate *priv = GET_PRIVATE (object); + + switch (prop_id) { + case PROP_NAME: + g_value_set_string (value, priv->name); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static void +finalize (GObject *object) +{ + LpfProviderPrivate *priv = GET_PRIVATE (object); + + g_free (priv->name); +} + +/* invoked by LprManager to activate the provider */ +void +lpf_provider_activate (LpfProvider *self, GObject *obj) +{ + LpfProviderPrivate *priv = GET_PRIVATE (self); + gchar *cache_dir = NULL; + + g_return_if_fail (LPF_IS_PROVIDER (self)); + + cache_dir = g_strdup_printf("%s/%s/%s/", g_get_user_cache_dir (), PACKAGE_NAME, priv->name); + if (cache_dir) + g_mkdir_with_parents (cache_dir, 0700); + + LPF_PROVIDER_GET_CLASS (self)->activate (self, obj); + + g_free (cache_dir); +} + +/* invoked by LpfManager to activate the provider */ +void +lpf_provider_deactivate (LpfProvider *self, GObject *obj) +{ + g_return_if_fail (LPF_IS_PROVIDER (self)); + + LPF_PROVIDER_GET_CLASS (self)->deactivate (self, obj); +} + +/** + * lpf_provider_get_locs: + * @self: a #LpfProvider + * @match: locations to match + * @callback: (scope async): #LpfProviderGotLocsNotify to invoke + * once locations are available + * @user_data: (allow-none): User data for the callback + * + * Initiate a lookup for locations that match @match. Once complete + * @callback is invoked with a #GSList of matched #LpfLocation s. The + * caller is responsible for freeing the locations list via + * #lpf_provider_free_locs. + * + * Returns: 0 on success, -1 on error + */ +gint +lpf_provider_get_locs (LpfProvider *self, const char* match, LpfProviderGotLocsNotify callback, gpointer user_data) +{ + g_return_val_if_fail (LPF_IS_PROVIDER (self), -1); + g_return_val_if_fail (match, -1); + g_return_val_if_fail (callback, -1); + + return LPF_PROVIDER_GET_CLASS (self)->get_locs (self, match, callback, user_data); +} + +/** + * lpf_provider_free_locs: + * @self: a #LpfProvider + * @locs: (element-type LpfLoc): A linked list of location + * + * Free the location list + */ +void +lpf_provider_free_locs(LpfProvider *self, GSList *locs) +{ + g_slist_free_full (locs, g_object_unref); +} + +/** + * lpf_provider_get_trips: + * @self: a #LpfProvider + * @start: start of trip location + * @end: end of trip location + * @date: Date and time the trip starts as #GDateTime + * @flags: flags + * @callback: (scope async): #LpfProviderGotTripsNotify to invoke + * once trips are available + * @user_data: (allow-none): User data for the callback + * + * Initiate a lookup for trips starting at the location @start, ending + * at @end and starting (or depending on @flags) ending at @date. + * Once completed @callback is invoked with a #GSList of matched + * trips. The caller is responsible for freeing the locations list via + * #lpf_provider_free_trips. + * + * Returns: 0 on success, -1 on error + */ + +gint +lpf_provider_get_trips (LpfProvider *self, LpfLoc *start, LpfLoc *end, GDateTime *date, guint64 flags, LpfProviderGotLocsNotify callback, gpointer user_data) +{ + g_return_val_if_fail (LPF_IS_PROVIDER (self), -1); + g_return_val_if_fail (start, -1); + g_return_val_if_fail (end, -1); + g_return_val_if_fail (date, -1); + g_return_val_if_fail (callback, -1); + + return LPF_PROVIDER_GET_CLASS (self)->get_trips (self, start, end, date, flags, callback, user_data); +} + +static void +lpf_provider_class_init (LpfProviderClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (LpfProviderPrivate)); + + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->finalize = finalize; + + g_object_class_install_property + (object_class, PROP_NAME, + g_param_spec_string (LPF_PROVIDER_PROP_NAME, + "Name", + "Provider Name", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); +} + + +static void +lpf_provider_init (LpfProvider *self G_GNUC_UNUSED) +{ +} diff --git a/src/lpf-provider.h b/src/lpf-provider.h new file mode 100644 index 0000000..1c27183 --- /dev/null +++ b/src/lpf-provider.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2014 Guido Guenther <agx@sigxcpu.org> + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _LPF_PROVIDER +#define _LPF_PROVIDER + +#include <glib-object.h> +#include <lpf-loc.h> + +G_BEGIN_DECLS + +#define LPF_PROVIDER_MAJOR_VERSION 0 +#define LPF_PROVIDER_MINOR_VERSION 0 + +/** + * LPF_PROVIDER_ERROR: + * + * Error domain for providers + */ +#define LPF_PROVIDER_ERROR g_spawn_error_quark () + +/** + * LpfProviderError + * @LPF_PROVIDER_ERROR_REQUEST_FAILED: a request to fetch data from a remote failed + * @LPF_PROVIDER_ERROR_PARSE_FAILED: parsing the reply failed + * + * Error codes returned by providers + */ +typedef enum { + LPF_PROVIDER_ERROR_REQUEST_FAILED, + LPF_PROVIDER_ERROR_PARSE_FAILED, +} LpfProviderError; + +#define LPF_TYPE_PROVIDER lpf_provider_get_type() + +#define LPF_PROVIDER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), LPF_TYPE_PROVIDER, LpfProvider)) + +#define LPF_PROVIDER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), LPF_TYPE_PROVIDER, LpfProviderClass)) + +#define LPF_IS_PROVIDER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LPF_TYPE_PROVIDER)) + +#define LPF_IS_PROVIDER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), LPF_TYPE_PROVIDER)) + +#define LPF_PROVIDER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), LPF_TYPE_PROVIDER, LpfProviderClass)) + +#define LPF_PROVIDER_PROP_NAME "name" + +typedef void (*LpfProviderGotLocsNotify) (GSList *locs, gpointer user_data, GError *err); +typedef void (*LpfProviderGotTripsNotify) (GSList *trips, gpointer user_data, GError *err); + +typedef struct { + GObject parent; +} LpfProvider; + +typedef struct { + GObjectClass parent_class; + + void (*activate) (LpfProvider *self, GObject *obj); + void (*deactivate) (LpfProvider *self, GObject *obj); + + gint (*get_locs) (LpfProvider *self, const gchar *match, LpfProviderGotLocsNotify callback, gpointer user_data); + gint (*get_trips) (LpfProvider *self, LpfLoc *start, LpfLoc *end, GDateTime *date, guint64 flags, LpfProviderGotLocsNotify callback, gpointer user_data); +} LpfProviderClass; + +GType lpf_provider_get_type (void); + +LpfProvider *lpf_provider_new (void); + +typedef LpfProvider *(*LpfProviderCreateFunc) (void); +LpfProvider *lpf_provider_create (void); + +const char* lpf_provider_get_name (LpfProvider *self); +void lpf_provider_activate (LpfProvider *self, GObject *obj); +void lpf_provider_deactivate (LpfProvider *self, GObject *obj); +GQuark lpf_provider_error_quark (void); +gint lpf_provider_get_locs (LpfProvider *self, const gchar* match, LpfProviderGotLocsNotify callback, gpointer user_data); +gint lpf_provider_get_trips (LpfProvider *self, LpfLoc *start, LpfLoc *end, GDateTime *date, guint64 flags, LpfProviderGotLocsNotify callback, gpointer user_data); +void lpf_provider_free_locs (LpfProvider *self, GSList *locs); + +G_END_DECLS + +#endif /* _LPF_PROVIDER */ diff --git a/src/lpf-stop.c b/src/lpf-stop.c new file mode 100644 index 0000000..a42806c --- /dev/null +++ b/src/lpf-stop.c @@ -0,0 +1,316 @@ +/* + * lpf-loc.c: a stop during public transport + * + * Copyright (C) 2014 Guido Günther + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Guido Günther <agx@sigxcpu.org> + */ + +#include <glob.h> +#include <string.h> +#include <math.h> + +#include <glib.h> +#include <glib/gprintf.h> +#include <gmodule.h> + +#include "lpf-loc.h" +#include "lpf-stop.h" +#include "lpf-priv.h" + +enum { + LPF_STOP_PROP_0 = 0, + LPF_STOP_PROP_ARRIVAL, + LPF_STOP_PROP_DEPARTURE, + LPF_STOP_PROP_ARRIVAL_PLATFORM, + LPF_STOP_PROP_DEPARTURE_PLATFORM, + LPF_STOP_PROP_RT_ARRIVAL, + LPF_STOP_PROP_RT_DEPARTURE, + LPF_STOP_PROP_ARRIVAL_DELAY, + LPF_STOP_PROP_DEPARTURE_DELAY, +}; + +/** + * SECTION:lpf-stop + * @short_description: A stop during public transport + * + * A #LpfStop adds arrival and departure times to a #LpfLoc. + */ + +G_DEFINE_TYPE (LpfStop, lpf_stop, LPF_TYPE_LOC) +#define GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), LPF_TYPE_STOP, LpfStopPrivate)) + +typedef struct _LpfStopPrivate LpfStopPrivate; +struct _LpfStopPrivate { + gchar *name; + /* Planned */ + GDateTime *arr, *dep; + gchar *arr_plat, *dep_plat; + /* Predicted */ + GDateTime *rt_arr, *rt_dep; +}; + +static void +lpf_stop_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + LpfStop *self = LPF_STOP (object); + LpfStopPrivate *priv = GET_PRIVATE(self); + + switch (property_id) { + case LPF_STOP_PROP_ARRIVAL: + if (priv->arr) + g_date_time_unref (priv->arr); + priv->arr = g_value_get_boxed (value); + break; + + case LPF_STOP_PROP_DEPARTURE: + if (priv->dep) + g_date_time_unref (priv->dep); + priv->dep = g_value_get_boxed (value); + break; + + case LPF_STOP_PROP_ARRIVAL_PLATFORM: + g_free (priv->arr_plat); + priv->arr_plat = g_value_dup_string (value); + break; + + case LPF_STOP_PROP_DEPARTURE_PLATFORM: + g_free (priv->dep_plat); + priv->dep_plat = g_value_dup_string (value); + break; + + case LPF_STOP_PROP_RT_ARRIVAL: + if (priv->rt_arr) + g_date_time_unref (priv->rt_arr); + priv->rt_arr = g_value_get_boxed (value); + break; + + case LPF_STOP_PROP_RT_DEPARTURE: + if (priv->rt_dep) + g_date_time_unref (priv->rt_dep); + priv->rt_dep = g_value_get_boxed (value); + break; + + default: + /* We don't have any other property... */ + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +/** + * calc_delay: Calculate the time difference between planned and + * real time in minutes + */ +static gint +calc_delay(GDateTime *planned, GDateTime *real) +{ + if (planned == NULL || real == NULL) + return 0; + + return g_date_time_difference (planned, real) / (60 * 1000 * 1000); +} + + +static void +lpf_stop_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + LpfStop *self = LPF_STOP (object); + LpfStopPrivate *priv = GET_PRIVATE(self); + gint delay; + + switch (property_id) { + case LPF_STOP_PROP_ARRIVAL: + g_value_set_boxed (value, priv->arr); + break; + + case LPF_STOP_PROP_DEPARTURE: + g_value_set_boxed (value, priv->dep); + break; + + case LPF_STOP_PROP_ARRIVAL_PLATFORM: + g_value_set_string (value, priv->arr_plat); + break; + + case LPF_STOP_PROP_DEPARTURE_PLATFORM: + g_value_set_string (value, priv->dep_plat); + break; + + case LPF_STOP_PROP_RT_ARRIVAL: + g_value_set_boxed (value, priv->rt_arr); + break; + + case LPF_STOP_PROP_RT_DEPARTURE: + g_value_set_boxed (value, priv->rt_dep); + break; + + case LPF_STOP_PROP_ARRIVAL_DELAY: + delay = calc_delay (priv->arr, priv->rt_arr); + g_value_set_int (value, delay); + break; + + case LPF_STOP_PROP_DEPARTURE_DELAY: + delay = calc_delay (priv->dep, priv->rt_dep); + g_value_set_int (value, delay); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +lpf_stop_dispose(GObject *object) +{ + GObjectClass *parent_class = G_OBJECT_CLASS (lpf_stop_parent_class); + + if (parent_class->dispose != NULL) + parent_class->dispose (object); +} + + +static void +lpf_stop_finalize (GObject *object) +{ + LpfStop *self = LPF_STOP (object); + LpfStopPrivate *priv = GET_PRIVATE (self); + GObjectClass *parent_class = G_OBJECT_CLASS (lpf_stop_parent_class); + + g_date_time_unref (priv->dep); + g_date_time_unref (priv->arr); + g_free (priv->name); + g_free (priv->arr_plat); + g_free (priv->dep_plat); + if (priv->rt_dep) + g_date_time_unref (priv->rt_dep); + if (priv->rt_arr) + g_date_time_unref (priv->rt_arr); + if (parent_class->finalize != NULL) + parent_class->finalize (object); +} + + +static void +lpf_stop_class_init (LpfStopClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = lpf_stop_dispose; + object_class->finalize = lpf_stop_finalize; + g_type_class_add_private (klass, sizeof (LpfStopPrivate)); + + object_class->set_property = lpf_stop_set_property; + object_class->get_property = lpf_stop_get_property; + +/** + * LpfStop:departure: + * + * Train departs at this stop at this date and time + */ + g_object_class_install_property (object_class, + LPF_STOP_PROP_DEPARTURE, + g_param_spec_boxed ("departure", + "Departure", + "Departure date and time", + G_TYPE_DATE_TIME, + G_PARAM_READWRITE)); +/** + * LpfStop:arrival: + * + * Train arrives at this stop at this date and time + */ + g_object_class_install_property (object_class, + LPF_STOP_PROP_ARRIVAL, + g_param_spec_boxed ("arrival", + "Arrival", + "Arrival date and time", + G_TYPE_DATE_TIME, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + LPF_STOP_PROP_ARRIVAL_PLATFORM, + g_param_spec_string ("arr_plat", + "Arrival Platform", + "Arrival platform", + "", + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + LPF_STOP_PROP_DEPARTURE_PLATFORM, + g_param_spec_string ("dep_plat", + "Departure platform", + "Departure platform", + "", + G_PARAM_READWRITE)); + +/** + * LpfStop:rt_departure: + * + * Real time departure time + */ + g_object_class_install_property (object_class, + LPF_STOP_PROP_RT_DEPARTURE, + g_param_spec_boxed ("rt_departure", + "Real Time Departure", + "Real Time Departure date and time", + G_TYPE_DATE_TIME, + G_PARAM_READWRITE)); +/** + * LpfStop:rt_arrival: + * + * Real time arrival time + */ + g_object_class_install_property (object_class, + LPF_STOP_PROP_RT_ARRIVAL, + g_param_spec_boxed ("rt_arrival", + "Real Time Arrival", + "Real time arrival date and time", + G_TYPE_DATE_TIME, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + LPF_STOP_PROP_ARRIVAL_DELAY, + g_param_spec_int ("arrival_delay", + "Arrival Delay", + "Arrival delay", + INT_MIN, + INT_MAX, + 0, + G_PARAM_READABLE)); + + g_object_class_install_property (object_class, + LPF_STOP_PROP_DEPARTURE_DELAY, + g_param_spec_int ("departure_delay", + "Departure Delay", + "Departure delay", + INT_MIN, + INT_MAX, + 0, + G_PARAM_READABLE)); +} + +static void +lpf_stop_init (LpfStop *self) +{ +} diff --git a/src/lpf-stop.h b/src/lpf-stop.h new file mode 100644 index 0000000..c7a3bbf --- /dev/null +++ b/src/lpf-stop.h @@ -0,0 +1,59 @@ +/* + * lpf-stop.h: a public transport stop + * + * Copyright (C) 2014 Guido Günther + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Guido Günther <agx@sigxcpu.org> + */ + +#ifndef _LPF_STOP_H +#define _LPF_STOP_H + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define LPF_TYPE_STOP lpf_stop_get_type() + +#define LPF_STOP(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), LPF_TYPE_STOP, LpfStop)) + +#define LPF_STOP_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), LPF_TYPE_STOP, LpfStopClass)) + +#define LPF_IS_STOP(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LPF_TYPE_STOP)) + +#define LPF_IS_STOP_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), LPF_TYPE_STOP)) + +#define LPF_STOP_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), LPF_TYPE_STOP, LpfStopClass)) + +typedef struct { + GObject parent; +} LpfStop; + +typedef struct { + GObjectClass parent_class; +} LpfStopClass; + +GType lpf_stop_get_type (void); + +G_END_DECLS + +#endif /* _LPF_STOP_H */ diff --git a/src/lpf-trip-part.c b/src/lpf-trip-part.c new file mode 100644 index 0000000..15ef7f5 --- /dev/null +++ b/src/lpf-trip-part.c @@ -0,0 +1,200 @@ +/* + * lpf-loc.c: a part of a trip via public transport + * + * Copyright (C) 2014 Guido Günther + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Guido Günther <agx@sigxcpu.org> + */ + +#include <glob.h> +#include <string.h> +#include <math.h> + +#include <glib.h> +#include <glib/gprintf.h> +#include <gmodule.h> + +#include "lpf-trip-part.h" +#include "lpf-stop.h" +#include "lpf-priv.h" + +enum { + LPF_TRIP_PART_PROP_0 = 0, + LPF_TRIP_PART_PROP_START, + LPF_TRIP_PART_PROP_END, + LPF_TRIP_PART_PROP_LINE, + LPF_TRIP_PART_PROP_STOPS, +}; + +/** + * SECTION:lpf-trip-part + * @short_description: A part of a trip + * + * A #LpfTripPart represents a part of a trip. It has a start and end + * #LpfStop. + */ + +G_DEFINE_TYPE (LpfTripPart, lpf_trip_part, G_TYPE_OBJECT) +#define GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), LPF_TYPE_TRIP_PART, LpfTripPartPrivate)) + +typedef struct _LpfTripPartPrivate LpfTripPartPrivate; +struct _LpfTripPartPrivate { + LpfStop *start; + LpfStop *end; + gchar *line; + GSList *stops; +}; + +static void +lpf_trip_part_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + LpfTripPart *self = LPF_TRIP_PART (object); + LpfTripPartPrivate *priv = GET_PRIVATE(self); + + switch (property_id) { + case LPF_TRIP_PART_PROP_START: + if (priv->start) + g_object_unref (priv->start); + priv->start = g_value_get_object(value); + break; + + case LPF_TRIP_PART_PROP_END: + if (priv->end) + g_object_unref (priv->end); + priv->end = g_value_get_object(value); + break; + + case LPF_TRIP_PART_PROP_LINE: + g_free (priv->line); + priv->line = g_value_dup_string(value); + break; + + case LPF_TRIP_PART_PROP_STOPS: + priv->stops = g_value_get_pointer(value); + break; + + default: + /* We don't have any other property... */ + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +lpf_trip_part_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + LpfTripPart *self = LPF_TRIP_PART (object); + LpfTripPartPrivate *priv = GET_PRIVATE(self); + + switch (property_id) { + case LPF_TRIP_PART_PROP_START: + g_value_set_object (value, priv->start); + break; + + case LPF_TRIP_PART_PROP_END: + g_value_set_object (value, priv->end); + break; + + case LPF_TRIP_PART_PROP_LINE: + g_value_set_string (value, priv->line); + break; + + case LPF_TRIP_PART_PROP_STOPS: + g_value_set_pointer (value, priv->stops); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + + +static void +lpf_trip_part_finalize (GObject *object) +{ + LpfTripPart *self = LPF_TRIP_PART (object); + LpfTripPartPrivate *priv = GET_PRIVATE (self); + GObjectClass *parent_class = G_OBJECT_CLASS (lpf_trip_part_parent_class); + + g_object_unref (priv->start); + g_object_unref (priv->end); + g_slist_free_full (priv->stops, g_object_unref); + g_free (priv->line); + g_slist_free_full (priv->stops, g_object_unref); + + if (parent_class->finalize != NULL) + parent_class->finalize (object); +} + + +static void +lpf_trip_part_class_init (LpfTripPartClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = lpf_trip_part_finalize; + g_type_class_add_private (klass, sizeof (LpfTripPartPrivate)); + + object_class->set_property = lpf_trip_part_set_property; + object_class->get_property = lpf_trip_part_get_property; + + g_object_class_install_property (object_class, + LPF_TRIP_PART_PROP_START, + g_param_spec_object ("start", + "Start", + "The start location and date/time", + LPF_TYPE_STOP, + G_PARAM_CONSTRUCT | G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + LPF_TRIP_PART_PROP_END, + g_param_spec_object ("end", + "End", + "The end location and date/time", + LPF_TYPE_STOP, + G_PARAM_CONSTRUCT | G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + LPF_TRIP_PART_PROP_LINE, + g_param_spec_string ("line", + "Line", + "The Line that operates between start and end", + "", + G_PARAM_CONSTRUCT | G_PARAM_READWRITE)); +/** + * LpfTripPart:stops: (type GSList(LpfStop)) + */ + g_object_class_install_property (object_class, + LPF_TRIP_PART_PROP_STOPS, + g_param_spec_pointer ("stops", + "Stops", + "Stops between start and end", + G_PARAM_CONSTRUCT | G_PARAM_READWRITE)); +} + +static void +lpf_trip_part_init (LpfTripPart *self) +{ +} diff --git a/src/lpf-trip-part.h b/src/lpf-trip-part.h new file mode 100644 index 0000000..f514bf6 --- /dev/null +++ b/src/lpf-trip-part.h @@ -0,0 +1,59 @@ +/* + * lpf-trip.h: a part of a trip via public transport + * + * Copyright (C) 2014 Guido Günther + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Guido Günther <agx@sigxcpu.org> + */ + +#ifndef _LPF_TRIP_PART_H +#define _LPF_TRIP_PART_H + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define LPF_TYPE_TRIP_PART lpf_trip_part_get_type() + +#define LPF_TRIP_PART(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), LPF_TYPE_TRIP_PART, LpfTripPart)) + +#define LPF_TRIP_PART_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), LPF_TYPE_TRIP_PART, LpfTripPartClass)) + +#define LPF_IS_TRIP_PART(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LPF_TYPE_TRIP_PART)) + +#define LPF_IS_TRIP_PART_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), LPF_TYPE_TRIP_PART)) + +#define LPF_TRIP_PART_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), LPF_TYPE_TRIP_PART, LpfTripPartClass)) + +typedef struct { + GObject parent; +} LpfTripPart; + +typedef struct { + GObjectClass parent_class; +} LpfTripPartClass; + +GType lpf_trip_part_get_type (void); + +G_END_DECLS + +#endif /* _LPF_TRIP_PART_H */ diff --git a/src/lpf-trip.c b/src/lpf-trip.c new file mode 100644 index 0000000..aa874db --- /dev/null +++ b/src/lpf-trip.c @@ -0,0 +1,152 @@ +/* + * lpf-loc.c: a trip via public transport + * + * Copyright (C) 2014 Guido Günther + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Guido Günther <agx@sigxcpu.org> + */ + +#include <glob.h> +#include <string.h> +#include <math.h> + +#include <glib.h> +#include <glib/gprintf.h> +#include <gmodule.h> + +#include "lpf-trip.h" +#include "lpf-loc.h" +#include "lpf-priv.h" + +enum { + LPF_TRIP_PROP_0 = 0, + LPF_TRIP_PROP_PARTS, +}; + +/** + * SECTION:lpf-trip + * @short_description: A trip via public transport + * + * A #LpfTrip represents a trip. It consists of several #LpfTripParts each with a start + * and end location. + */ + +G_DEFINE_TYPE (LpfTrip, lpf_trip, G_TYPE_OBJECT) +#define GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), LPF_TYPE_TRIP, LpfTripPrivate)) + +typedef struct _LpfTripPrivate LpfTripPrivate; +struct _LpfTripPrivate { + GSList *parts; +}; + +static void +lpf_trip_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + LpfTrip *self = LPF_TRIP (object); + LpfTripPrivate *priv = GET_PRIVATE(self); + + switch (property_id) { + case LPF_TRIP_PROP_PARTS: + priv->parts = g_value_get_pointer(value); + break; + + default: + /* We don't have any other property... */ + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +lpf_trip_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + LpfTrip *self = LPF_TRIP (object); + LpfTripPrivate *priv = GET_PRIVATE(self); + + switch (property_id) { + case LPF_TRIP_PROP_PARTS: + g_value_set_pointer (value, priv->parts); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + + +static void +lpf_trip_dispose(GObject *object) +{ + GObjectClass *parent_class = G_OBJECT_CLASS (lpf_trip_parent_class); + + if (parent_class->dispose != NULL) + parent_class->dispose (object); +} + + +static void +lpf_trip_finalize (GObject *object) +{ + LpfTrip *self = LPF_TRIP (object); + LpfTripPrivate *priv = GET_PRIVATE (self); + GObjectClass *parent_class = G_OBJECT_CLASS (lpf_trip_parent_class); + + g_slist_free_full (priv->parts, g_object_unref); + + if (parent_class->finalize != NULL) + parent_class->finalize (object); +} + + +static void +lpf_trip_class_init (LpfTripClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = lpf_trip_dispose; + object_class->finalize = lpf_trip_finalize; + g_type_class_add_private (klass, sizeof (LpfTripPrivate)); + + object_class->set_property = lpf_trip_set_property; + object_class->get_property = lpf_trip_get_property; + + +/** + * LpfTrip:parts: (type GSList(LpfTripPart)) + * + * The parts of a trip as #LpfTripPart. + */ + g_object_class_install_property (object_class, + LPF_TRIP_PROP_PARTS, + g_param_spec_pointer ("parts", + "trip parts", + "The parts of the trip", + G_PARAM_CONSTRUCT | G_PARAM_READWRITE)); +} + +static void +lpf_trip_init (LpfTrip *self) +{ +} diff --git a/src/lpf-trip.h b/src/lpf-trip.h new file mode 100644 index 0000000..d0f1542 --- /dev/null +++ b/src/lpf-trip.h @@ -0,0 +1,59 @@ +/* + * lpf-trip.h: a trip via public transport + * + * Copyright (C) 2014 Guido Günther + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Guido Günther <agx@sigxcpu.org> + */ + +#ifndef _LPF_TRIP_H +#define _LPF_TRIP_H + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define LPF_TYPE_TRIP lpf_trip_get_type() + +#define LPF_TRIP(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), LPF_TYPE_TRIP, LpfTrip)) + +#define LPF_TRIP_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), LPF_TYPE_TRIP, LpfTripClass)) + +#define LPF_IS_TRIP(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LPF_TYPE_TRIP)) + +#define LPF_IS_TRIP_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), LPF_TYPE_TRIP)) + +#define LPF_TRIP_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), LPF_TYPE_TRIP, LpfTripClass)) + +typedef struct { + GObject parent; +} LpfTrip; + +typedef struct { + GObjectClass parent_class; +} LpfTripClass; + +GType lpf_trip_get_type (void); + +G_END_DECLS + +#endif /* _LPF_TRIP_H */ diff --git a/src/providers/Makefile.am b/src/providers/Makefile.am new file mode 100644 index 0000000..5bc3a84 --- /dev/null +++ b/src/providers/Makefile.am @@ -0,0 +1,32 @@ +include $(top_srcdir)/flymake.mk + +SUBDIRS = tests + +pkglibdir = $(LPF_PROVIDERS_DIR) + +pkglib_LTLIBRARIES = \ + libplanfahr-provider-de-db.la \ + $(NULL) + +libplanfahr_provider_de_db_la_SOURCES = \ + de-db.h \ + de-db.c \ + hafas-bin6.h \ + hafas-bin6.c \ + $(NULL) + +libplanfahr_provider_de_db_la_CFLAGS = \ + $(GIO2_CFLAGS) \ + $(GOBJECT2_CFLAGS) \ + $(GTHREAD2_CFLAGS) \ + $(LIBXML2_CFLAGS) \ + $(LIBSOUP_CFLAGS) \ + $(WARN_CFLAGS) \ + -I$(top_srcdir)/src \ + $(NULL) + +libplanfahr_provider_de_db_la_LDFLAGS = \ + -module \ + -avoid-version \ + $(LIBSOUP_LIBS) \ + $(NULL) diff --git a/src/providers/de-db.c b/src/providers/de-db.c new file mode 100644 index 0000000..d58aba2 --- /dev/null +++ b/src/providers/de-db.c @@ -0,0 +1,751 @@ +/* + * db.c: Deutsche Bahn provider for libplanfahr + * + * Copyright (C) 2014 Guido Günther + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Author: Guido Günther <agx@sigxcpu.org> +n */ + +#include <config.h> + +#include <gmodule.h> +#include <libsoup/soup.h> +#include <string.h> + +#include <libxml/parser.h> +#include <libxml/xpath.h> + +#include "lpf-priv.h" +#include "lpf-loc.h" +#include "lpf-trip.h" +#include "lpf-trip-part.h" +#include "lpf-stop.h" +#include "de-db.h" +#include "hafas-bin6.h" + +#define LOC_URL "http://mobile.bahn.de/bin/mobil/query.exe/en" +#define TRIPS_URL "http://reiseauskunft.bahn.de/bin/query.exe/eox" + +#define PROVIDER_NAME "de_db" + +/* transfers data between invocation and passed in callback */ +typedef struct _LpfProviderGotItUserData { + LpfProvider *self; + gpointer callback; + gpointer user_data; +} LpfProviderGotItUserData; + +G_DEFINE_TYPE (LpfProviderDeDb, lpf_provider_de_db, LPF_TYPE_PROVIDER) +#define GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), LPF_TYPE_PROVIDER_DE_DB, LpfProviderDeDbPrivate)) + +int lpf_provider_major_version = LPF_PROVIDER_MAJOR_VERSION; +int lpf_provider_minor_version = LPF_PROVIDER_MINOR_VERSION; + +G_MODULE_EXPORT LpfProvider * +lpf_provider_create (void) +{ + return LPF_PROVIDER (lpf_provider_de_db_new ()); +} + +typedef struct _LpfProviderDeDbPrivate LpfProviderDeDbPrivate; + +struct _LpfProviderDeDbPrivate { + SoupSession *session; + char *logdir; + gboolean debug; +}; + +static void +lpf_provider_de_db_finalize (GObject *object) +{ + G_OBJECT_CLASS (lpf_provider_de_db_parent_class)->finalize (object); +} + +static void +lpf_provider_de_db_activate (LpfProvider *self, GObject *obj) +{ + LpfProviderDeDbPrivate *priv = GET_PRIVATE(self); + GFile *dir; + gchar *debugstr; + + priv->session = soup_session_new(); + priv->logdir = g_build_path(G_DIR_SEPARATOR_S, + g_get_user_cache_dir(), + PACKAGE, + PROVIDER_NAME, + NULL); + + debugstr = getenv ("LPF_DEBUG"); + if (debugstr && strstr (debugstr, "provider")) + priv->debug = TRUE; + + dir = g_file_new_for_path (priv->logdir); + g_file_make_directory_with_parents (dir, NULL, NULL); + g_object_unref (dir); +} + +static void +lpf_provider_de_db_deactivate (LpfProvider *self, GObject *obj) +{ + LpfProviderDeDbPrivate *priv = GET_PRIVATE(self); + + if (priv->session) + g_object_unref (priv->session); + + g_free (priv->logdir); + + xmlCleanupParser(); +} + + +static GSList* +parse_stations_xml (const char *xml) +{ + gint i = 0; + xmlDocPtr doc = NULL; + xmlChar *xpath = (xmlChar*) "//MLc[@t=\"ST\"]"; + xmlXPathContextPtr context = NULL; + xmlXPathObjectPtr result = NULL; + xmlNodeSetPtr nodeset = NULL; + gchar *name, *x, *y; + gdouble lo, la; + LpfLoc *loc; + char *id; + GSList *locs = NULL; + + g_return_val_if_fail (xml, NULL); + + LPF_DEBUG("%s", xml); + doc = xmlParseMemory(xml, strlen(xml)); + if (doc == NULL ) { + g_warning("Document not parsed successfully"); + return NULL; + } + + context = xmlXPathNewContext(doc); + if (context == NULL) { + g_warning("Error in xmlXPathNewContext\n"); + goto out; + } + + result = xmlXPathEvalExpression(xpath, context); + if (result == NULL) { + g_warning("Error in xmlXPathEvalExpression\n"); + goto out; + } + + if(xmlXPathNodeSetIsEmpty(result->nodesetval)){ + g_warning("No result\n"); + goto out; + } + + nodeset = result->nodesetval; + for (i=0; i < nodeset->nodeNr; i++) { + name = (gchar*) xmlGetProp(nodeset->nodeTab[i], BAD_CAST "n"); + x = (gchar*) xmlGetProp(nodeset->nodeTab[i], BAD_CAST "x"); + y = (gchar*) xmlGetProp(nodeset->nodeTab[i], BAD_CAST "y"); + id = (gchar*) xmlGetProp(nodeset->nodeTab[i], BAD_CAST "i"); + + lo = (gdouble) g_ascii_strtod(x, NULL) / 1000000.0; + la = (gdouble) g_ascii_strtod(y, NULL) / 1000000.0; + + LPF_DEBUG ("%s (%lf, %lf) - %s", name, lo, la, id); + + loc = (LpfLoc*) g_object_new (LPF_TYPE_LOC, "name", name, "long", lo, "lat", la, NULL); + lpf_loc_set_opaque (loc, id); + locs = g_slist_append(locs, loc); + g_free(x); + g_free(y); + } + out: + xmlXPathFreeContext(context); + xmlXPathFreeObject(result); + xmlFreeDoc(doc); + return locs; +} + + +static void +log_response_body(LpfProviderDeDb *self, SoupMessage *msg, const char* type) +{ + LpfProviderDeDbPrivate *priv = GET_PRIVATE(self); + gchar *querylog = NULL; + gchar *filename = NULL; + gchar *time = NULL; + GDateTime *now; + + if (G_LIKELY(!priv->debug)) + return; + + now = g_date_time_new_now_local(); + + time = g_date_time_format (now, "%F_%T"); + filename = g_strdup_printf ("%s-%s.body", type, time); + + querylog = g_build_path (G_DIR_SEPARATOR_S, + priv->logdir, + filename, + NULL); + + if (!g_file_set_contents (querylog, msg->response_body->data, msg->response_body->length, NULL)) + g_warning ("Failed tpo write out query contents to %s", querylog); + + g_free (querylog); + g_free (filename); + g_free (time); + g_date_time_unref(now); +} + + +static void +got_stations (SoupSession *session, SoupMessage *msg, gpointer user_data) +{ + GSList *locs = NULL; + LpfProviderGotItUserData *locs_data = (LpfProviderGotItUserData*)user_data; + LpfProviderGotLocsNotify callback; + LpfProviderDeDb *self; + gpointer data; + GError *err = NULL; + + g_return_if_fail(session); + g_return_if_fail(msg); + g_return_if_fail(user_data); + + callback = locs_data->callback; + data = locs_data->user_data; + self = (LpfProviderDeDb*)locs_data->self; + g_free (locs_data); + + LPF_DEBUG("Status: %d", msg->status_code); + if (!SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)) { + LPF_DEBUG("HTTP request failed"); + g_set_error (&err, + LPF_PROVIDER_ERROR, + LPF_PROVIDER_ERROR_REQUEST_FAILED, + "HTTP request failed: %d", msg->status_code); + goto out; + } + + log_response_body (self, msg, "station"); + + if ((locs = parse_stations_xml(msg->response_body->data)) == NULL) { + g_set_error (&err, + LPF_PROVIDER_ERROR, + LPF_PROVIDER_ERROR_PARSE_FAILED, + "Failed to parse locations"); + goto out; + } + +out: + (*callback)(locs, data, err); +} + + +static gint +lpf_provider_de_db_get_locs (LpfProvider *self, const char* match, LpfProviderGotLocsNotify callback, gpointer user_data) +{ + LpfProviderDeDbPrivate *priv = GET_PRIVATE(self); + SoupMessage *msg; + char *xml; + LpfProviderGotItUserData *locs_data = NULL; + gint ret = -1; + + g_return_val_if_fail (priv->session, -1); + + locs_data = g_malloc(sizeof(LpfProviderDeDbPrivate)); + if (!locs_data) + goto out; + xml = g_strdup_printf ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<ReqC ver=\"1.1\" prod=\"String\" lang=\"EN\">" + "<MLcReq><MLc n=\"%s\" t=\"ST\"/>" + "</MLcReq></ReqC>", match); + + msg = soup_message_new ("POST", LOC_URL); + if (!msg) + goto out; + soup_message_set_request (msg, "text/xml", SOUP_MEMORY_TAKE, xml, strlen (xml)); + + locs_data->user_data = user_data; + locs_data->callback = callback; + locs_data->self = self; + + soup_session_queue_message (priv->session, msg, got_stations, locs_data); + ret = 0; + out: + if (ret < 0) + g_free (locs_data); + return ret; +} + + +static GSList* +hafas_binary_parse_each_trip (const gchar *data, gsize num, guint base, const char *enc) +{ + gint i, j, k; + const HafasBin6Trip *t; + const HafasBin6TripPartDetail *pd; + const HafasBin6TripPart *p; + const HafasBin6TripStop *stop; + guint16 day_off; + +#ifdef ENABLE_DEBUG + const HafasBin6TripDetail *d; +#endif + guint h, m; + LpfTrip *trip = NULL; + LpfTripPart *part = NULL; + LpfStop *start = NULL, *end = NULL, *astop = NULL; + GDateTime *dt; + GSList *trips = NULL, *parts = NULL, *stops = NULL; + const char *line; + + for (i = 0; i < num; i++) { + /* The trips itself */ + t = HAFAS_BIN6_TRIP(data, i); + day_off = hafas_bin6_parse_service_day(data, i); + + LPF_DEBUG("Trip #%d, Changes: %4d", i, t->changes); +#ifdef ENABLE_DEBUG + /* trip details */ + d = HAFAS_BIN6_TRIP_DETAIL(data, i); + LPF_DEBUG("Trip #%d, Status: %4d", i, d->rt_status); + LPF_DEBUG("Trip #%d, Delay: %4d", i, d->delay); +#endif + /* trip parts */ + for (j = 0; j < t->part_cnt; j++) { + p = HAFAS_BIN6_TRIP_PART(data, i, j); + start = g_object_new(LPF_TYPE_STOP, NULL); + if (hafas_bin6_parse_station(data, p->dep_off, LPF_LOC(start), enc) < 0) { + g_warning("Failed to parse start station %d/%d", i, j); + goto error; + } + end = g_object_new(LPF_TYPE_STOP, NULL); + if (hafas_bin6_parse_station(data, p->arr_off, LPF_LOC(end), enc) < 0) { + g_warning("Failed to parse end station %d/%d", i, j); + goto error; + } + line = HAFAS_BIN6_STR(data, p->line_off); + + h = p->dep / 100; + m = p->dep % 100; + dt = hafas_bin6_date_time (base, day_off, h, m); + g_object_set (start, "departure", dt, NULL); + + if (g_strcmp0 (HAFAS_BIN6_NO_PLATFORM, HAFAS_BIN6_STR(data, p->dep_pos_off))) { + g_object_set (start, + "dep_plat", HAFAS_BIN6_STR(data, p->dep_pos_off), + NULL); + } + + h = p->arr / 100; + m = p->arr % 100; + dt = hafas_bin6_date_time (base, day_off, h, m); + g_object_set (end, "arrival", dt, NULL); + + if (g_strcmp0 (HAFAS_BIN6_NO_PLATFORM, HAFAS_BIN6_STR(data, p->arr_pos_off))) { + g_object_set (end, + "arr_plat", HAFAS_BIN6_STR(data, p->arr_pos_off), + NULL); + } + + /* trip part details */ + pd = HAFAS_BIN6_TRIP_PART_DETAIL(data, i, j); + + if (pd->arr_pred != HAFAS_BIN6_NO_REALTIME) { + h = pd->arr_pred / 100; + m = pd->arr_pred % 100; + dt = hafas_bin6_date_time (base, day_off, h, m); + g_object_set (start, "rt_departure", dt, NULL); + } + + if (pd->dep_pred != HAFAS_BIN6_NO_REALTIME) { + h = pd->dep_pred / 100; + m = pd->dep_pred % 100; + dt = hafas_bin6_date_time (base, day_off, h, m); + g_object_set (end, "rt_arrival", dt, NULL); + } + + LPF_DEBUG("Trip #%d, part #%d, Pred. Dep Plat: %s, Pred. Arr Plat: %s", i, j, + HAFAS_BIN6_STR(data, pd->dep_pos_pred_off), + HAFAS_BIN6_STR(data, pd->arr_pos_pred_off)); + + for (k = 0; k < pd->stops_cnt; k++) { + stop = HAFAS_BIN6_STOP(data, i, j, k); + + astop = g_object_new (LPF_TYPE_STOP, + "arrival", hafas_bin6_date_time (base, + day_off, + stop->arr / 100, + stop->arr % 100), + "departure", hafas_bin6_date_time (base, + day_off, + stop->dep / 100, + stop->dep % 100), + NULL + ); + + if (hafas_bin6_parse_station(data, stop->stop_idx, LPF_LOC(astop), enc) < 0) { + g_warning("Failed to parse stop %d/%d", i, j); + goto error; + } + +#ifdef ENABLE_DEBUG + /* FIXME: add these and predicted dep/arr/plat to LpfStop too */ + LPF_DEBUG("Trip #%d, part #%d," + "Dep: %.6s Arr: %.6s", i, j, + HAFAS_BIN6_STR(data, stop->dep_pos_off), + HAFAS_BIN6_STR(data, stop->arr_pos_off)); +#endif + stops = g_slist_append (stops, astop); + astop = NULL; + } + part = g_object_new (LPF_TYPE_TRIP_PART, + "start", start, + "end", end, + "line", line, + "stops", stops, + NULL); + start = end = NULL; + line = NULL; + stops = NULL; + + parts = g_slist_append (parts, part); + part = NULL; + } + trip = g_object_new (LPF_TYPE_TRIP, + "parts", parts, + NULL); + parts = NULL; + trips = g_slist_append (trips, trip); + trip = NULL; + } + + return trips; + +error: + if (trips) + g_slist_free_full (trips, g_object_unref); + if (parts) + g_slist_free_full (parts, g_object_unref); + if (stops) + g_slist_free_full (stops, g_object_unref); + if (start) + g_object_unref (start); + if (trip) + g_object_unref (trip); + if (part) + g_object_unref (part); + if (astop) + g_object_unref (astop); + + return NULL; +} + + +static GSList* +hafas_binary_parse_trips (const char *data, gsize length) +{ + HafasBin6Header *header; +#ifdef ENABLE_DEBUG + HafasBin6Loc *start, *end; +#endif + HafasBin6ExtHeader *ext; + HafasBin6TripDetailsHeader *details; + GSList *trips = NULL; + guint16 version; + const gchar *encoding; + + g_return_val_if_fail (data, NULL); + g_return_val_if_fail (length, NULL); + + version = *(guint16*)data; + g_return_val_if_fail(version == 6, NULL); + + g_return_val_if_fail(sizeof (HafasBin6Header) < length, NULL); + header = (HafasBin6Header*) data; +#ifdef ENABLE_DEBUG + start = (HafasBin6Loc*)&header->start; + end = (HafasBin6Loc*)&header->end; +#endif + LPF_DEBUG("%d Trips from '%s' to '%s'", + header->num_trips, + HAFAS_BIN6_STR(data, start->name_off), + HAFAS_BIN6_STR(data, end->name_off)); + + g_return_val_if_fail ((sizeof (HafasBin6Header) + + sizeof(HafasBin6Header) * header->num_trips) < length, + NULL); + + ext = HAFAS_BIN6_EXT_HEADER(data); + g_return_val_if_fail (header->ext + + sizeof(HafasBin6ExtHeader) < length, NULL); + + /* We might not have attrs_index0 */ + g_return_val_if_fail(sizeof (HafasBin6ExtHeader) - 1 < ext->length, NULL); + + LPF_DEBUG("Ext length: 0x%.4x", ext->length); + LPF_DEBUG("Errors: 0x%.4x", ext->err); + LPF_DEBUG("Sequence: 0x%.4x", ext->seq); + LPF_DEBUG("Detail table: 0x%.4x", ext->details_tbl); + encoding = HAFAS_BIN6_STR(data, ext->enc_off); + LPF_DEBUG("Encoding: %s", encoding); + LPF_DEBUG("Request Id: %s", HAFAS_BIN6_STR(data, ext->req_id_off)); + + if (ext->err) { + g_warning ("Error %d", ext->err); + goto out; + } + + if (ext->seq <= 0) { + g_warning("Illegal sequence number %d", ext->seq); + goto out; + } + + g_return_val_if_fail (ext->details_tbl + + sizeof (HafasBin6TripDetailsHeader) < length, NULL); + details = HAFAS_BIN6_TRIP_DETAILS_HEADER(data); + LPF_DEBUG("Trip detail version: %d", details->version); + g_return_val_if_fail (details->version == 1, NULL); + g_return_val_if_fail (details->stop_size == sizeof(HafasBin6TripStop), NULL); + g_return_val_if_fail (details->part_detail_size == sizeof(HafasBin6TripPartDetail), NULL); + + trips = hafas_binary_parse_each_trip (data, header->num_trips, header->days, encoding); + g_return_val_if_fail (trips, NULL); + out: + return trips; +} + +static gint +decompress (const gchar *in, gsize inlen, + gchar **out, gsize *outlen, + GError **err) { + gint ret = -1; + gsize read, written; + GConverter *decomp = NULL; + GConverterResult conv; + gsize outbuflen = inlen * 2; + gchar *outbuf = NULL; + + g_return_val_if_fail (inlen > 0, ret); + g_return_val_if_fail (in, ret); + g_return_val_if_fail (err, ret); + + decomp = (GConverter *)g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP); + while (TRUE) { + g_free (outbuf); + outbuf = g_try_malloc (outbuflen); + if (outbuf == NULL) + break; + conv = g_converter_convert (decomp, + in, inlen, + outbuf, outbuflen, + G_CONVERTER_INPUT_AT_END, + &read, &written, + err); + if (conv == G_CONVERTER_ERROR) { + if ((*err)->code == G_IO_ERROR_NO_SPACE) { + outbuflen *= 2; + g_clear_error (err); + continue; + } else { + break; + } + } else if (conv == G_CONVERTER_FINISHED) { + if (read != inlen) { + g_warning ("Expected %" G_GSIZE_FORMAT + ", got %" G_GSIZE_FORMAT, inlen, read); + break; + } + ret = 0; + break; + } else { + g_warning ("Unhandled condition %d", conv); + ret = -1; + break; + } + } + + if (ret == 0) { + *out = outbuf; + *outlen = written; + } else + g_free (outbuf); + + g_object_unref (decomp); + return ret; +} + +static void +got_trips (SoupSession *session, SoupMessage *msg, gpointer user_data) +{ + GSList *trips = NULL; + LpfProviderGotItUserData *trips_data = (LpfProviderGotItUserData*)user_data; + LpfProviderGotTripsNotify callback; + LpfProviderDeDb *self; + gpointer data; + gchar *decomp = NULL; + gsize len; + GError *err = NULL; + + g_return_if_fail(session); + g_return_if_fail(msg); + g_return_if_fail(user_data); + + g_object_ref (msg); + + callback = trips_data->callback; + data = trips_data->user_data; + self = (LpfProviderDeDb*)trips_data->self; + g_free (trips_data); + + LPF_DEBUG("Status: %d", msg->status_code); + if (!SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)) { + g_set_error (&err, + LPF_PROVIDER_ERROR, + LPF_PROVIDER_ERROR_REQUEST_FAILED, + "HTTP request failed: %d", msg->status_code); + goto out; + } + + log_response_body (self, msg, "trip"); + + if (decompress(msg->response_body->data, msg->response_body->length, &decomp, &len, &err) < 0) { + goto out; + } + + LPF_DEBUG("Decompressed to %" G_GSIZE_FORMAT " bytes", len); + if ((trips = hafas_binary_parse_trips(decomp, len)) == NULL) { + g_set_error (&err, + LPF_PROVIDER_ERROR, + LPF_PROVIDER_ERROR_PARSE_FAILED, + "Failed to parse trips"); + goto out; + } + +out: + g_object_unref (msg); + g_free (decomp); + + (*callback)(trips, data, err); +} + + +static gint +lpf_provider_de_db_get_trips (LpfProvider *self, + LpfLoc *start, + LpfLoc *end, + GDateTime *date, + guint64 flags, + LpfProviderGotTripsNotify callback, + gpointer user_data) +{ + LpfProviderDeDbPrivate *priv = GET_PRIVATE(self); + SoupMessage *msg; + SoupURI *uri = NULL; + LpfProviderGotItUserData *trips_data = NULL; + gint ret = -1; + gchar *datestr = NULL, *timestr = NULL; + /* allowed vehicle types */ + const gchar *train_restriction = "11111111111111"; + /* whether time is arrival or departure time */ + const gchar *by_departure = "1"; + char *start_id = NULL, *end_id = NULL; + + g_return_val_if_fail (start, -1); + g_return_val_if_fail (end, -1); + g_return_val_if_fail (callback, -1); + g_return_val_if_fail (priv->session, -1); + g_return_val_if_fail (date, -1); + + g_object_ref (start); + g_object_ref (end); + + trips_data = g_try_malloc(sizeof(LpfProviderDeDbPrivate)); + if (!trips_data) + goto out; + + datestr = g_date_time_format (date, "%d.%m.%y"); + timestr = g_date_time_format (date, "%H:%m"); + + start_id = lpf_loc_get_opaque(start); + end_id = lpf_loc_get_opaque(end); + if (start_id == NULL || end_id == NULL) { + g_warning ("Details missing."); + goto out; + } + + uri = soup_uri_new (TRIPS_URL); + soup_uri_set_query_from_fields (uri, + "start", "Suchen", + "REQ0JourneyStopsS0ID", start_id, + "REQ0JourneyStopsZ0ID", end_id, + "REQ0JourneyDate", datestr, + "REQ0JourneyTime", timestr, + "REQ0HafasSearchForw", by_departure, + "REQ0JourneyProduct_prod_list_1", train_restriction, + "h2g-direct", "11", NULL); + + LPF_DEBUG ("URI: %s", soup_uri_to_string (uri, FALSE)); + + msg = soup_message_new_from_uri ("GET", uri); + if (!msg) + goto out; + + trips_data->user_data = user_data; + trips_data->callback = callback; + trips_data->self = self; + + soup_session_queue_message (priv->session, msg, got_trips, trips_data); + ret = 0; + out: + g_free (datestr); + g_free (timestr); + g_object_unref (start); + g_object_unref (end); + if (ret < 0) + g_free (trips_data); + return ret; +} + + +static void +lpf_provider_de_db_class_init (LpfProviderDeDbClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + LpfProviderClass *plugin_class = LPF_PROVIDER_CLASS (klass); + + g_type_class_add_private (klass, sizeof (LpfProviderDeDbPrivate)); + + plugin_class->activate = lpf_provider_de_db_activate; + plugin_class->deactivate = lpf_provider_de_db_deactivate; + plugin_class->get_locs = lpf_provider_de_db_get_locs; + plugin_class->get_trips = lpf_provider_de_db_get_trips; + object_class->finalize = lpf_provider_de_db_finalize; +} + +static void +lpf_provider_de_db_init (LpfProviderDeDb *self) +{ +} + +LpfProviderDeDb * +lpf_provider_de_db_new (void) +{ + return g_object_new (LPF_TYPE_PROVIDER_DE_DB, LPF_PROVIDER_PROP_NAME, + PROVIDER_NAME, NULL); +} diff --git a/src/providers/de-db.h b/src/providers/de-db.h new file mode 100644 index 0000000..a0278c9 --- /dev/null +++ b/src/providers/de-db.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2014 Guido Guenther <agx@sigxcpu.org> + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _LPF_PROVIDER_DE_DB_H +#define _LPF_PROVIDER_DE_DB_H + +#include "lpf-provider.h" + +G_BEGIN_DECLS +#define LPF_TYPE_PROVIDER_DE_DB lpf_provider_de_db_get_type() +#define LPF_PROVIDER_DE_DB(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), LPF_TYPE_PROVIDER_DE_DB, LpfProviderDe_Db)) +#define LPF_PROVIDER_DE_DB_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), LPF_TYPE_PROVIDER_DE_DB, LpfProviderDe_DbClass)) +#define LPF_IS_PROVIDER_DE_DB(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LPF_TYPE_PROVIDER_DE_DB)) +#define LPF_IS_PROVIDER_DE_DB_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), LPF_TYPE_PROVIDER_DE_DB)) +#define LPF_PROVIDER_DE_DB_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), LPF_TYPE_PROVIDER_DE_DB, LpfProviderDe_DbClass)) + +typedef struct { + LpfProvider parent; +} LpfProviderDeDb; + +typedef struct { + LpfProviderClass parent_class; +} LpfProviderDeDbClass; + +GType lpf_provider_de_db_get_type (void); + +LpfProviderDeDb *lpf_provider_de_db_new (void); + +G_END_DECLS +#endif /* _LPF_PROVIDER_DB */ diff --git a/src/providers/hafas-bin6-format.h b/src/providers/hafas-bin6-format.h new file mode 100644 index 0000000..e2878b0 --- /dev/null +++ b/src/providers/hafas-bin6-format.h @@ -0,0 +1,323 @@ +/* + * hafas-bin6-format.h Hafas Binary 6 format + * + * Copyright (C) 2014 Guido Günther + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Author: Guido Günther <agx@sigxcpu.org> + */ + +#ifndef _HAFAS_BIN6_FORMAT_H +#define _HAFAS_BIN6_FORMAT_H + +/** + * Hafas Binary format version 6 + * + * The binary format uses several headers that mark contain the start + * different tables (*_tbl variables). An byte offset into such a table + * is marked by a variable ending in _off. If the elements in a table + * are fixed size (array) indexes are used. These end in _idx. + * + * Based on AbstractHafasProvider in public-transport-enabler and + * + */ + +/** + * HafasBin6Header: + * hafas binary format header version 6 + * +*/ +typedef struct _HafasBin6Header { + guint16 version; /* 0x00 */ + gchar start[14]; /* HafasBin6Loc */ /* 0x02 */ + gchar end[14]; /* HafasBin6Loc */ /* 0x10 */ + guint16 num_trips; /* # of trips in this resp. */ /* 0x1e */ + guint32 service_tbl; /* Start of service table */ /* 0x20 */ + guint32 strings_tbl; /* start of strings table */ /* 0x24 */ + gint16 days; /* date base in days since 1980 */ /* 0x28 */ + gchar unknown0[12]; /* 0x2a */ + guint32 stations_tbl; /* start of stations table */ /* 0x36 */ + guint32 comments_tbl; /* start of comments table */ /* 0x3a */ + gchar unknown1[8]; /* 0x3e */ + guint32 ext; /* Start of extenson header */ /* 0x46 */ +} __attribute__ ((packed)) HafasBin6Header; + + +typedef enum _HafasBin6LocTypes { + HAFAS_BIN6_LOC_TYPE_STATION = 1, + HAFAS_BIN6_LOC_TYPE_ADDRESS = 2, + HAFAS_BIN6_LOC_TYPE_POI = 3, +} HafasBin6LocTypes; + +/** + * HafasBin6Loc: + * + * Start and end location from #HafasBin6Header + */ +typedef struct _HafasBin6Loc { + guint16 name_off; /* offset into string table */ /* 0x00 */ + guint16 unknown0; /* 0x02 */ + guint16 type; /* as in HafasBinLocTypes */ /* 0x04 */ + gint32 lon; /* longitude * 10^6 */ /* 0x06 */ + gint32 lat; /* latitude * 10^6 */ /* 0x0a */ +} __attribute__ ((packed)) HafasBin6Loc; + +/** + * HafasBin6Trip: + * + * One entry per trip, follows directly after #HafasBin6Header + */ +typedef struct _HafasBin6Trip { + guint16 service_off; /* offset into service days table */ /* 0x00 */ + guint32 parts_off; /* offset after header */ /* 0x02 */ + guint16 part_cnt; /* number of parts in this trip */ /* 0x06 */ + guint16 changes; /* number of train changes in this trip */ /* 0x08 */ + guint16 unknown0; /* 0x0a */ +} __attribute__ ((packed)) HafasBin6Trip; + +/** + * HafasBin6Station: + * + * A station in the stations table as pointed to by the #HafasBin6Header + */ +typedef struct _HafasBin6Station { + guint16 name_off; /* offset in string table */ /* 0x00 */ + guint32 id; /* id of this station */ /* 0x02 */ + gint32 lon; /* longitute * 10^6 */ /* 0x06 */ + gint32 lat; /* latitude * 10^6 */ /* 0x0a */ +} __attribute__ ((packed)) HafasBin6Station; + +/** + * HafasBin6Errors: + * + * Errors in err field of #HafasBin6ExtHeader + */ +typedef enum _HafasBin6Errors { + HAFAS_BIN6_ERROR_NONE = 0, + HAFAS_BIN6_ERROR_SESSION_EXPIRED = 1, + /* TBD */ +} HafasBin6Errors; + +/** + * HafasBin6ExtHeader: + * + * Hafas Binary format version 6 extension header. If attrs_index0 != + * 0 and length >= 0x30 then attribute indexes for all trips follow + * after this header. Location of this header is determined by ext in + * the #HafasBin6Header. + */ +typedef struct _HafasBin6ExtHeader { + guint32 length; /* 0x00 */ + guint32 unknown0; /* 0x04 */ + guint16 seq; /* 0x08 */ + guint16 req_id_off; /* request id offset into string table */ /* 0x0a */ + guint32 details_tbl; /* offset to trip details header */ /* 0x0c */ + guint16 err; /* 0x10 */ + gchar unknown1[14]; /* 0x12 */ + guint16 enc_off; /* encoding offset into string table */ /* 0x20 */ + guint16 ld_off; /* ld offset into string table */ /* 0x22 */ + guint16 attrs_off; /* attributes offset */ /* 0x24 */ + gchar pad[6]; /* 0x26 */ + guint32 attrs_index0; /* attribute indexes start here */ /* 0x2c */ +} HafasBin6ExtHeader; + +/** + * HafasBin6TripDetailsHeader: + * + * Header for all trip details following this header. The details_index_off + * is relative to this header and stores just another offset to the final + * location of the #HafasBin6TripDetail. + */ +typedef struct _HafasBin6TripDetailsHeader { + guint16 version; /* 0x00 */ + guint16 unknown0; /* 0x02 */ + guint16 details_index_off; /* 0x04 */ + guint16 part_details_off; /* size of part details struct */ /* 0x06 */ + guint16 part_detail_size; /* 0x08 */ + guint16 stop_size; /* size of stop struct */ /* 0x0a */ + guint16 stops_off; /* 0x0c */ +} HafasBin6TripDetailsHeader; + +/** + * HafasBin6ServiceDay: + * + * The service day table is used to calculate day offsets on top of + * #HafasBin6Header days. See #hafas_binary_parse_service_day for details. + */ +typedef struct _HafasBin6ServiceDay { + guint16 days_off; /* offset into string table */ /* 0x00 */ + guint16 byte_base; /* days * 8 */ /* 0x02 */ + guint16 byte_len; /* # of bytes to follow */ /* 0x04 */ + gchar byte0; /* first day offset byte */ /* 0x06 */ +} __attribute__ ((packed)) HafasBin6ServiceDay; + +typedef enum _HafasBin6TripDetailRTStatus { + HAFAS_BIN6_RTSTATUS_CANCELLED = 0x0002, + HAFAS_BIN6_RTSTATUS_NORMAL = 0xFFFF, +} HafasBin6TripDetailRTStatus; + +/** + * HafasBin6TripDetail: + * + * Status of this trip + */ +typedef struct _HafasBin6TripDetail { + guint16 rt_status; /* real time status */ /* 0x00 */ + guint16 delay; /* 0x02 */ +} HafasBin6TripDetail; + +typedef enum _HafasBin6TripPartType { + footway = 1, + train = 2, +} HafasBin6TripPartType; + +/** + * HafasBin6TripPartType: + * + * A part of the trip using one "vehicle". + */ +typedef struct _HafasBin6TripPart { + guint16 dep; /* planned departure time at start */ /* 0x00 */ + guint16 dep_off; /* start, offset into stations table */ /* 0x02 */ + guint16 arr; /* planned arrival time at end */ /* 0x04 */ + guint16 arr_off; /* end, offset into stations table */ /* 0x06 */ + guint16 type; /* 0x08 */ + guint16 line_off; /* offset into string table */ /* 0x0a */ + guint16 dep_pos_off; /* offset into string table */ /* 0x0c */ + guint16 arr_pos_off; /* offset into string table */ /* 0x0e */ + guint16 attr_index; /* 0x10 */ + guint16 comments_off; /* offset into comments table */ /* 0x12 */ +} HafasBin6TripPart; + +/** + * HAFAS_BIN6_NO_PLATFORM: no platform for this stop + */ +#define HAFAS_BIN6_NO_PLATFORM "---" +/** + * HAFAS_BIN6_NO_REALTIME: no realtime information for this stop + */ +#define HAFAS_BIN6_NO_REALTIME 0xFFFF + +/** + * HafasBin6TripPartDetail: + * + * More details of one part of the trip. In contrast to + * #HafasBin6TripPart times and platforms are predicted instead of + * planned values. + */ +typedef struct _HafasBin6TripPartDetail { + guint16 dep_pred; /* predicted departure time */ /* 0x00 */ + guint16 arr_pred; /* predicted arrival time */ /* 0x02 */ + guint16 dep_pos_pred_off; /* offset into string table */ /* 0x04 */ + guint16 arr_pos_pred_off; /* offset into string table */ /* 0x06 */ + guint32 unknown0; /* 0x08 */ + guint16 stop_index; /* index of first stop */ /* 0x0c */ + guint16 stops_cnt; /* number of stops */ /* 0x0e */ +} HafasBin6TripPartDetail; + +/** + * HafasBin6Stop: + * + * Platforms and times (planned and predicted) for stops on the trip + */ +typedef struct _HafasBin6TripStop { + guint16 dep; /* planned departure time at this stop */ /* 0x00 */ + guint16 arr; /* planned arrival time at this stop */ /* 0x02 */ + guint16 dep_pos_off; /* platform, offset into string table */ /* 0x04 */ + guint16 arr_pos_off; /* platfrom, offset into string table */ /* 0x06 */ + guint32 unknown0; /* 0x08 */ + guint16 dep_pred; /* predicted departure time at this stop */ /* 0x0c */ + guint16 arr_pred; /* predicted arrival time at this stop */ /* 0x0e */ + guint16 dep_pos_pred_off; /* offset into string table */ /* 0x10 */ + guint16 arr_pos_pred_off; /* offset into string table */ /* 0x12 */ + guint32 unknown1; /* 0x14 */ + guint16 stop_idx; /* index into stations table */ /* 0x18 */ + +} __attribute__ ((packed)) HafasBin6TripStop; + +typedef struct _HafasBin6Attr { + guint16 key_off; /* offset into string table */ /* 0x00 */ + guint16 val_off; /* offset into string table */ /* 0x02 */ +} HafasBin6Attr; + +/* + * Access to headers and tables + */ +/* the main header */ +#define HAFAS_BIN6_HEADER(data) ((HafasBin6Header*)(data)) +/* extension header */ +#define HAFAS_BIN6_EXT_HEADER(data) ((HafasBin6ExtHeader*)((data) + (((HafasBin6Header*)(data))->ext))) +/* trip details header */ +#define HAFAS_BIN6_TRIP_DETAILS_HEADER(data) ((HafasBin6TripDetailsHeader*)((data) + HAFAS_BIN6_EXT_HEADER(data)->details_tbl)) +/* trip details index table */ +#define _HAFAS_BIN6_TRIP_DETAILS_INDEX(data) ((gchar*)(((gchar*)HAFAS_BIN6_TRIP_DETAILS_HEADER(data)) + \ + (HAFAS_BIN6_TRIP_DETAILS_HEADER(data)->details_index_off))) +/* trip part details index table */ +#define _HAFAS_BIN6_TRIP_PART_DETAILS_INDEX(data) ((gchar*)(((gchar*)HAFAS_BIN6_TRIP_DETAILS_HEADER(data)) + \ + (HAFAS_BIN6_TRIP_DETAILS_HEADER(data)->part_details_off))) +/* stops index table */ +#define _HAFAS_BIN6_STOPS_INDEX(data) ((gchar*)(((gchar*)HAFAS_BIN6_TRIP_DETAILS_HEADER(data)) + \ + (HAFAS_BIN6_TRIP_DETAILS_HEADER(data)->stops_off))) +/* trips table */ +#define _HAFAS_BIN6_TRIPS_TABLE(data) ((gchar*)((data) + sizeof(HafasBin6Header))) + +/* + * Access to the data structures making up the trips + */ +/* Start and end of trip */ +#define HAFAS_BIN6_START(data) ((HafasBin6Loc*)(((HafasBin6Header*)data)->start)) +#define HAFAS_BIN6_END(data) ((HafasBin6Loc*)(((HafasBin6Header*)data)->end)) +/* trip details and trip part details use an indirection via a common index table */ +#define _HAFAS_BIN6_TRIP_INDEX(data, idx) \ + (*((guint16*)(_HAFAS_BIN6_TRIP_DETAILS_INDEX(data) + 2 * (idx)))) +/* Get a string at offset off */ +#define HAFAS_BIN6_STR(data, off) \ + ((const gchar*)((gchar*)((data) + (((HafasBin6Header*)(data))->strings_tbl) + (off)))) +/* Get the n-th station from the staitons table */ +#define HAFAS_BIN6_STATION(data, idx) \ + ((HafasBin6Station*)((data) + (((HafasBin6Header*)(data))->stations_tbl) + ((idx) * sizeof(HafasBin6Station)))) +/* Get the idx-th trip */ +#define HAFAS_BIN6_TRIP(data, idx) \ + ((HafasBin6Trip*)((data) + sizeof(HafasBin6Header) + ((idx) * sizeof(HafasBin6Trip)))) +/* Get the service table entry for the idx-th trip */ +#define HAFAS_BIN6_SERVICE_DAY(data, idx) \ + ((HafasBin6ServiceDay*)((data) + (((HafasBin6Header*)(data))->service_tbl) + \ + (HAFAS_BIN6_TRIP(data, idx)->service_off))) +/* Get the idy-th part of the idx-th trip */ +#define HAFAS_BIN6_TRIP_PART(data, idx, idy) \ + ((HafasBin6TripPart*)(_HAFAS_BIN6_TRIPS_TABLE(data) + \ + (HAFAS_BIN6_TRIP(data, idx)->parts_off) + \ + idy * sizeof(HafasBin6TripPart))) +/* Get the trip details for the idx-th trip */ +#define HAFAS_BIN6_TRIP_DETAIL(data, idx) \ + ((HafasBin6TripDetail*)(_HAFAS_BIN6_TRIP_DETAILS_INDEX(data) + \ + _HAFAS_BIN6_TRIP_INDEX(data, idx))) +/* Get the trip part details for the idx-th trip and the idy-th part */ +#define HAFAS_BIN6_TRIP_PART_DETAIL(data, idx, idy) \ + ((HafasBin6TripPartDetail*)(_HAFAS_BIN6_TRIP_PART_DETAILS_INDEX(data) + \ + _HAFAS_BIN6_TRIP_INDEX(data, idx) + \ + ((idy) * sizeof(HafasBin6TripPartDetail)))) +/* Get the idz-th stop of the idy-th part in the idx-th trip */ +#define HAFAS_BIN6_STOP(data, idx, idy, idz) \ + ((HafasBin6TripStop*)(_HAFAS_BIN6_STOPS_INDEX(data) + \ + (HAFAS_BIN6_TRIP_PART_DETAIL(data, idx, idy)->stop_index) * sizeof(HafasBin6TripStop) + \ + idz * sizeof(HafasBin6TripStop))) + + +/* Convert hafas longitude/latitude information to floating point */ +#define HAFAS_BIN6_LL_DOUBlE(l) ((gdouble) (l) / 1000000.0) + +#endif /* _HAFAS_BINARY_H */ diff --git a/src/providers/hafas-bin6.c b/src/providers/hafas-bin6.c new file mode 100644 index 0000000..e860284 --- /dev/null +++ b/src/providers/hafas-bin6.c @@ -0,0 +1,119 @@ +/* + * hafas-bin6.c: HAFAS Binary Format Version 6 provider + * + * Copyright (C) 2014 Guido Günther + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Author: Guido Günther <agx@sigxcpu.org> + */ + +#include <config.h> +#include <libsoup/soup.h> + + +#include "hafas-bin6.h" +#include "lpf-loc.h" +#include "lpf-priv.h" + +gint +hafas_bin6_parse_station(const gchar *data, guint16 off, LpfLoc *loc, const char *enc) +{ + gchar *name; + HafasBin6Station *station; + gdouble lon, lat; + gint ret = -1; + + station = HAFAS_BIN6_STATION(data, off); + + name = g_convert (HAFAS_BIN6_STR(data, station->name_off), + -1, + "utf-8", + enc, + NULL, NULL, NULL); + if (name == NULL) { + g_warning ("Failed to convert station name at %d", off); + goto err; + } + lon = HAFAS_BIN6_LL_DOUBlE (station->lon); + lat = HAFAS_BIN6_LL_DOUBlE (station->lat); + + LPF_DEBUG("name: %s", name); + g_object_set (loc, "name", name, "long", lon, "lat", lat, + NULL); + ret = 0; +err: + return ret; +} + + +/** + * hafas_bin6_parse_service_day: + * + * Parse a servide day entry and return the offset from the base date in days. + */ +guint +hafas_bin6_parse_service_day (const char *data, int idx) +{ + gint i; + gchar bits; + guint off; + const HafasBin6ServiceDay *s; + + s = HAFAS_BIN6_SERVICE_DAY(data, idx); + off = s->byte_base * 8; + + /* look at all service bytes */ + for (i = 0; i < s->byte_len; i++) { + bits = (&(s->byte0))[i]; + if (bits == 0) { /* zero means +8 days */ + off += 8; + continue; + } + + /* count leading zeros meaning +1 day */ + while ((bits & 0x80) == 0) { + bits <<= 1; + off++; + } + break; + } + return off; +} + +/** + * hafas_bin6_date_time: + * @base_days: day off set from 1980-01-01 + * @off_days: day offset from base_days + * @hours: hour trip starts/ends + * @minutes: minute the trip starts/ends + * + * Calculate date and time from hafas bin 6 input + * + * Returns: the travel date and time as #GDateTime + */ +GDateTime* +hafas_bin6_date_time(guint base_days, guint off_days, guint hours, guint min) +{ + /* FIXME: should we always use Europe/Berlin as TZ? */ + GDateTime *dt, *base = g_date_time_new_local (1979, 12, 31, 0, 0, 0); + + dt = g_date_time_add (base, + (base_days + off_days) * G_TIME_SPAN_DAY + + hours * G_TIME_SPAN_HOUR + + min * G_TIME_SPAN_MINUTE); + g_date_time_unref (base); + return dt; +} diff --git a/src/providers/hafas-bin6.h b/src/providers/hafas-bin6.h new file mode 100644 index 0000000..a402da4 --- /dev/null +++ b/src/providers/hafas-bin6.h @@ -0,0 +1,33 @@ +/* + * hafas-bin6.h: hafas binary format version 6 + * + * Copyright (C) 2014 Guido Günther + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Author: Guido Günther <agx@sigxcpu.org> + */ + +#ifndef _HAFAS_BIN6_H +#define _HAFAS_BIN6_H + +#include "hafas-bin6-format.h" +#include "lpf-loc.h" + +gint hafas_bin6_parse_station(const gchar *data, guint16 off, LpfLoc *station, const char *enc); +guint hafas_bin6_parse_service_day (const char *data, int idx); +GDateTime* hafas_bin6_date_time(guint base_days, guint off_days, guint hours, guint min); + +#endif /* _HAFAS_BIN6_H */ diff --git a/src/providers/tests/Makefile.am b/src/providers/tests/Makefile.am new file mode 100644 index 0000000..721885d --- /dev/null +++ b/src/providers/tests/Makefile.am @@ -0,0 +1,53 @@ +include $(top_srcdir)/flymake.mk + +check_PROGRAMS = de-db hafas-bin6 + +AM_CFLAGS = \ + $(GLIB2_CFLAGS) \ + $(GIO2_CFLAGS) \ + $(GOBJECT2_CFLAGS) \ + $(GTHREAD2_CFLAGS) \ + $(LIBXML2_CFLAGS) \ + $(WARN_CFLAGS) \ + -I$(top_srcdir)/src \ + -DLPF_TEST_SRCDIR=\""$(abs_srcdir)"\" \ + $(NULL) + +LDADD = \ + $(top_builddir)/src/libplanfahr-0.0.la \ + $(GLIB2_LIBS) \ + $(GIO2_LIBS) \ + $(GOBJECT2_LIBS) \ + $(GTHREAD2_LIBS) \ + $(NULL) + +de_db_SOURCES = de-db.c +de_db_CFLAGS = \ + $(AM_CFLAGS) \ + $(LIBSOUP_CFLAGS) \ + $(LIBXML2_CFLAGS) \ + $(NULL) +de_db_LDADD = \ + $(LDADD) \ + $(LIBSOUP_LIBS) \ + $(LIBXML2_LIBS) \ + $(NULL) + +hafas_bin6_SOURCES = \ + hafas-bin6.c \ + ../hafas-bin6-format.h \ + $(NULL) +hafas_bin6_CFLAGS = \ + $(AM_CFLAGS) \ + $(NULL) +hafas_bin6_LDADD = \ + $(LDADD) \ + $(NULL) + + +uninstalled_testdir = tests/ +dist_uninstalled_test_DATA = \ + hafas-bin-6-station-query-1.bin \ + $(NULL) + +TESTS = $(check_PROGRAMS) diff --git a/src/providers/tests/de-db.c b/src/providers/tests/de-db.c new file mode 100644 index 0000000..db34274 --- /dev/null +++ b/src/providers/tests/de-db.c @@ -0,0 +1,138 @@ +/* + * db.c: Deutsche Bahn provider for libplanfahr + * + * Copyright (C) 2014 Guido Günther + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Author: Guido Günther <agx@sigxcpu.org> + */ + +#include "../de-db.c" +#include "../hafas-bin6.c" + +/* Make sure we can parse the station xml list as returned by the current Deutsche Bahn Hafas */ +static void +test_parse_stations (void) +{ + GSList *locs; + LpfLoc *loc; + char *name; + double lon, lat; + + char *xml = g_strjoin(NULL, +"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>", +"<ResC xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"No path to XML scheme defined\" ver=\"1.1\" prod=\"String\" lang=\"EN\">", +" <MLcRes flag=\"FINAL\">", +" <MLc t=\"ST\" n=\"Erpel(Rhein)\" i=\"A=1@O=Erpel(Rhein)@X=7241593@Y=50582067@U=80@L=008001858@B=1@p=1386184594@\" x=\"7241593\" y=\"50582067\" />", +" <MLc t=\"ST\" n=\"Erpel B42\" i=\"A=1@O=Erpel B42@X=7231174@Y=50583649@U=81@L=000441204@B=1@p=1387219918@\" x=\"7231174\" y=\"50583649\" />", +" <MLc t=\"ST\" n=\"Bahnhofstr., Erpel\" i=\"A=1@O=Bahnhofstr., Erpel@X=7243175@Y=50580943@U=81@L=000448602@B=1@p=1387219918@\" x=\"7243175\" y=\"50580943\" />", +" <MLc t=\"ST\" n=\"Rheinfähre, Erpel\" i=\"A=1@O=Rheinfähre, Erpel@X=7238401@Y=50581663@U=81@L=000441205@B=1@p=1387219918@\" x=\"7238401\" y=\"50581663\" />", +" <MLc t=\"ST\" n=\"Orsberg Ort, Erpel\" i=\"A=1@O=Orsberg Ort, Erpel@X=7243399@Y=50593061@U=81@L=000454657@B=1@p=1387219918@\" x=\"7243399\" y=\"50593061\" />", +" <MLc t=\"ST\" n=\"Neutor, Erpel\" i=\"A=1@O=Neutor, Erpel@X=7235282@Y=50584108@U=81@L=000454652@B=1@p=1387219918@\" x=\"7235282\" y=\"50584108\" />", +" <MLc t=\"ST\" n=\"Erpeldange Am Schlass, Luxemburg\" i=\"A=1@O=Erpeldange Am Schlass, Luxemburg@X=6114741@Y=49852458@U=81@L=000864180@B=1@p=1387219918@\" x=\"6114741\" y=\"49852458\" />", +" </MLcRes>", +"</ResC>", NULL); + + locs = parse_stations_xml(xml); + g_assert_nonnull (locs); + g_assert_cmpint (g_slist_length (locs), ==, 7); + + loc = g_slist_nth (locs, 4)->data; + g_object_get (loc, "name", &name, "long", &lon, "lat", &lat, NULL); + + g_assert_cmpstr (name, ==, "Orsberg Ort, Erpel"); + g_assert_cmpint (lon, ==, 7); + g_assert_cmpint (lat, ==, 50); + + g_slist_free_full (locs, g_object_unref); + g_free (xml); + g_free (name); +} + +/* Make sure we can parse the binary data trip information */ +static void +test_parse_trips (void) +{ + GSList *trips; + gchar *binary; + gsize length; + int i; + GSList *parts; + LpfTrip *trip; + LpfTripPart *part; + LpfLoc *stop; + gchar *name; + GDateTime *dep, *arr; + + g_assert_true(g_file_get_contents(LPF_TEST_SRCDIR "/hafas-bin-6-station-query-1.bin", &binary, &length, NULL)); + + trips = hafas_binary_parse_trips (binary, length); + + g_assert (g_slist_length (trips) == 3); + + for (i = 0; i < g_slist_length (trips); i++) { + trip = LPF_TRIP(g_slist_nth_data (trips, i)); + g_object_get (G_OBJECT(trip), "parts", &parts, NULL); + + part = LPF_TRIP_PART(g_slist_nth_data (parts, 0)); + g_object_get (G_OBJECT(part), "start", &stop, NULL); + g_object_get (G_OBJECT(stop), + "name", &name, + "arrival", &arr, + "departure", &dep, + NULL); + /* All trips start in Erpel */ + g_assert (!g_strcmp0 (name, "Erpel(Rhein)")); + g_free (name); + + g_assert (dep != NULL); + g_assert (arr == NULL); + g_date_time_unref (dep); + + /* All trips end in Unkel */ + part = LPF_TRIP_PART(g_slist_nth_data (parts, + g_slist_length(parts)-1)); + g_object_get (G_OBJECT(part), "end", &stop, NULL); + g_object_get (G_OBJECT(stop), + "name", &name, + "arrival", &arr, + "departure", &dep, + NULL); + + g_assert (dep == NULL); + g_assert (arr != NULL); + g_date_time_unref (arr); + + g_assert (g_strrstr (name, "Unkel") != NULL); + g_free (name); + } + + g_slist_free (trips); + +} + + +int main(int argc, char **argv) +{ + gboolean ret; + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/providers/de-db/parse_stations", test_parse_stations); + g_test_add_func ("/providers/de-db/parse_trips", test_parse_trips); + + ret = g_test_run (); + return ret; +} diff --git a/src/providers/tests/hafas-bin-6-station-query-1.bin b/src/providers/tests/hafas-bin-6-station-query-1.bin Binary files differnew file mode 100644 index 0000000..87afa7f --- /dev/null +++ b/src/providers/tests/hafas-bin-6-station-query-1.bin diff --git a/src/providers/tests/hafas-bin6.c b/src/providers/tests/hafas-bin6.c new file mode 100644 index 0000000..ea5e277 --- /dev/null +++ b/src/providers/tests/hafas-bin6.c @@ -0,0 +1,156 @@ +/* + * hafas-bin6.c: test hafas binary v6 parser + * + * Copyright (C) 2014 Guido Günther + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Author: Guido Günther <agx@sigxcpu.org> + */ + +#include <glib.h> + +#include "../hafas-bin6.h" + + +/* sanity check our parser's structures */ +static void +test_sizes (void) +{ + g_assert_cmpint(sizeof(HafasBin6Header), ==, 0x4a); + g_assert_cmpint(sizeof(HafasBin6Trip), ==, 0x0c); + g_assert_cmpint(sizeof(HafasBin6Loc), ==, 0x0e); + g_assert_cmpint(sizeof(HafasBin6ExtHeader), ==, 0x30); + g_assert_cmpint(sizeof(HafasBin6TripDetailsHeader), ==, 0x0e); + g_assert_cmpint(sizeof(HafasBin6Station), ==, 0x0e); + g_assert_cmpint(sizeof(HafasBin6ServiceDay), ==, 0x07); + g_assert_cmpint(sizeof(HafasBin6TripDetail), ==, 0x04); + g_assert_cmpint(sizeof(HafasBin6TripPart), ==, 0x14); + g_assert_cmpint(sizeof(HafasBin6TripPartDetail), ==, 0x10); + g_assert_cmpint(sizeof(HafasBin6TripStop), ==, 0x1a); +} + +/* + * Make sure we can parse the binary data trip information This is + * just to test the raw parser. The wrapping into Lpf objects and + * conversions of e.g. time and date are tested separately. + */ +static void +test_parse_erpel_unkel(void) +{ + gchar *bin; + gsize length; + HafasBin6Loc *start, *end; + HafasBin6ExtHeader *ext; + HafasBin6TripDetailsHeader *detail_header; + HafasBin6TripPartDetail *part_detail; + HafasBin6TripDetail *detail; + HafasBin6Trip *trip; + HafasBin6TripPart *part; + HafasBin6ServiceDay *day; + HafasBin6TripStop *stop; + HafasBin6Station *station; + gint i, j, k; + guint expected_changes[3] = { 0, 0, 0 }; + guint expected_parts[3] = { 1, 2, 1 }; + const gchar *expected_part_line[3][2] = { {"RB 12560", "doesnot" }, + {"Fussweg", "Bus 565" }, + {"RB 12562", "matter" }}; + const gint expected_part_dep[3][2] = { { 956, 0}, + { 958, 1003 }, + {1056, 0 }}; + + const gint expected_part_arr[3][2] = { { 959, 2}, + {1003, 1014}, + {1059, 0 }}; + guint expected_stops[3][2] = { {0, 0}, {0, 8}, {0, 0} }; + + g_assert_true(g_file_get_contents(LPF_TEST_SRCDIR "/hafas-bin-6-station-query-1.bin", &bin, &length, NULL)); + + g_assert_cmpint(HAFAS_BIN6_HEADER(bin)->num_trips, ==, 3); + + /* header information */ + start = HAFAS_BIN6_START(bin); + end = HAFAS_BIN6_END(bin); + g_assert_cmpint (start->lon, ==, 7241593); + g_assert_cmpint (start->lat, ==, 50582067); + g_assert_cmpint (start->type, ==, HAFAS_BIN6_LOC_TYPE_STATION); + g_assert_cmpint (end->lon, ==, 7219803); + g_assert_cmpint (end->lat, ==, 50602742); + g_assert_cmpint (end->type, ==, HAFAS_BIN6_LOC_TYPE_STATION); + + /* ext header information */ + ext = HAFAS_BIN6_EXT_HEADER(bin); + g_assert_cmpint (ext->err, ==, HAFAS_BIN6_ERROR_NONE); + g_assert_cmpstr (HAFAS_BIN6_STR(bin, ext->enc_off), ==, "iso-8859-1"); + g_assert_cmpstr (HAFAS_BIN6_STR(bin, ext->req_id_off), ==, "50.02519042.1387923122"); + g_assert_cmpint (ext->seq, ==, 1); + + /* detail header information */ + detail_header = HAFAS_BIN6_TRIP_DETAILS_HEADER(bin); + g_assert_cmpint (detail_header->version, ==, 1); + g_assert_cmpint (detail_header->stop_size, ==, sizeof(HafasBin6TripStop)); + g_assert_cmpint (detail_header->part_detail_size, == ,sizeof(HafasBin6TripPartDetail)); + + for (i = 0; i < HAFAS_BIN6_HEADER(bin)->num_trips; i++) { + trip = HAFAS_BIN6_TRIP(bin, i); + g_assert_cmpint (trip->changes, ==, expected_changes[i]); + g_assert_cmpint (trip->part_cnt, ==, expected_parts[i]); + + day = HAFAS_BIN6_SERVICE_DAY(bin, i); + g_assert_cmpint (day->byte_base, ==, 0); + g_assert_cmpint (day->byte_len, ==, 2); + g_assert_cmpint (day->byte0, ==, (gchar)(0x80)); + + detail = HAFAS_BIN6_TRIP_DETAIL(bin, i); + g_assert_cmpint (detail->rt_status, ==, HAFAS_BIN6_RTSTATUS_NORMAL); + g_assert_cmpint (detail->delay, ==, 0); + + for (j = 0; j < trip->part_cnt; j++) { + /* planned departures and arrivals */ + part = HAFAS_BIN6_TRIP_PART(bin, i, j); + g_assert_cmpstr (HAFAS_BIN6_STR(bin, part->line_off), ==, expected_part_line[i][j]); + g_assert_cmpint (part->arr, ==, expected_part_arr[i][j]); + g_assert_cmpint (part->dep, ==, expected_part_dep[i][j]); + /* predicted departures and arrivals */ + part_detail = HAFAS_BIN6_TRIP_PART_DETAIL(bin, i, j); + g_assert_cmpint (part_detail->arr_pred, ==, 65535); + g_assert_cmpint (part_detail->dep_pred, ==, 65535); + /* stops */ + g_assert_cmpint (part_detail->stops_cnt, ==, expected_stops[i][j]); + for (k = 0; k < part_detail->stops_cnt; k++) { + stop = HAFAS_BIN6_STOP(bin, i, j, k); + g_assert_cmpstr (HAFAS_BIN6_STR(bin, stop->dep_pos_pred_off), ==, "---"); + } + /* check a single station more thoroughly */ + stop = HAFAS_BIN6_STOP(bin, 1, 1, 2); + station = HAFAS_BIN6_STATION(bin, stop->stop_idx); + g_assert_cmpstr (HAFAS_BIN6_STR(bin, station->name_off), ==, "Heister Kapelle, Unkel"); + } + } +} + + +int main(int argc, char **argv) +{ + gboolean ret; + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/providers/hafas-bin6/sizes", test_sizes); + g_test_add_func ("/providers/hafas_bin6/parse_erpel_unkel", test_parse_erpel_unkel); + + ret = g_test_run (); + return ret; +} |