├── AUTHORS ├── COPYING ├── ChangeLog ├── INSTALL ├── Makefile.am ├── NEWS ├── README.md ├── autogen.sh ├── configure.ac ├── intltool-extract.in ├── intltool-merge.in ├── intltool-update.in └── src ├── Makefile.am └── bootchart-lite.c /AUTHORS: -------------------------------------------------------------------------------- 1 | Fred Chien 2 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Library General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License 307 | along with this program; if not, write to the Free Software 308 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 309 | 310 | 311 | Also add information on how to contact you by electronic and paper mail. 312 | 313 | If the program is interactive, make it output a short notice like this 314 | when it starts in an interactive mode: 315 | 316 | Gnomovision version 69, Copyright (C) year name of author 317 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 318 | This is free software, and you are welcome to redistribute it 319 | under certain conditions; type `show c' for details. 320 | 321 | The hypothetical commands `show w' and `show c' should show the appropriate 322 | parts of the General Public License. Of course, the commands you use may 323 | be called something other than `show w' and `show c'; they could even be 324 | mouse-clicks or menu items--whatever suits your program. 325 | 326 | You should also get your employer (if you work as a programmer) or your 327 | school, if any, to sign a "copyright disclaimer" for the program, if 328 | necessary. Here is a sample; alter the names: 329 | 330 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 331 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 332 | 333 | , 1 April 1989 334 | Ty Coon, President of Vice 335 | 336 | This General Public License does not permit incorporating your program into 337 | proprietary programs. If your program is a subroutine library, you may 338 | consider it more useful to permit linking proprietary applications with the 339 | library. If this is what you want to do, use the GNU Library General 340 | Public License instead of this License. 341 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | 2007-11-27 Michael Lauer 2 | 3 | * Add sample project 4 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | Installation Instructions 2 | ************************* 3 | 4 | Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, 5 | 2006 Free Software Foundation, Inc. 6 | 7 | This file is free documentation; the Free Software Foundation gives 8 | unlimited permission to copy, distribute and modify it. 9 | 10 | Basic Installation 11 | ================== 12 | 13 | Briefly, the shell commands `./configure; make; make install' should 14 | configure, build, and install this package. The following 15 | more-detailed instructions are generic; see the `README' file for 16 | instructions specific to this package. 17 | 18 | The `configure' shell script attempts to guess correct values for 19 | various system-dependent variables used during compilation. It uses 20 | those values to create a `Makefile' in each directory of the package. 21 | It may also create one or more `.h' files containing system-dependent 22 | definitions. Finally, it creates a shell script `config.status' that 23 | you can run in the future to recreate the current configuration, and a 24 | file `config.log' containing compiler output (useful mainly for 25 | debugging `configure'). 26 | 27 | It can also use an optional file (typically called `config.cache' 28 | and enabled with `--cache-file=config.cache' or simply `-C') that saves 29 | the results of its tests to speed up reconfiguring. Caching is 30 | disabled by default to prevent problems with accidental use of stale 31 | cache files. 32 | 33 | If you need to do unusual things to compile the package, please try 34 | to figure out how `configure' could check whether to do them, and mail 35 | diffs or instructions to the address given in the `README' so they can 36 | be considered for the next release. If you are using the cache, and at 37 | some point `config.cache' contains results you don't want to keep, you 38 | may remove or edit it. 39 | 40 | The file `configure.ac' (or `configure.in') is used to create 41 | `configure' by a program called `autoconf'. You need `configure.ac' if 42 | you want to change it or regenerate `configure' using a newer version 43 | of `autoconf'. 44 | 45 | The simplest way to compile this package is: 46 | 47 | 1. `cd' to the directory containing the package's source code and type 48 | `./configure' to configure the package for your system. 49 | 50 | Running `configure' might take a while. While running, it prints 51 | some messages telling which features it is checking for. 52 | 53 | 2. Type `make' to compile the package. 54 | 55 | 3. Optionally, type `make check' to run any self-tests that come with 56 | the package. 57 | 58 | 4. Type `make install' to install the programs and any data files and 59 | documentation. 60 | 61 | 5. You can remove the program binaries and object files from the 62 | source code directory by typing `make clean'. To also remove the 63 | files that `configure' created (so you can compile the package for 64 | a different kind of computer), type `make distclean'. There is 65 | also a `make maintainer-clean' target, but that is intended mainly 66 | for the package's developers. If you use it, you may have to get 67 | all sorts of other programs in order to regenerate files that came 68 | with the distribution. 69 | 70 | Compilers and Options 71 | ===================== 72 | 73 | Some systems require unusual options for compilation or linking that the 74 | `configure' script does not know about. Run `./configure --help' for 75 | details on some of the pertinent environment variables. 76 | 77 | You can give `configure' initial values for configuration parameters 78 | by setting variables in the command line or in the environment. Here 79 | is an example: 80 | 81 | ./configure CC=c99 CFLAGS=-g LIBS=-lposix 82 | 83 | *Note Defining Variables::, for more details. 84 | 85 | Compiling For Multiple Architectures 86 | ==================================== 87 | 88 | You can compile the package for more than one kind of computer at the 89 | same time, by placing the object files for each architecture in their 90 | own directory. To do this, you can use GNU `make'. `cd' to the 91 | directory where you want the object files and executables to go and run 92 | the `configure' script. `configure' automatically checks for the 93 | source code in the directory that `configure' is in and in `..'. 94 | 95 | With a non-GNU `make', it is safer to compile the package for one 96 | architecture at a time in the source code directory. After you have 97 | installed the package for one architecture, use `make distclean' before 98 | reconfiguring for another architecture. 99 | 100 | Installation Names 101 | ================== 102 | 103 | By default, `make install' installs the package's commands under 104 | `/usr/local/bin', include files under `/usr/local/include', etc. You 105 | can specify an installation prefix other than `/usr/local' by giving 106 | `configure' the option `--prefix=PREFIX'. 107 | 108 | You can specify separate installation prefixes for 109 | architecture-specific files and architecture-independent files. If you 110 | pass the option `--exec-prefix=PREFIX' to `configure', the package uses 111 | PREFIX as the prefix for installing programs and libraries. 112 | Documentation and other data files still use the regular prefix. 113 | 114 | In addition, if you use an unusual directory layout you can give 115 | options like `--bindir=DIR' to specify different values for particular 116 | kinds of files. Run `configure --help' for a list of the directories 117 | you can set and what kinds of files go in them. 118 | 119 | If the package supports it, you can cause programs to be installed 120 | with an extra prefix or suffix on their names by giving `configure' the 121 | option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. 122 | 123 | Optional Features 124 | ================= 125 | 126 | Some packages pay attention to `--enable-FEATURE' options to 127 | `configure', where FEATURE indicates an optional part of the package. 128 | They may also pay attention to `--with-PACKAGE' options, where PACKAGE 129 | is something like `gnu-as' or `x' (for the X Window System). The 130 | `README' should mention any `--enable-' and `--with-' options that the 131 | package recognizes. 132 | 133 | For packages that use the X Window System, `configure' can usually 134 | find the X include and library files automatically, but if it doesn't, 135 | you can use the `configure' options `--x-includes=DIR' and 136 | `--x-libraries=DIR' to specify their locations. 137 | 138 | Specifying the System Type 139 | ========================== 140 | 141 | There may be some features `configure' cannot figure out automatically, 142 | but needs to determine by the type of machine the package will run on. 143 | Usually, assuming the package is built to be run on the _same_ 144 | architectures, `configure' can figure that out, but if it prints a 145 | message saying it cannot guess the machine type, give it the 146 | `--build=TYPE' option. TYPE can either be a short name for the system 147 | type, such as `sun4', or a canonical name which has the form: 148 | 149 | CPU-COMPANY-SYSTEM 150 | 151 | where SYSTEM can have one of these forms: 152 | 153 | OS KERNEL-OS 154 | 155 | See the file `config.sub' for the possible values of each field. If 156 | `config.sub' isn't included in this package, then this package doesn't 157 | need to know the machine type. 158 | 159 | If you are _building_ compiler tools for cross-compiling, you should 160 | use the option `--target=TYPE' to select the type of system they will 161 | produce code for. 162 | 163 | If you want to _use_ a cross compiler, that generates code for a 164 | platform different from the build platform, you should specify the 165 | "host" platform (i.e., that on which the generated programs will 166 | eventually be run) with `--host=TYPE'. 167 | 168 | Sharing Defaults 169 | ================ 170 | 171 | If you want to set default values for `configure' scripts to share, you 172 | can create a site shell script called `config.site' that gives default 173 | values for variables like `CC', `cache_file', and `prefix'. 174 | `configure' looks for `PREFIX/share/config.site' if it exists, then 175 | `PREFIX/etc/config.site' if it exists. Or, you can set the 176 | `CONFIG_SITE' environment variable to the location of the site script. 177 | A warning: not all `configure' scripts look for a site script. 178 | 179 | Defining Variables 180 | ================== 181 | 182 | Variables not defined in a site shell script can be set in the 183 | environment passed to `configure'. However, some packages may run 184 | configure again during the build, and the customized values of these 185 | variables may be lost. In order to avoid this problem, you should set 186 | them in the `configure' command line, using `VAR=value'. For example: 187 | 188 | ./configure CC=/usr/local2/bin/gcc 189 | 190 | causes the specified `gcc' to be used as the C compiler (unless it is 191 | overridden in the site shell script). 192 | 193 | Unfortunately, this technique does not work for `CONFIG_SHELL' due to 194 | an Autoconf bug. Until the bug is fixed you can use this workaround: 195 | 196 | CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash 197 | 198 | `configure' Invocation 199 | ====================== 200 | 201 | `configure' recognizes the following options to control how it operates. 202 | 203 | `--help' 204 | `-h' 205 | Print a summary of the options to `configure', and exit. 206 | 207 | `--version' 208 | `-V' 209 | Print the version of Autoconf used to generate the `configure' 210 | script, and exit. 211 | 212 | `--cache-file=FILE' 213 | Enable the cache: use and save the results of the tests in FILE, 214 | traditionally `config.cache'. FILE defaults to `/dev/null' to 215 | disable caching. 216 | 217 | `--config-cache' 218 | `-C' 219 | Alias for `--cache-file=config.cache'. 220 | 221 | `--quiet' 222 | `--silent' 223 | `-q' 224 | Do not print messages saying which checks are being made. To 225 | suppress all normal output, redirect it to `/dev/null' (any error 226 | messages will still be shown). 227 | 228 | `--srcdir=DIR' 229 | Look for the package's source code in directory DIR. Usually 230 | `configure' can determine that directory automatically. 231 | 232 | `configure' also accepts some other, not widely useful, options. Run 233 | `configure --help' for more details. 234 | 235 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = src 2 | 3 | # Extra clean files so that maintainer-clean removes *everything* 4 | MAINTAINERCLEANFILES = aclocal.m4 compile config.guess config.sub \ 5 | configure depcomp install-sh ltmain.sh \ 6 | Makefile.in missing config.h.in \ 7 | mkinstalldirs \ 8 | intltool-extract intltool-merge intltool-update 9 | 10 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cfsghost/bootchart-lite/45c5217d65529d57921f414c736ae8c48ffd1e9a/NEWS -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Bootchart Lite for Embedded System 2 | === 3 | 4 | Bootchart-lite is a tool for performance analysis and visualization of the Linux boot process. Basicly, bootchart-lite is same with [Bootchart project](http://www.bootchart.org) but more lightweight than Bootchart. 5 | 6 | Hardware platform of embedded system usually has less powerful, Original version of Bootchart is a little heavy cause imprecise information and data of Linux boot process was collected during boot time. For resolving this performance problem of bootchart, bootchart-lite was made with small effect on performance as a target. 7 | 8 | License 9 | --- 10 | Licensed under the GPL 11 | 12 | Authors 13 | --- 14 | Copyright(c) 2008-2015 Fred Chien <> 15 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | srcdir=`dirname $0` 4 | test -z "$srcdir" && srcdir=. 5 | 6 | PKG_NAME="bootchart-lite" 7 | 8 | (test -f $srcdir/configure.ac) || { 9 | echo -n "**Error**: Directory "\`$srcdir\'" does not look like the" 10 | echo " top-level $PKG_NAME directory" 11 | exit 1 12 | } 13 | 14 | which gnome-autogen.sh || { 15 | echo "You need to install gnome-common from the GNOME CVS" 16 | exit 1 17 | } 18 | 19 | REQUIRED_AUTOMAKE_VERSION=1.7 USE_GNOME2_MACROS=0 REQUIRED_GETTEXT_VERSION=0.10.36 . gnome-autogen.sh 20 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_PREREQ(2.53) 2 | AC_INIT(bootchart-lite, 0.0.1, http://www.openmoko.org/) 3 | AM_INIT_AUTOMAKE() 4 | AC_CONFIG_SRCDIR(src/bootchart-lite.c) 5 | dnl AM_CONFIG_HEADER(config.h) 6 | AM_MAINTAINER_MODE 7 | 8 | AC_ISC_POSIX 9 | AC_PROG_CC 10 | AC_PROG_CC_STDC 11 | AC_STDC_HEADERS 12 | #AC_PROG_LIBTOOL 13 | #AC_PROG_INTLTOOL([0.35.0]) 14 | 15 | dnl i18n support 16 | dnl GETTEXT_PACKAGE=AC_PACKAGE_NAME 17 | dnl AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE", [Gettext package]) 18 | dnl AC_SUBST(GETTEXT_PACKAGE) 19 | dnl AM_GLIB_GNU_GETTEXT 20 | 21 | dnl PKG_CHECK_MODULES(DEPENDENCIES, libmokoui2 gconf-2.0) 22 | 23 | #AC_SUBST(DEPENDENCIES) 24 | 25 | AC_OUTPUT([ 26 | Makefile 27 | src/Makefile 28 | ]) 29 | 30 | -------------------------------------------------------------------------------- /intltool-extract.in: -------------------------------------------------------------------------------- 1 | #!@INTLTOOL_PERL@ -w 2 | # -*- Mode: perl; indent-tabs-mode: nil; c-basic-offset: 4 -*- 3 | 4 | # 5 | # The Intltool Message Extractor 6 | # 7 | # Copyright (C) 2000-2001, 2003 Free Software Foundation. 8 | # 9 | # Intltool is free software; you can redistribute it and/or 10 | # modify it under the terms of the GNU General Public License as 11 | # published by the Free Software Foundation; either version 2 of the 12 | # License, or (at your option) any later version. 13 | # 14 | # Intltool is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | # General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU General Public License 20 | # along with this program; if not, write to the Free Software 21 | # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 22 | # 23 | # As a special exception to the GNU General Public License, if you 24 | # distribute this file as part of a program that contains a 25 | # configuration script generated by Autoconf, you may include it under 26 | # the same distribution terms that you use for the rest of that program. 27 | # 28 | # Authors: Kenneth Christiansen 29 | # Darin Adler 30 | # 31 | 32 | ## Release information 33 | my $PROGRAM = "intltool-extract"; 34 | my $PACKAGE = "intltool"; 35 | my $VERSION = "0.36.2"; 36 | 37 | ## Loaded modules 38 | use strict; 39 | use File::Basename; 40 | use Getopt::Long; 41 | 42 | ## Scalars used by the option stuff 43 | my $TYPE_ARG = "0"; 44 | my $LOCAL_ARG = "0"; 45 | my $HELP_ARG = "0"; 46 | my $VERSION_ARG = "0"; 47 | my $UPDATE_ARG = "0"; 48 | my $QUIET_ARG = "0"; 49 | my $SRCDIR_ARG = "."; 50 | 51 | my $FILE; 52 | my $OUTFILE; 53 | 54 | my $gettext_type = ""; 55 | my $input; 56 | my %messages = (); 57 | my %loc = (); 58 | my %count = (); 59 | my %comments = (); 60 | my $strcount = 0; 61 | 62 | my $XMLCOMMENT = ""; 63 | 64 | ## Use this instead of \w for XML files to handle more possible characters. 65 | my $w = "[-A-Za-z0-9._:]"; 66 | 67 | ## Always print first 68 | $| = 1; 69 | 70 | ## Handle options 71 | GetOptions ( 72 | "type=s" => \$TYPE_ARG, 73 | "local|l" => \$LOCAL_ARG, 74 | "help|h" => \$HELP_ARG, 75 | "version|v" => \$VERSION_ARG, 76 | "update" => \$UPDATE_ARG, 77 | "quiet|q" => \$QUIET_ARG, 78 | "srcdir=s" => \$SRCDIR_ARG, 79 | ) or &error; 80 | 81 | &split_on_argument; 82 | 83 | 84 | ## Check for options. 85 | ## This section will check for the different options. 86 | 87 | sub split_on_argument { 88 | 89 | if ($VERSION_ARG) { 90 | &version; 91 | 92 | } elsif ($HELP_ARG) { 93 | &help; 94 | 95 | } elsif ($LOCAL_ARG) { 96 | &place_local; 97 | &extract; 98 | 99 | } elsif ($UPDATE_ARG) { 100 | &place_normal; 101 | &extract; 102 | 103 | } elsif (@ARGV > 0) { 104 | &place_normal; 105 | &message; 106 | &extract; 107 | 108 | } else { 109 | &help; 110 | 111 | } 112 | } 113 | 114 | sub place_normal { 115 | $FILE = $ARGV[0]; 116 | $OUTFILE = "$FILE.h"; 117 | 118 | my $dirname = dirname ($OUTFILE); 119 | if (! -d "$dirname" && $dirname ne "") { 120 | system ("mkdir -p $dirname"); 121 | } 122 | } 123 | 124 | sub place_local { 125 | $FILE = $ARGV[0]; 126 | $OUTFILE = fileparse($FILE, ()); 127 | if (!-e "tmp/") { 128 | system("mkdir tmp/"); 129 | } 130 | $OUTFILE = "./tmp/$OUTFILE.h" 131 | } 132 | 133 | sub determine_type { 134 | if ($TYPE_ARG =~ /^gettext\/(.*)/) { 135 | $gettext_type=$1 136 | } 137 | } 138 | 139 | ## Sub for printing release information 140 | sub version{ 141 | print <<_EOF_; 142 | ${PROGRAM} (${PACKAGE}) $VERSION 143 | Copyright (C) 2000, 2003 Free Software Foundation, Inc. 144 | Written by Kenneth Christiansen, 2000. 145 | 146 | This is free software; see the source for copying conditions. There is NO 147 | warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 148 | _EOF_ 149 | exit; 150 | } 151 | 152 | ## Sub for printing usage information 153 | sub help { 154 | print <<_EOF_; 155 | Usage: ${PROGRAM} [OPTION]... [FILENAME] 156 | Generates a header file from an XML source file. 157 | 158 | It grabs all strings between <_translatable_node> and its end tag in 159 | XML files. Read manpage (man ${PROGRAM}) for more info. 160 | 161 | --type=TYPE Specify the file type of FILENAME. Currently supports: 162 | "gettext/glade", "gettext/ini", "gettext/keys" 163 | "gettext/rfc822deb", "gettext/schemas", 164 | "gettext/scheme", "gettext/xml", "gettext/quoted" 165 | -l, --local Writes output into current working directory 166 | (conflicts with --update) 167 | --update Writes output into the same directory the source file 168 | reside (conflicts with --local) 169 | --srcdir Root of the source tree 170 | -v, --version Output version information and exit 171 | -h, --help Display this help and exit 172 | -q, --quiet Quiet mode 173 | 174 | Report bugs to http://bugzilla.gnome.org/ (product name "$PACKAGE") 175 | or send email to . 176 | _EOF_ 177 | exit; 178 | } 179 | 180 | ## Sub for printing error messages 181 | sub error{ 182 | print STDERR "Try `${PROGRAM} --help' for more information.\n"; 183 | exit; 184 | } 185 | 186 | sub message { 187 | print "Generating C format header file for translation.\n" unless $QUIET_ARG; 188 | } 189 | 190 | sub extract { 191 | &determine_type; 192 | 193 | &convert; 194 | 195 | open OUT, ">$OUTFILE"; 196 | binmode (OUT) if $^O eq 'MSWin32'; 197 | &msg_write; 198 | close OUT; 199 | 200 | print "Wrote $OUTFILE\n" unless $QUIET_ARG; 201 | } 202 | 203 | sub convert { 204 | 205 | ## Reading the file 206 | { 207 | local (*IN); 208 | local $/; #slurp mode 209 | open (IN, "<$SRCDIR_ARG/$FILE") || die "can't open $SRCDIR_ARG/$FILE: $!"; 210 | $input = ; 211 | } 212 | 213 | &type_ini if $gettext_type eq "ini"; 214 | &type_keys if $gettext_type eq "keys"; 215 | &type_xml if $gettext_type eq "xml"; 216 | &type_glade if $gettext_type eq "glade"; 217 | &type_scheme if $gettext_type eq "scheme"; 218 | &type_schemas if $gettext_type eq "schemas"; 219 | &type_rfc822deb if $gettext_type eq "rfc822deb"; 220 | &type_quoted if $gettext_type eq "quoted"; 221 | } 222 | 223 | sub entity_decode_minimal 224 | { 225 | local ($_) = @_; 226 | 227 | s/'/'/g; # ' 228 | s/"/"/g; # " 229 | s/&/&/g; 230 | 231 | return $_; 232 | } 233 | 234 | sub entity_decode 235 | { 236 | local ($_) = @_; 237 | 238 | s/'/'/g; # ' 239 | s/"/"/g; # " 240 | s/<//g; 242 | s/&/&/g; 243 | 244 | return $_; 245 | } 246 | 247 | sub escape_char 248 | { 249 | return '\"' if $_ eq '"'; 250 | return '\n' if $_ eq "\n"; 251 | return '\\\\' if $_ eq '\\'; 252 | 253 | return $_; 254 | } 255 | 256 | sub escape 257 | { 258 | my ($string) = @_; 259 | return join "", map &escape_char, split //, $string; 260 | } 261 | 262 | sub type_ini { 263 | ### For generic translatable desktop files ### 264 | while ($input =~ /^(#(.+)\n)?^_.*=(.*)$/mg) { 265 | if (defined($2)) { 266 | $comments{$3} = $2; 267 | } 268 | $messages{$3} = []; 269 | } 270 | } 271 | 272 | sub type_keys { 273 | ### For generic translatable mime/keys files ### 274 | while ($input =~ /^\s*_\w+=(.*)$/mg) { 275 | $messages{$1} = []; 276 | } 277 | } 278 | 279 | sub type_xml { 280 | ### For generic translatable XML files ### 281 | my $tree = readXml($input); 282 | parseTree(0, $tree); 283 | } 284 | 285 | sub print_var { 286 | my $var = shift; 287 | my $vartype = ref $var; 288 | 289 | if ($vartype =~ /ARRAY/) { 290 | my @arr = @{$var}; 291 | print "[ "; 292 | foreach my $el (@arr) { 293 | print_var($el); 294 | print ", "; 295 | } 296 | print "] "; 297 | } elsif ($vartype =~ /HASH/) { 298 | my %hash = %{$var}; 299 | print "{ "; 300 | foreach my $key (keys %hash) { 301 | print "$key => "; 302 | print_var($hash{$key}); 303 | print ", "; 304 | } 305 | print "} "; 306 | } else { 307 | print $var; 308 | } 309 | } 310 | 311 | # Same syntax as getAttributeString in intltool-merge.in.in, similar logic (look for ## differences comment) 312 | sub getAttributeString 313 | { 314 | my $sub = shift; 315 | my $do_translate = shift || 1; 316 | my $language = shift || ""; 317 | my $translate = shift; 318 | my $result = ""; 319 | foreach my $e (reverse(sort(keys %{ $sub }))) { 320 | my $key = $e; 321 | my $string = $sub->{$e}; 322 | my $quote = '"'; 323 | 324 | $string =~ s/^[\s]+//; 325 | $string =~ s/[\s]+$//; 326 | 327 | if ($string =~ /^'.*'$/) 328 | { 329 | $quote = "'"; 330 | } 331 | $string =~ s/^['"]//g; 332 | $string =~ s/['"]$//g; 333 | 334 | ## differences from intltool-merge.in.in 335 | if ($key =~ /^_/) { 336 | $comments{entity_decode($string)} = $XMLCOMMENT if $XMLCOMMENT; 337 | $messages{entity_decode($string)} = []; 338 | $$translate = 2; 339 | } 340 | ## differences end here from intltool-merge.in.in 341 | $result .= " $key=$quote$string$quote"; 342 | } 343 | return $result; 344 | } 345 | 346 | # Verbatim copy from intltool-merge.in.in 347 | sub getXMLstring 348 | { 349 | my $ref = shift; 350 | my $spacepreserve = shift || 0; 351 | my @list = @{ $ref }; 352 | my $result = ""; 353 | 354 | my $count = scalar(@list); 355 | my $attrs = $list[0]; 356 | my $index = 1; 357 | 358 | $spacepreserve = 1 if ((exists $attrs->{"xml:space"}) && ($attrs->{"xml:space"} =~ /^["']?preserve["']?$/)); 359 | $spacepreserve = 0 if ((exists $attrs->{"xml:space"}) && ($attrs->{"xml:space"} =~ /^["']?default["']?$/)); 360 | 361 | while ($index < $count) { 362 | my $type = $list[$index]; 363 | my $content = $list[$index+1]; 364 | if (! $type ) { 365 | # We've got CDATA 366 | if ($content) { 367 | # lets strip the whitespace here, and *ONLY* here 368 | $content =~ s/\s+/ /gs if (!$spacepreserve); 369 | $result .= $content; 370 | } 371 | } elsif ( "$type" ne "1" ) { 372 | # We've got another element 373 | $result .= "<$type"; 374 | $result .= getAttributeString(@{$content}[0], 0); # no nested translatable elements 375 | if ($content) { 376 | my $subresult = getXMLstring($content, $spacepreserve); 377 | if ($subresult) { 378 | $result .= ">".$subresult . ""; 379 | } else { 380 | $result .= "/>"; 381 | } 382 | } else { 383 | $result .= "/>"; 384 | } 385 | } 386 | $index += 2; 387 | } 388 | return $result; 389 | } 390 | 391 | # Verbatim copy from intltool-merge.in.in, except for MULTIPLE_OUTPUT handling removed 392 | # Translate list of nodes if necessary 393 | sub translate_subnodes 394 | { 395 | my $fh = shift; 396 | my $content = shift; 397 | my $language = shift || ""; 398 | my $singlelang = shift || 0; 399 | my $spacepreserve = shift || 0; 400 | 401 | my @nodes = @{ $content }; 402 | 403 | my $count = scalar(@nodes); 404 | my $index = 0; 405 | while ($index < $count) { 406 | my $type = $nodes[$index]; 407 | my $rest = $nodes[$index+1]; 408 | traverse($fh, $type, $rest, $language, $spacepreserve); 409 | $index += 2; 410 | } 411 | } 412 | 413 | # Based on traverse() in intltool-merge.in.in 414 | sub traverse 415 | { 416 | my $fh = shift; # unused, to allow us to sync code between -merge and -extract 417 | my $nodename = shift; 418 | my $content = shift; 419 | my $language = shift || ""; 420 | my $spacepreserve = shift || 0; 421 | 422 | if ($nodename && "$nodename" eq "1") { 423 | $XMLCOMMENT = $content; 424 | } elsif ($nodename) { 425 | # element 426 | my @all = @{ $content }; 427 | my $attrs = shift @all; 428 | my $translate = 0; 429 | my $outattr = getAttributeString($attrs, 1, $language, \$translate); 430 | 431 | if ($nodename =~ /^_/) { 432 | $translate = 1; 433 | $nodename =~ s/^_//; 434 | } 435 | my $lookup = ''; 436 | 437 | $spacepreserve = 0 if ((exists $attrs->{"xml:space"}) && ($attrs->{"xml:space"} =~ /^["']?default["']?$/)); 438 | $spacepreserve = 1 if ((exists $attrs->{"xml:space"}) && ($attrs->{"xml:space"} =~ /^["']?preserve["']?$/)); 439 | 440 | if ($translate) { 441 | $lookup = getXMLstring($content, $spacepreserve); 442 | if (!$spacepreserve) { 443 | $lookup =~ s/^\s+//s; 444 | $lookup =~ s/\s+$//s; 445 | } 446 | 447 | if ($lookup && $translate != 2) { 448 | $comments{$lookup} = $XMLCOMMENT if $XMLCOMMENT; 449 | $messages{$lookup} = []; 450 | } elsif ($translate == 2) { 451 | translate_subnodes($fh, \@all, $language, 1, $spacepreserve); 452 | } 453 | } else { 454 | $XMLCOMMENT = ""; 455 | my $count = scalar(@all); 456 | if ($count > 0) { 457 | my $index = 0; 458 | while ($index < $count) { 459 | my $type = $all[$index]; 460 | my $rest = $all[$index+1]; 461 | traverse($fh, $type, $rest, $language, $spacepreserve); 462 | $index += 2; 463 | } 464 | } 465 | } 466 | $XMLCOMMENT = ""; 467 | } 468 | } 469 | 470 | 471 | # Verbatim copy from intltool-merge.in.in, $fh for compatibility 472 | sub parseTree 473 | { 474 | my $fh = shift; 475 | my $ref = shift; 476 | my $language = shift || ""; 477 | 478 | my $name = shift @{ $ref }; 479 | my $cont = shift @{ $ref }; 480 | 481 | while (!$name || "$name" eq "1") { 482 | $name = shift @{ $ref }; 483 | $cont = shift @{ $ref }; 484 | } 485 | 486 | my $spacepreserve = 0; 487 | my $attrs = @{$cont}[0]; 488 | $spacepreserve = 1 if ((exists $attrs->{"xml:space"}) && ($attrs->{"xml:space"} =~ /^["']?preserve["']?$/)); 489 | 490 | traverse($fh, $name, $cont, $language, $spacepreserve); 491 | } 492 | 493 | # Verbatim copy from intltool-merge.in.in 494 | sub intltool_tree_comment 495 | { 496 | my $expat = shift; 497 | my $data = $expat->original_string(); 498 | my $clist = $expat->{Curlist}; 499 | my $pos = $#$clist; 500 | 501 | $data =~ s/^$//s; 503 | push @$clist, 1 => $data; 504 | } 505 | 506 | # Verbatim copy from intltool-merge.in.in 507 | sub intltool_tree_cdatastart 508 | { 509 | my $expat = shift; 510 | my $clist = $expat->{Curlist}; 511 | my $pos = $#$clist; 512 | 513 | push @$clist, 0 => $expat->original_string(); 514 | } 515 | 516 | # Verbatim copy from intltool-merge.in.in 517 | sub intltool_tree_cdataend 518 | { 519 | my $expat = shift; 520 | my $clist = $expat->{Curlist}; 521 | my $pos = $#$clist; 522 | 523 | $clist->[$pos] .= $expat->original_string(); 524 | } 525 | 526 | # Verbatim copy from intltool-merge.in.in 527 | sub intltool_tree_char 528 | { 529 | my $expat = shift; 530 | my $text = shift; 531 | my $clist = $expat->{Curlist}; 532 | my $pos = $#$clist; 533 | 534 | # Use original_string so that we retain escaped entities 535 | # in CDATA sections. 536 | # 537 | if ($pos > 0 and $clist->[$pos - 1] eq '0') { 538 | $clist->[$pos] .= $expat->original_string(); 539 | } else { 540 | push @$clist, 0 => $expat->original_string(); 541 | } 542 | } 543 | 544 | # Verbatim copy from intltool-merge.in.in 545 | sub intltool_tree_start 546 | { 547 | my $expat = shift; 548 | my $tag = shift; 549 | my @origlist = (); 550 | 551 | # Use original_string so that we retain escaped entities 552 | # in attribute values. We must convert the string to an 553 | # @origlist array to conform to the structure of the Tree 554 | # Style. 555 | # 556 | my @original_array = split /\x/, $expat->original_string(); 557 | my $source = $expat->original_string(); 558 | 559 | # Remove leading tag. 560 | # 561 | $source =~ s|^\s*<\s*(\S+)||s; 562 | 563 | # Grab attribute key/value pairs and push onto @origlist array. 564 | # 565 | while ($source) 566 | { 567 | if ($source =~ /^\s*([\w:-]+)\s*[=]\s*["]/) 568 | { 569 | $source =~ s|^\s*([\w:-]+)\s*[=]\s*["]([^"]*)["]||s; 570 | push @origlist, $1; 571 | push @origlist, '"' . $2 . '"'; 572 | } 573 | elsif ($source =~ /^\s*([\w:-]+)\s*[=]\s*[']/) 574 | { 575 | $source =~ s|^\s*([\w:-]+)\s*[=]\s*[']([^']*)[']||s; 576 | push @origlist, $1; 577 | push @origlist, "'" . $2 . "'"; 578 | } 579 | else 580 | { 581 | last; 582 | } 583 | } 584 | 585 | my $ol = [ { @origlist } ]; 586 | 587 | push @{ $expat->{Lists} }, $expat->{Curlist}; 588 | push @{ $expat->{Curlist} }, $tag => $ol; 589 | $expat->{Curlist} = $ol; 590 | } 591 | 592 | # Copied from intltool-merge.in.in and added comment handler. 593 | sub readXml 594 | { 595 | my $xmldoc = shift || return; 596 | my $ret = eval 'require XML::Parser'; 597 | if(!$ret) { 598 | die "You must have XML::Parser installed to run $0\n\n"; 599 | } 600 | my $xp = new XML::Parser(Style => 'Tree'); 601 | $xp->setHandlers(Char => \&intltool_tree_char); 602 | $xp->setHandlers(Start => \&intltool_tree_start); 603 | $xp->setHandlers(CdataStart => \&intltool_tree_cdatastart); 604 | $xp->setHandlers(CdataEnd => \&intltool_tree_cdataend); 605 | 606 | ## differences from intltool-merge.in.in 607 | $xp->setHandlers(Comment => \&intltool_tree_comment); 608 | ## differences end here from intltool-merge.in.in 609 | 610 | my $tree = $xp->parse($xmldoc); 611 | #print_var($tree); 612 | 613 | # Hello thereHowdydo 614 | # would be: 615 | # [foo, [{}, 1, "comment", head, [{id => "a"}, 0, "Hello ", em, [{}, 0, "there"]], bar, 616 | # [{}, 0, "Howdy", ref, [{}]], 0, "do" ] ] 617 | 618 | return $tree; 619 | } 620 | 621 | sub type_schemas { 622 | ### For schemas XML files ### 623 | 624 | # FIXME: We should handle escaped < (less than) 625 | while ($input =~ / 626 | \s* 627 | (\s*(?:\s*)?(.*?)\s*<\/default>\s*)? 628 | (\s*(?:\s*)?(.*?)\s*<\/short>\s*)? 629 | (\s*(?:\s*)?(.*?)\s*<\/long>\s*)? 630 | <\/locale> 631 | /sgx) { 632 | my @totranslate = ($3,$6,$9); 633 | my @eachcomment = ($2,$5,$8); 634 | foreach (@totranslate) { 635 | my $currentcomment = shift @eachcomment; 636 | next if !$_; 637 | s/\s+/ /g; 638 | $messages{entity_decode_minimal($_)} = []; 639 | $comments{entity_decode_minimal($_)} = $currentcomment if (defined($currentcomment)); 640 | } 641 | } 642 | } 643 | 644 | sub type_rfc822deb { 645 | ### For rfc822-style Debian configuration files ### 646 | 647 | my $lineno = 1; 648 | my $type = ''; 649 | while ($input =~ /\G(.*?)(^|\n)(_+)([^:]+):[ \t]*(.*?)(?=\n\S|$)/sg) 650 | { 651 | my ($pre, $newline, $underscore, $tag, $text) = ($1, $2, $3, $4, $5); 652 | while ($pre =~ m/\n/g) 653 | { 654 | $lineno ++; 655 | } 656 | $lineno += length($newline); 657 | my @str_list = rfc822deb_split(length($underscore), $text); 658 | for my $str (@str_list) 659 | { 660 | $strcount++; 661 | $messages{$str} = []; 662 | $loc{$str} = $lineno; 663 | $count{$str} = $strcount; 664 | my $usercomment = ''; 665 | while($pre =~ s/(^|\n)#([^\n]*)$//s) 666 | { 667 | $usercomment = "\n" . $2 . $usercomment; 668 | } 669 | $comments{$str} = $tag . $usercomment; 670 | } 671 | $lineno += ($text =~ s/\n//g); 672 | } 673 | } 674 | 675 | sub rfc822deb_split { 676 | # Debian defines a special way to deal with rfc822-style files: 677 | # when a value contain newlines, it consists of 678 | # 1. a short form (first line) 679 | # 2. a long description, all lines begin with a space, 680 | # and paragraphs are separated by a single dot on a line 681 | # This routine returns an array of all paragraphs, and reformat 682 | # them. 683 | # When first argument is 2, the string is a comma separated list of 684 | # values. 685 | my $type = shift; 686 | my $text = shift; 687 | $text =~ s/^[ \t]//mg; 688 | return (split(/, */, $text, 0)) if $type ne 1; 689 | return ($text) if $text !~ /\n/; 690 | 691 | $text =~ s/([^\n]*)\n//; 692 | my @list = ($1); 693 | my $str = ''; 694 | for my $line (split (/\n/, $text)) 695 | { 696 | chomp $line; 697 | if ($line =~ /^\.\s*$/) 698 | { 699 | # New paragraph 700 | $str =~ s/\s*$//; 701 | push(@list, $str); 702 | $str = ''; 703 | } 704 | elsif ($line =~ /^\s/) 705 | { 706 | # Line which must not be reformatted 707 | $str .= "\n" if length ($str) && $str !~ /\n$/; 708 | $line =~ s/\s+$//; 709 | $str .= $line."\n"; 710 | } 711 | else 712 | { 713 | # Continuation line, remove newline 714 | $str .= " " if length ($str) && $str !~ /\n$/; 715 | $str .= $line; 716 | } 717 | } 718 | $str =~ s/\s*$//; 719 | push(@list, $str) if length ($str); 720 | return @list; 721 | } 722 | 723 | sub type_quoted { 724 | while ($input =~ /\"(([^\"]|\\\")*[^\\\"])\"/g) { 725 | my $message = $1; 726 | my $before = $`; 727 | $message =~ s/\\\"/\"/g; 728 | $before =~ s/[^\n]//g; 729 | $messages{$message} = []; 730 | $loc{$message} = length ($before) + 2; 731 | } 732 | } 733 | 734 | sub type_glade { 735 | ### For translatable Glade XML files ### 736 | 737 | my $tags = "label|title|text|format|copyright|comments|preview_text|tooltip|message"; 738 | 739 | while ($input =~ /<($tags)>([^<]+)<\/($tags)>/sg) { 740 | # Glade sometimes uses tags that normally mark translatable things for 741 | # little bits of non-translatable content. We work around this by not 742 | # translating strings that only includes something like label4 or window1. 743 | $messages{entity_decode($2)} = [] unless $2 =~ /^(window|label|dialog)[0-9]+$/; 744 | } 745 | 746 | while ($input =~ /(..[^<]*)<\/items>/sg) { 747 | for my $item (split (/\n/, $1)) { 748 | $messages{entity_decode($item)} = []; 749 | } 750 | } 751 | 752 | ## handle new glade files 753 | while ($input =~ /<(property|atkproperty)\s+[^>]*translatable\s*=\s*"yes"(?:\s+[^>]*comments\s*=\s*"([^"]*)")?[^>]*>([^<]+)<\/\1>/sg) { 754 | $messages{entity_decode($3)} = [] unless $3 =~ /^(window|label)[0-9]+$/; 755 | if (defined($2) and !($3 =~ /^(window|label)[0-9]+$/)) { 756 | $comments{entity_decode($3)} = entity_decode($2) ; 757 | } 758 | } 759 | while ($input =~ /]*)"\s+description="([^>]+)"\/>/sg) { 760 | $messages{entity_decode_minimal($2)} = []; 761 | } 762 | } 763 | 764 | sub type_scheme { 765 | my ($line, $i, $state, $str, $trcomment, $char); 766 | for $line (split(/\n/, $input)) { 767 | $i = 0; 768 | $state = 0; # 0 - nothing, 1 - string, 2 - translatable string 769 | while ($i < length($line)) { 770 | if (substr($line,$i,1) eq "\"") { 771 | if ($state == 2) { 772 | $comments{$str} = $trcomment if ($trcomment); 773 | $messages{$str} = []; 774 | $str = ''; 775 | $state = 0; $trcomment = ""; 776 | } elsif ($state == 1) { 777 | $str = ''; 778 | $state = 0; $trcomment = ""; 779 | } else { 780 | $state = 1; 781 | $str = ''; 782 | if ($i>0 && substr($line,$i-1,1) eq '_') { 783 | $state = 2; 784 | } 785 | } 786 | } elsif (!$state) { 787 | if (substr($line,$i,1) eq ";") { 788 | $trcomment = substr($line,$i+1); 789 | $trcomment =~ s/^;*\s*//; 790 | $i = length($line); 791 | } elsif ($trcomment && substr($line,$i,1) !~ /\s|\(|\)|_/) { 792 | $trcomment = ""; 793 | } 794 | } else { 795 | if (substr($line,$i,1) eq "\\") { 796 | $char = substr($line,$i+1,1); 797 | if ($char ne "\"" && $char ne "\\") { 798 | $str = $str . "\\"; 799 | } 800 | $i++; 801 | } 802 | $str = $str . substr($line,$i,1); 803 | } 804 | $i++; 805 | } 806 | } 807 | } 808 | 809 | sub msg_write { 810 | my @msgids; 811 | if (%count) 812 | { 813 | @msgids = sort { $count{$a} <=> $count{$b} } keys %count; 814 | } 815 | else 816 | { 817 | @msgids = sort keys %messages; 818 | } 819 | for my $message (@msgids) 820 | { 821 | my $offsetlines = 1; 822 | $offsetlines++ if $message =~ /%/; 823 | if (defined ($comments{$message})) 824 | { 825 | while ($comments{$message} =~ m/\n/g) 826 | { 827 | $offsetlines++; 828 | } 829 | } 830 | print OUT "# ".($loc{$message} - $offsetlines). " \"$FILE\"\n" 831 | if defined $loc{$message}; 832 | print OUT "/* ".$comments{$message}." */\n" 833 | if defined $comments{$message}; 834 | print OUT "/* xgettext:no-c-format */\n" if $message =~ /%/; 835 | 836 | my @lines = split (/\n/, $message, -1); 837 | for (my $n = 0; $n < @lines; $n++) 838 | { 839 | if ($n == 0) 840 | { 841 | print OUT "char *s = N_(\""; 842 | } 843 | else 844 | { 845 | print OUT " \""; 846 | } 847 | 848 | print OUT escape($lines[$n]); 849 | 850 | if ($n < @lines - 1) 851 | { 852 | print OUT "\\n\"\n"; 853 | } 854 | else 855 | { 856 | print OUT "\");\n"; 857 | } 858 | } 859 | } 860 | } 861 | 862 | -------------------------------------------------------------------------------- /intltool-merge.in: -------------------------------------------------------------------------------- 1 | #!@INTLTOOL_PERL@ -w 2 | # -*- Mode: perl; indent-tabs-mode: nil; c-basic-offset: 4 -*- 3 | 4 | # 5 | # The Intltool Message Merger 6 | # 7 | # Copyright (C) 2000, 2003 Free Software Foundation. 8 | # Copyright (C) 2000, 2001 Eazel, Inc 9 | # 10 | # Intltool is free software; you can redistribute it and/or 11 | # modify it under the terms of the GNU General Public License 12 | # version 2 published by the Free Software Foundation. 13 | # 14 | # Intltool is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | # General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU General Public License 20 | # along with this program; if not, write to the Free Software 21 | # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 22 | # 23 | # As a special exception to the GNU General Public License, if you 24 | # distribute this file as part of a program that contains a 25 | # configuration script generated by Autoconf, you may include it under 26 | # the same distribution terms that you use for the rest of that program. 27 | # 28 | # Authors: Maciej Stachowiak 29 | # Kenneth Christiansen 30 | # Darin Adler 31 | # 32 | # Proper XML UTF-8'ification written by Cyrille Chepelov 33 | # 34 | 35 | ## Release information 36 | my $PROGRAM = "intltool-merge"; 37 | my $PACKAGE = "intltool"; 38 | my $VERSION = "0.36.2"; 39 | 40 | ## Loaded modules 41 | use strict; 42 | use Getopt::Long; 43 | use Text::Wrap; 44 | use File::Basename; 45 | 46 | my $must_end_tag = -1; 47 | my $last_depth = -1; 48 | my $translation_depth = -1; 49 | my @tag_stack = (); 50 | my @entered_tag = (); 51 | my @translation_strings = (); 52 | my $leading_space = ""; 53 | 54 | ## Scalars used by the option stuff 55 | my $HELP_ARG = 0; 56 | my $VERSION_ARG = 0; 57 | my $BA_STYLE_ARG = 0; 58 | my $XML_STYLE_ARG = 0; 59 | my $KEYS_STYLE_ARG = 0; 60 | my $DESKTOP_STYLE_ARG = 0; 61 | my $SCHEMAS_STYLE_ARG = 0; 62 | my $RFC822DEB_STYLE_ARG = 0; 63 | my $QUOTED_STYLE_ARG = 0; 64 | my $QUIET_ARG = 0; 65 | my $PASS_THROUGH_ARG = 0; 66 | my $UTF8_ARG = 0; 67 | my $MULTIPLE_OUTPUT = 0; 68 | my $cache_file; 69 | 70 | ## Handle options 71 | GetOptions 72 | ( 73 | "help" => \$HELP_ARG, 74 | "version" => \$VERSION_ARG, 75 | "quiet|q" => \$QUIET_ARG, 76 | "oaf-style|o" => \$BA_STYLE_ARG, ## for compatibility 77 | "ba-style|b" => \$BA_STYLE_ARG, 78 | "xml-style|x" => \$XML_STYLE_ARG, 79 | "keys-style|k" => \$KEYS_STYLE_ARG, 80 | "desktop-style|d" => \$DESKTOP_STYLE_ARG, 81 | "schemas-style|s" => \$SCHEMAS_STYLE_ARG, 82 | "rfc822deb-style|r" => \$RFC822DEB_STYLE_ARG, 83 | "quoted-style" => \$QUOTED_STYLE_ARG, 84 | "pass-through|p" => \$PASS_THROUGH_ARG, 85 | "utf8|u" => \$UTF8_ARG, 86 | "multiple-output|m" => \$MULTIPLE_OUTPUT, 87 | "cache|c=s" => \$cache_file 88 | ) or &error; 89 | 90 | my $PO_DIR; 91 | my $FILE; 92 | my $OUTFILE; 93 | 94 | my %po_files_by_lang = (); 95 | my %translations = (); 96 | my $iconv = $ENV{"ICONV"} || "iconv"; 97 | my $devnull = ($^O eq 'MSWin32' ? 'NUL:' : '/dev/null'); 98 | 99 | sub isProgramInPath 100 | { 101 | my ($file) = @_; 102 | # If either a file exists, or when run it returns 0 exit status 103 | return 1 if ((-x $file) or (system("$file -l >$devnull") == 0)); 104 | return 0; 105 | } 106 | 107 | if (! isProgramInPath ("$iconv")) 108 | { 109 | print STDERR " *** iconv is not found on this system!\n". 110 | " *** Without it, intltool-merge can not convert encodings.\n"; 111 | exit; 112 | } 113 | 114 | # Use this instead of \w for XML files to handle more possible characters. 115 | my $w = "[-A-Za-z0-9._:]"; 116 | 117 | # XML quoted string contents 118 | my $q = "[^\\\"]*"; 119 | 120 | ## Check for options. 121 | 122 | if ($VERSION_ARG) 123 | { 124 | &print_version; 125 | } 126 | elsif ($HELP_ARG) 127 | { 128 | &print_help; 129 | } 130 | elsif ($BA_STYLE_ARG && @ARGV > 2) 131 | { 132 | &utf8_sanity_check; 133 | &preparation; 134 | &print_message; 135 | &ba_merge_translations; 136 | &finalize; 137 | } 138 | elsif ($XML_STYLE_ARG && @ARGV > 2) 139 | { 140 | &utf8_sanity_check; 141 | &preparation; 142 | &print_message; 143 | &xml_merge_output; 144 | &finalize; 145 | } 146 | elsif ($KEYS_STYLE_ARG && @ARGV > 2) 147 | { 148 | &utf8_sanity_check; 149 | &preparation; 150 | &print_message; 151 | &keys_merge_translations; 152 | &finalize; 153 | } 154 | elsif ($DESKTOP_STYLE_ARG && @ARGV > 2) 155 | { 156 | &utf8_sanity_check; 157 | &preparation; 158 | &print_message; 159 | &desktop_merge_translations; 160 | &finalize; 161 | } 162 | elsif ($SCHEMAS_STYLE_ARG && @ARGV > 2) 163 | { 164 | &utf8_sanity_check; 165 | &preparation; 166 | &print_message; 167 | &schemas_merge_translations; 168 | &finalize; 169 | } 170 | elsif ($RFC822DEB_STYLE_ARG && @ARGV > 2) 171 | { 172 | &preparation; 173 | &print_message; 174 | &rfc822deb_merge_translations; 175 | &finalize; 176 | } 177 | elsif ($QUOTED_STYLE_ARG && @ARGV > 2) 178 | { 179 | &utf8_sanity_check; 180 | &preparation; 181 | &print_message; 182 | "ed_merge_translations; 183 | &finalize; 184 | } 185 | else 186 | { 187 | &print_help; 188 | } 189 | 190 | exit; 191 | 192 | ## Sub for printing release information 193 | sub print_version 194 | { 195 | print <<_EOF_; 196 | ${PROGRAM} (${PACKAGE}) ${VERSION} 197 | Written by Maciej Stachowiak, Darin Adler and Kenneth Christiansen. 198 | 199 | Copyright (C) 2000-2003 Free Software Foundation, Inc. 200 | Copyright (C) 2000-2001 Eazel, Inc. 201 | This is free software; see the source for copying conditions. There is NO 202 | warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 203 | _EOF_ 204 | exit; 205 | } 206 | 207 | ## Sub for printing usage information 208 | sub print_help 209 | { 210 | print <<_EOF_; 211 | Usage: ${PROGRAM} [OPTION]... PO_DIRECTORY FILENAME OUTPUT_FILE 212 | Generates an output file that includes some localized attributes from an 213 | untranslated source file. 214 | 215 | Mandatory options: (exactly one must be specified) 216 | -b, --ba-style includes translations in the bonobo-activation style 217 | -d, --desktop-style includes translations in the desktop style 218 | -k, --keys-style includes translations in the keys style 219 | -s, --schemas-style includes translations in the schemas style 220 | -r, --rfc822deb-style includes translations in the RFC822 style 221 | --quoted-style includes translations in the quoted string style 222 | -x, --xml-style includes translations in the standard xml style 223 | 224 | Other options: 225 | -u, --utf8 convert all strings to UTF-8 before merging 226 | (default for everything except RFC822 style) 227 | -p, --pass-through deprecated, does nothing and issues a warning 228 | -m, --multiple-output output one localized file per locale, instead of 229 | a single file containing all localized elements 230 | -c, --cache=FILE specify cache file name 231 | (usually \$top_builddir/po/.intltool-merge-cache) 232 | -q, --quiet suppress most messages 233 | --help display this help and exit 234 | --version output version information and exit 235 | 236 | Report bugs to http://bugzilla.gnome.org/ (product name "$PACKAGE") 237 | or send email to . 238 | _EOF_ 239 | exit; 240 | } 241 | 242 | 243 | ## Sub for printing error messages 244 | sub print_error 245 | { 246 | print STDERR "Try `${PROGRAM} --help' for more information.\n"; 247 | exit; 248 | } 249 | 250 | 251 | sub print_message 252 | { 253 | print "Merging translations into $OUTFILE.\n" unless $QUIET_ARG; 254 | } 255 | 256 | 257 | sub preparation 258 | { 259 | $PO_DIR = $ARGV[0]; 260 | $FILE = $ARGV[1]; 261 | $OUTFILE = $ARGV[2]; 262 | 263 | &gather_po_files; 264 | &get_translation_database; 265 | } 266 | 267 | # General-purpose code for looking up translations in .po files 268 | 269 | sub po_file2lang 270 | { 271 | my ($tmp) = @_; 272 | $tmp =~ s/^.*\/(.*)\.po$/$1/; 273 | return $tmp; 274 | } 275 | 276 | sub gather_po_files 277 | { 278 | if (my $linguas = $ENV{"LINGUAS"}) 279 | { 280 | for my $lang (split / /, $linguas) { 281 | my $po_file = $PO_DIR . "/" . $lang . ".po"; 282 | if (-e $po_file) { 283 | $po_files_by_lang{$lang} = $po_file; 284 | } 285 | } 286 | } 287 | else 288 | { 289 | if (open LINGUAS_FILE, "$PO_DIR/LINGUAS") 290 | { 291 | while () 292 | { 293 | next if /^#/; 294 | 295 | if (/([-a-zA-Z_@.]+)\n/) 296 | { 297 | my $lang = $1; 298 | 299 | my $po_file = $PO_DIR . "/" . $lang . ".po"; 300 | if (-e $po_file) { 301 | $po_files_by_lang{$lang} = $po_file; 302 | } 303 | } 304 | } 305 | 306 | close LINGUAS_FILE; 307 | } 308 | else 309 | { 310 | for my $po_file (glob "$PO_DIR/*.po") { 311 | $po_files_by_lang{po_file2lang($po_file)} = $po_file; 312 | } 313 | } 314 | } 315 | } 316 | 317 | sub get_local_charset 318 | { 319 | my ($encoding) = @_; 320 | my $alias_file = $ENV{"G_CHARSET_ALIAS"} || "@INTLTOOL_LIBDIR@/charset.alias"; 321 | 322 | # seek character encoding aliases in charset.alias (glib) 323 | 324 | if (open CHARSET_ALIAS, $alias_file) 325 | { 326 | while () 327 | { 328 | next if /^\#/; 329 | return $1 if (/^\s*([-._a-zA-Z0-9]+)\s+$encoding\b/i) 330 | } 331 | 332 | close CHARSET_ALIAS; 333 | } 334 | 335 | # if not found, return input string 336 | 337 | return $encoding; 338 | } 339 | 340 | sub get_po_encoding 341 | { 342 | my ($in_po_file) = @_; 343 | my $encoding = ""; 344 | 345 | open IN_PO_FILE, $in_po_file or die; 346 | while () 347 | { 348 | ## example: "Content-Type: text/plain; charset=ISO-8859-1\n" 349 | if (/Content-Type\:.*charset=([-a-zA-Z0-9]+)\\n/) 350 | { 351 | $encoding = $1; 352 | last; 353 | } 354 | } 355 | close IN_PO_FILE; 356 | 357 | if (!$encoding) 358 | { 359 | print STDERR "Warning: no encoding found in $in_po_file. Assuming ISO-8859-1\n" unless $QUIET_ARG; 360 | $encoding = "ISO-8859-1"; 361 | } 362 | 363 | system ("$iconv -f $encoding -t UTF-8 <$devnull 2>$devnull"); 364 | if ($?) { 365 | $encoding = get_local_charset($encoding); 366 | } 367 | 368 | return $encoding 369 | } 370 | 371 | sub utf8_sanity_check 372 | { 373 | print STDERR "Warning: option --pass-through has been removed.\n" if $PASS_THROUGH_ARG; 374 | $UTF8_ARG = 1; 375 | } 376 | 377 | sub get_translation_database 378 | { 379 | if ($cache_file) { 380 | &get_cached_translation_database; 381 | } else { 382 | &create_translation_database; 383 | } 384 | } 385 | 386 | sub get_newest_po_age 387 | { 388 | my $newest_age; 389 | 390 | foreach my $file (values %po_files_by_lang) 391 | { 392 | my $file_age = -M $file; 393 | $newest_age = $file_age if !$newest_age || $file_age < $newest_age; 394 | } 395 | 396 | $newest_age = 0 if !$newest_age; 397 | 398 | return $newest_age; 399 | } 400 | 401 | sub create_cache 402 | { 403 | print "Generating and caching the translation database\n" unless $QUIET_ARG; 404 | 405 | &create_translation_database; 406 | 407 | open CACHE, ">$cache_file" || die; 408 | print CACHE join "\x01", %translations; 409 | close CACHE; 410 | } 411 | 412 | sub load_cache 413 | { 414 | print "Found cached translation database\n" unless $QUIET_ARG; 415 | 416 | my $contents; 417 | open CACHE, "<$cache_file" || die; 418 | { 419 | local $/; 420 | $contents = ; 421 | } 422 | close CACHE; 423 | %translations = split "\x01", $contents; 424 | } 425 | 426 | sub get_cached_translation_database 427 | { 428 | my $cache_file_age = -M $cache_file; 429 | if (defined $cache_file_age) 430 | { 431 | if ($cache_file_age <= &get_newest_po_age) 432 | { 433 | &load_cache; 434 | return; 435 | } 436 | print "Found too-old cached translation database\n" unless $QUIET_ARG; 437 | } 438 | 439 | &create_cache; 440 | } 441 | 442 | sub create_translation_database 443 | { 444 | for my $lang (keys %po_files_by_lang) 445 | { 446 | my $po_file = $po_files_by_lang{$lang}; 447 | 448 | if ($UTF8_ARG) 449 | { 450 | my $encoding = get_po_encoding ($po_file); 451 | 452 | if (lc $encoding eq "utf-8") 453 | { 454 | open PO_FILE, "<$po_file"; 455 | } 456 | else 457 | { 458 | print "NOTICE: $po_file is not in UTF-8 but $encoding, converting...\n" unless $QUIET_ARG;; 459 | 460 | open PO_FILE, "$iconv -f $encoding -t UTF-8 $po_file|"; 461 | } 462 | } 463 | else 464 | { 465 | open PO_FILE, "<$po_file"; 466 | } 467 | 468 | my $nextfuzzy = 0; 469 | my $inmsgid = 0; 470 | my $inmsgstr = 0; 471 | my $msgid = ""; 472 | my $msgstr = ""; 473 | 474 | while () 475 | { 476 | $nextfuzzy = 1 if /^#, fuzzy/; 477 | 478 | if (/^msgid "((\\.|[^\\]+)*)"/ ) 479 | { 480 | $translations{$lang, $msgid} = $msgstr if $inmsgstr && $msgid && $msgstr; 481 | $msgid = ""; 482 | $msgstr = ""; 483 | 484 | if ($nextfuzzy) { 485 | $inmsgid = 0; 486 | } else { 487 | $msgid = unescape_po_string($1); 488 | $inmsgid = 1; 489 | } 490 | $inmsgstr = 0; 491 | $nextfuzzy = 0; 492 | } 493 | 494 | if (/^msgstr "((\\.|[^\\]+)*)"/) 495 | { 496 | $msgstr = unescape_po_string($1); 497 | $inmsgstr = 1; 498 | $inmsgid = 0; 499 | } 500 | 501 | if (/^"((\\.|[^\\]+)*)"/) 502 | { 503 | $msgid .= unescape_po_string($1) if $inmsgid; 504 | $msgstr .= unescape_po_string($1) if $inmsgstr; 505 | } 506 | } 507 | $translations{$lang, $msgid} = $msgstr if $inmsgstr && $msgid && $msgstr; 508 | } 509 | } 510 | 511 | sub finalize 512 | { 513 | } 514 | 515 | sub unescape_one_sequence 516 | { 517 | my ($sequence) = @_; 518 | 519 | return "\\" if $sequence eq "\\\\"; 520 | return "\"" if $sequence eq "\\\""; 521 | return "\n" if $sequence eq "\\n"; 522 | return "\r" if $sequence eq "\\r"; 523 | return "\t" if $sequence eq "\\t"; 524 | return "\b" if $sequence eq "\\b"; 525 | return "\f" if $sequence eq "\\f"; 526 | return "\a" if $sequence eq "\\a"; 527 | return chr(11) if $sequence eq "\\v"; # vertical tab, see ascii(7) 528 | 529 | return chr(hex($1)) if ($sequence =~ /\\x([0-9a-fA-F]{2})/); 530 | return chr(oct($1)) if ($sequence =~ /\\([0-7]{3})/); 531 | 532 | # FIXME: Is \0 supported as well? Kenneth and Rodney don't want it, see bug #48489 533 | 534 | return $sequence; 535 | } 536 | 537 | sub unescape_po_string 538 | { 539 | my ($string) = @_; 540 | 541 | $string =~ s/(\\x[0-9a-fA-F]{2}|\\[0-7]{3}|\\.)/unescape_one_sequence($1)/eg; 542 | 543 | return $string; 544 | } 545 | 546 | sub entity_decode 547 | { 548 | local ($_) = @_; 549 | 550 | s/'/'/g; # ' 551 | s/"/"/g; # " 552 | s/<//g; 554 | s/&/&/g; 555 | 556 | return $_; 557 | } 558 | 559 | # entity_encode: (string) 560 | # 561 | # Encode the given string to XML format (encode '<' etc). 562 | 563 | sub entity_encode 564 | { 565 | my ($pre_encoded) = @_; 566 | 567 | my @list_of_chars = unpack ('C*', $pre_encoded); 568 | 569 | # with UTF-8 we only encode minimalistic 570 | return join ('', map (&entity_encode_int_minimalist, @list_of_chars)); 571 | } 572 | 573 | sub entity_encode_int_minimalist 574 | { 575 | return """ if $_ == 34; 576 | return "&" if $_ == 38; 577 | return "'" if $_ == 39; 578 | return "<" if $_ == 60; 579 | return chr $_; 580 | } 581 | 582 | sub entity_encoded_translation 583 | { 584 | my ($lang, $string) = @_; 585 | 586 | my $translation = $translations{$lang, $string}; 587 | return $string if !$translation; 588 | return entity_encode ($translation); 589 | } 590 | 591 | ## XML (bonobo-activation specific) merge code 592 | 593 | sub ba_merge_translations 594 | { 595 | my $source; 596 | 597 | { 598 | local $/; # slurp mode 599 | open INPUT, "<$FILE" or die "can't open $FILE: $!"; 600 | $source = ; 601 | close INPUT; 602 | } 603 | 604 | open OUTPUT, ">$OUTFILE" or die "can't open $OUTFILE: $!"; 605 | # Binmode so that selftest works ok if using a native Win32 Perl... 606 | binmode (OUTPUT) if $^O eq 'MSWin32'; 607 | 608 | while ($source =~ s|^(.*?)([ \t]*<\s*$w+\s+($w+\s*=\s*"$q"\s*)+/?>)([ \t]*\n)?||s) 609 | { 610 | print OUTPUT $1; 611 | 612 | my $node = $2 . "\n"; 613 | 614 | my @strings = (); 615 | $_ = $node; 616 | while (s/(\s)_($w+\s*=\s*"($q)")/$1$2/s) { 617 | push @strings, entity_decode($3); 618 | } 619 | print OUTPUT; 620 | 621 | my %langs; 622 | for my $string (@strings) 623 | { 624 | for my $lang (keys %po_files_by_lang) 625 | { 626 | $langs{$lang} = 1 if $translations{$lang, $string}; 627 | } 628 | } 629 | 630 | for my $lang (sort keys %langs) 631 | { 632 | $_ = $node; 633 | s/(\sname\s*=\s*)"($q)"/$1"$2-$lang"/s; 634 | s/(\s)_($w+\s*=\s*")($q)"/$1 . $2 . entity_encoded_translation($lang, $3) . '"'/seg; 635 | print OUTPUT; 636 | } 637 | } 638 | 639 | print OUTPUT $source; 640 | 641 | close OUTPUT; 642 | } 643 | 644 | 645 | ## XML (non-bonobo-activation) merge code 646 | 647 | 648 | # Process tag attributes 649 | # Only parameter is a HASH containing attributes -> values mapping 650 | sub getAttributeString 651 | { 652 | my $sub = shift; 653 | my $do_translate = shift || 0; 654 | my $language = shift || ""; 655 | my $result = ""; 656 | my $translate = shift; 657 | foreach my $e (reverse(sort(keys %{ $sub }))) { 658 | my $key = $e; 659 | my $string = $sub->{$e}; 660 | my $quote = '"'; 661 | 662 | $string =~ s/^[\s]+//; 663 | $string =~ s/[\s]+$//; 664 | 665 | if ($string =~ /^'.*'$/) 666 | { 667 | $quote = "'"; 668 | } 669 | $string =~ s/^['"]//g; 670 | $string =~ s/['"]$//g; 671 | 672 | if ($do_translate && $key =~ /^_/) { 673 | $key =~ s|^_||g; 674 | if ($language) { 675 | # Handle translation 676 | my $decode_string = entity_decode($string); 677 | my $translation = $translations{$language, $decode_string}; 678 | if ($translation) { 679 | $translation = entity_encode($translation); 680 | $string = $translation; 681 | } 682 | $$translate = 2; 683 | } else { 684 | $$translate = 2 if ($translate && (!$$translate)); # watch not to "overwrite" $translate 685 | } 686 | } 687 | 688 | $result .= " $key=$quote$string$quote"; 689 | } 690 | return $result; 691 | } 692 | 693 | # Returns a translatable string from XML node, it works on contents of every node in XML::Parser tree 694 | sub getXMLstring 695 | { 696 | my $ref = shift; 697 | my $spacepreserve = shift || 0; 698 | my @list = @{ $ref }; 699 | my $result = ""; 700 | 701 | my $count = scalar(@list); 702 | my $attrs = $list[0]; 703 | my $index = 1; 704 | 705 | $spacepreserve = 1 if ((exists $attrs->{"xml:space"}) && ($attrs->{"xml:space"} =~ /^["']?preserve["']?$/)); 706 | $spacepreserve = 0 if ((exists $attrs->{"xml:space"}) && ($attrs->{"xml:space"} =~ /^["']?default["']?$/)); 707 | 708 | while ($index < $count) { 709 | my $type = $list[$index]; 710 | my $content = $list[$index+1]; 711 | if (! $type ) { 712 | # We've got CDATA 713 | if ($content) { 714 | # lets strip the whitespace here, and *ONLY* here 715 | $content =~ s/\s+/ /gs if (!$spacepreserve); 716 | $result .= $content; 717 | } 718 | } elsif ( "$type" ne "1" ) { 719 | # We've got another element 720 | $result .= "<$type"; 721 | $result .= getAttributeString(@{$content}[0], 0); # no nested translatable elements 722 | if ($content) { 723 | my $subresult = getXMLstring($content, $spacepreserve); 724 | if ($subresult) { 725 | $result .= ">".$subresult . ""; 726 | } else { 727 | $result .= "/>"; 728 | } 729 | } else { 730 | $result .= "/>"; 731 | } 732 | } 733 | $index += 2; 734 | } 735 | return $result; 736 | } 737 | 738 | # Translate list of nodes if necessary 739 | sub translate_subnodes 740 | { 741 | my $fh = shift; 742 | my $content = shift; 743 | my $language = shift || ""; 744 | my $singlelang = shift || 0; 745 | my $spacepreserve = shift || 0; 746 | 747 | my @nodes = @{ $content }; 748 | 749 | my $count = scalar(@nodes); 750 | my $index = 0; 751 | while ($index < $count) { 752 | my $type = $nodes[$index]; 753 | my $rest = $nodes[$index+1]; 754 | if ($singlelang) { 755 | my $oldMO = $MULTIPLE_OUTPUT; 756 | $MULTIPLE_OUTPUT = 1; 757 | traverse($fh, $type, $rest, $language, $spacepreserve); 758 | $MULTIPLE_OUTPUT = $oldMO; 759 | } else { 760 | traverse($fh, $type, $rest, $language, $spacepreserve); 761 | } 762 | $index += 2; 763 | } 764 | } 765 | 766 | sub isWellFormedXmlFragment 767 | { 768 | my $ret = eval 'require XML::Parser'; 769 | if(!$ret) { 770 | die "You must have XML::Parser installed to run $0\n\n"; 771 | } 772 | 773 | my $fragment = shift; 774 | return 0 if (!$fragment); 775 | 776 | $fragment = "$fragment"; 777 | my $xp = new XML::Parser(Style => 'Tree'); 778 | my $tree = 0; 779 | eval { $tree = $xp->parse($fragment); }; 780 | return $tree; 781 | } 782 | 783 | sub traverse 784 | { 785 | my $fh = shift; 786 | my $nodename = shift; 787 | my $content = shift; 788 | my $language = shift || ""; 789 | my $spacepreserve = shift || 0; 790 | 791 | if (!$nodename) { 792 | if ($content =~ /^[\s]*$/) { 793 | $leading_space .= $content; 794 | } 795 | print $fh $content; 796 | } else { 797 | # element 798 | my @all = @{ $content }; 799 | my $attrs = shift @all; 800 | my $translate = 0; 801 | my $outattr = getAttributeString($attrs, 1, $language, \$translate); 802 | 803 | if ($nodename =~ /^_/) { 804 | $translate = 1; 805 | $nodename =~ s/^_//; 806 | } 807 | my $lookup = ''; 808 | 809 | $spacepreserve = 0 if ((exists $attrs->{"xml:space"}) && ($attrs->{"xml:space"} =~ /^["']?default["']?$/)); 810 | $spacepreserve = 1 if ((exists $attrs->{"xml:space"}) && ($attrs->{"xml:space"} =~ /^["']?preserve["']?$/)); 811 | 812 | print $fh "<$nodename", $outattr; 813 | if ($translate) { 814 | $lookup = getXMLstring($content, $spacepreserve); 815 | if (!$spacepreserve) { 816 | $lookup =~ s/^\s+//s; 817 | $lookup =~ s/\s+$//s; 818 | } 819 | 820 | if ($lookup || $translate == 2) { 821 | my $translation = $translations{$language, $lookup} if isWellFormedXmlFragment($translations{$language, $lookup}); 822 | if ($MULTIPLE_OUTPUT && ($translation || $translate == 2)) { 823 | $translation = $lookup if (!$translation); 824 | print $fh " xml:lang=\"", $language, "\"" if $language; 825 | print $fh ">"; 826 | if ($translate == 2) { 827 | translate_subnodes($fh, \@all, $language, 1, $spacepreserve); 828 | } else { 829 | print $fh $translation; 830 | } 831 | print $fh ""; 832 | 833 | return; # this means there will be no same translation with xml:lang="$language"... 834 | # if we want them both, just remove this "return" 835 | } else { 836 | print $fh ">"; 837 | if ($translate == 2) { 838 | translate_subnodes($fh, \@all, $language, 1, $spacepreserve); 839 | } else { 840 | print $fh $lookup; 841 | } 842 | print $fh ""; 843 | } 844 | } else { 845 | print $fh "/>"; 846 | } 847 | 848 | for my $lang (sort keys %po_files_by_lang) { 849 | if ($MULTIPLE_OUTPUT && $lang ne "$language") { 850 | next; 851 | } 852 | if ($lang) { 853 | # Handle translation 854 | # 855 | my $translate = 0; 856 | my $localattrs = getAttributeString($attrs, 1, $lang, \$translate); 857 | my $translation = $translations{$lang, $lookup} if isWellFormedXmlFragment($translations{$lang, $lookup}); 858 | if ($translate && !$translation) { 859 | $translation = $lookup; 860 | } 861 | 862 | if ($translation || $translate) { 863 | print $fh "\n"; 864 | $leading_space =~ s/.*\n//g; 865 | print $fh $leading_space; 866 | print $fh "<", $nodename, " xml:lang=\"", $lang, "\"", $localattrs, ">"; 867 | if ($translate == 2) { 868 | translate_subnodes($fh, \@all, $lang, 1, $spacepreserve); 869 | } else { 870 | print $fh $translation; 871 | } 872 | print $fh ""; 873 | } 874 | } 875 | } 876 | 877 | } else { 878 | my $count = scalar(@all); 879 | if ($count > 0) { 880 | print $fh ">"; 881 | my $index = 0; 882 | while ($index < $count) { 883 | my $type = $all[$index]; 884 | my $rest = $all[$index+1]; 885 | traverse($fh, $type, $rest, $language, $spacepreserve); 886 | $index += 2; 887 | } 888 | print $fh ""; 889 | } else { 890 | print $fh "/>"; 891 | } 892 | } 893 | } 894 | } 895 | 896 | sub intltool_tree_comment 897 | { 898 | my $expat = shift; 899 | my $data = shift; 900 | my $clist = $expat->{Curlist}; 901 | my $pos = $#$clist; 902 | 903 | push @$clist, 1 => $data; 904 | } 905 | 906 | sub intltool_tree_cdatastart 907 | { 908 | my $expat = shift; 909 | my $clist = $expat->{Curlist}; 910 | my $pos = $#$clist; 911 | 912 | push @$clist, 0 => $expat->original_string(); 913 | } 914 | 915 | sub intltool_tree_cdataend 916 | { 917 | my $expat = shift; 918 | my $clist = $expat->{Curlist}; 919 | my $pos = $#$clist; 920 | 921 | $clist->[$pos] .= $expat->original_string(); 922 | } 923 | 924 | sub intltool_tree_char 925 | { 926 | my $expat = shift; 927 | my $text = shift; 928 | my $clist = $expat->{Curlist}; 929 | my $pos = $#$clist; 930 | 931 | # Use original_string so that we retain escaped entities 932 | # in CDATA sections. 933 | # 934 | if ($pos > 0 and $clist->[$pos - 1] eq '0') { 935 | $clist->[$pos] .= $expat->original_string(); 936 | } else { 937 | push @$clist, 0 => $expat->original_string(); 938 | } 939 | } 940 | 941 | sub intltool_tree_start 942 | { 943 | my $expat = shift; 944 | my $tag = shift; 945 | my @origlist = (); 946 | 947 | # Use original_string so that we retain escaped entities 948 | # in attribute values. We must convert the string to an 949 | # @origlist array to conform to the structure of the Tree 950 | # Style. 951 | # 952 | my @original_array = split /\x/, $expat->original_string(); 953 | my $source = $expat->original_string(); 954 | 955 | # Remove leading tag. 956 | # 957 | $source =~ s|^\s*<\s*(\S+)||s; 958 | 959 | # Grab attribute key/value pairs and push onto @origlist array. 960 | # 961 | while ($source) 962 | { 963 | if ($source =~ /^\s*([\w:-]+)\s*[=]\s*["]/) 964 | { 965 | $source =~ s|^\s*([\w:-]+)\s*[=]\s*["]([^"]*)["]||s; 966 | push @origlist, $1; 967 | push @origlist, '"' . $2 . '"'; 968 | } 969 | elsif ($source =~ /^\s*([\w:-]+)\s*[=]\s*[']/) 970 | { 971 | $source =~ s|^\s*([\w:-]+)\s*[=]\s*[']([^']*)[']||s; 972 | push @origlist, $1; 973 | push @origlist, "'" . $2 . "'"; 974 | } 975 | else 976 | { 977 | last; 978 | } 979 | } 980 | 981 | my $ol = [ { @origlist } ]; 982 | 983 | push @{ $expat->{Lists} }, $expat->{Curlist}; 984 | push @{ $expat->{Curlist} }, $tag => $ol; 985 | $expat->{Curlist} = $ol; 986 | } 987 | 988 | sub readXml 989 | { 990 | my $filename = shift || return; 991 | if(!-f $filename) { 992 | die "ERROR Cannot find filename: $filename\n"; 993 | } 994 | 995 | my $ret = eval 'require XML::Parser'; 996 | if(!$ret) { 997 | die "You must have XML::Parser installed to run $0\n\n"; 998 | } 999 | my $xp = new XML::Parser(Style => 'Tree'); 1000 | $xp->setHandlers(Char => \&intltool_tree_char); 1001 | $xp->setHandlers(Start => \&intltool_tree_start); 1002 | $xp->setHandlers(CdataStart => \&intltool_tree_cdatastart); 1003 | $xp->setHandlers(CdataEnd => \&intltool_tree_cdataend); 1004 | my $tree = $xp->parsefile($filename); 1005 | 1006 | # Hello thereHowdydo 1007 | # would be: 1008 | # [foo, [{}, head, [{id => "a"}, 0, "Hello ", em, [{}, 0, "there"]], bar, [{}, 1009 | # 0, "Howdy", ref, [{}]], 0, "do" ] ] 1010 | 1011 | return $tree; 1012 | } 1013 | 1014 | sub print_header 1015 | { 1016 | my $infile = shift; 1017 | my $fh = shift; 1018 | my $source; 1019 | 1020 | if(!-f $infile) { 1021 | die "ERROR Cannot find filename: $infile\n"; 1022 | } 1023 | 1024 | print $fh qq{\n}; 1025 | { 1026 | local $/; 1027 | open DOCINPUT, "<${FILE}" or die; 1028 | $source = ; 1029 | close DOCINPUT; 1030 | } 1031 | if ($source =~ /()/s) 1032 | { 1033 | print $fh "$1\n"; 1034 | } 1035 | elsif ($source =~ /(]*>)/s) 1036 | { 1037 | print $fh "$1\n"; 1038 | } 1039 | } 1040 | 1041 | sub parseTree 1042 | { 1043 | my $fh = shift; 1044 | my $ref = shift; 1045 | my $language = shift || ""; 1046 | 1047 | my $name = shift @{ $ref }; 1048 | my $cont = shift @{ $ref }; 1049 | 1050 | while (!$name || "$name" eq "1") { 1051 | $name = shift @{ $ref }; 1052 | $cont = shift @{ $ref }; 1053 | } 1054 | 1055 | my $spacepreserve = 0; 1056 | my $attrs = @{$cont}[0]; 1057 | $spacepreserve = 1 if ((exists $attrs->{"xml:space"}) && ($attrs->{"xml:space"} =~ /^["']?preserve["']?$/)); 1058 | 1059 | traverse($fh, $name, $cont, $language, $spacepreserve); 1060 | } 1061 | 1062 | sub xml_merge_output 1063 | { 1064 | my $source; 1065 | 1066 | if ($MULTIPLE_OUTPUT) { 1067 | for my $lang (sort keys %po_files_by_lang) { 1068 | if ( ! -d $lang ) { 1069 | mkdir $lang or -d $lang or die "Cannot create subdirectory $lang: $!\n"; 1070 | } 1071 | open OUTPUT, ">$lang/$OUTFILE" or die "Cannot open $lang/$OUTFILE: $!\n"; 1072 | binmode (OUTPUT) if $^O eq 'MSWin32'; 1073 | my $tree = readXml($FILE); 1074 | print_header($FILE, \*OUTPUT); 1075 | parseTree(\*OUTPUT, $tree, $lang); 1076 | close OUTPUT; 1077 | print "CREATED $lang/$OUTFILE\n" unless $QUIET_ARG; 1078 | } 1079 | } 1080 | open OUTPUT, ">$OUTFILE" or die "Cannot open $OUTFILE: $!\n"; 1081 | binmode (OUTPUT) if $^O eq 'MSWin32'; 1082 | my $tree = readXml($FILE); 1083 | print_header($FILE, \*OUTPUT); 1084 | parseTree(\*OUTPUT, $tree); 1085 | close OUTPUT; 1086 | print "CREATED $OUTFILE\n" unless $QUIET_ARG; 1087 | } 1088 | 1089 | sub keys_merge_translations 1090 | { 1091 | open INPUT, "<${FILE}" or die; 1092 | open OUTPUT, ">${OUTFILE}" or die; 1093 | binmode (OUTPUT) if $^O eq 'MSWin32'; 1094 | 1095 | while () 1096 | { 1097 | if (s/^(\s*)_(\w+=(.*))/$1$2/) 1098 | { 1099 | my $string = $3; 1100 | 1101 | print OUTPUT; 1102 | 1103 | my $non_translated_line = $_; 1104 | 1105 | for my $lang (sort keys %po_files_by_lang) 1106 | { 1107 | my $translation = $translations{$lang, $string}; 1108 | next if !$translation; 1109 | 1110 | $_ = $non_translated_line; 1111 | s/(\w+)=.*/[$lang]$1=$translation/; 1112 | print OUTPUT; 1113 | } 1114 | } 1115 | else 1116 | { 1117 | print OUTPUT; 1118 | } 1119 | } 1120 | 1121 | close OUTPUT; 1122 | close INPUT; 1123 | } 1124 | 1125 | sub desktop_merge_translations 1126 | { 1127 | open INPUT, "<${FILE}" or die; 1128 | open OUTPUT, ">${OUTFILE}" or die; 1129 | binmode (OUTPUT) if $^O eq 'MSWin32'; 1130 | 1131 | while () 1132 | { 1133 | if (s/^(\s*)_(\w+=(.*))/$1$2/) 1134 | { 1135 | my $string = $3; 1136 | 1137 | print OUTPUT; 1138 | 1139 | my $non_translated_line = $_; 1140 | 1141 | for my $lang (sort keys %po_files_by_lang) 1142 | { 1143 | my $translation = $translations{$lang, $string}; 1144 | next if !$translation; 1145 | 1146 | $_ = $non_translated_line; 1147 | s/(\w+)=.*/${1}[$lang]=$translation/; 1148 | print OUTPUT; 1149 | } 1150 | } 1151 | else 1152 | { 1153 | print OUTPUT; 1154 | } 1155 | } 1156 | 1157 | close OUTPUT; 1158 | close INPUT; 1159 | } 1160 | 1161 | sub schemas_merge_translations 1162 | { 1163 | my $source; 1164 | 1165 | { 1166 | local $/; # slurp mode 1167 | open INPUT, "<$FILE" or die "can't open $FILE: $!"; 1168 | $source = ; 1169 | close INPUT; 1170 | } 1171 | 1172 | open OUTPUT, ">$OUTFILE" or die; 1173 | binmode (OUTPUT) if $^O eq 'MSWin32'; 1174 | 1175 | # FIXME: support attribute translations 1176 | 1177 | # Empty nodes never need translation, so unmark all of them. 1178 | # For example, <_foo/> is just replaced by . 1179 | $source =~ s|<\s*_($w+)\s*/>|<$1/>|g; 1180 | 1181 | while ($source =~ s/ 1182 | (.*?) 1183 | (\s+)((\s*) 1184 | (\s*(?:\s*)?(.*?)\s*<\/default>)?(\s*) 1185 | (\s*(?:\s*)?(.*?)\s*<\/short>)?(\s*) 1186 | (\s*(?:\s*)?(.*?)\s*<\/long>)?(\s*) 1187 | <\/locale>) 1188 | //sx) 1189 | { 1190 | print OUTPUT $1; 1191 | 1192 | my $locale_start_spaces = $2 ? $2 : ''; 1193 | my $default_spaces = $4 ? $4 : ''; 1194 | my $short_spaces = $7 ? $7 : ''; 1195 | my $long_spaces = $10 ? $10 : ''; 1196 | my $locale_end_spaces = $13 ? $13 : ''; 1197 | my $c_default_block = $3 ? $3 : ''; 1198 | my $default_string = $6 ? $6 : ''; 1199 | my $short_string = $9 ? $9 : ''; 1200 | my $long_string = $12 ? $12 : ''; 1201 | 1202 | print OUTPUT "$locale_start_spaces$c_default_block"; 1203 | 1204 | $default_string =~ s/\s+/ /g; 1205 | $default_string = entity_decode($default_string); 1206 | $short_string =~ s/\s+/ /g; 1207 | $short_string = entity_decode($short_string); 1208 | $long_string =~ s/\s+/ /g; 1209 | $long_string = entity_decode($long_string); 1210 | 1211 | for my $lang (sort keys %po_files_by_lang) 1212 | { 1213 | my $default_translation = $translations{$lang, $default_string}; 1214 | my $short_translation = $translations{$lang, $short_string}; 1215 | my $long_translation = $translations{$lang, $long_string}; 1216 | 1217 | next if (!$default_translation && !$short_translation && 1218 | !$long_translation); 1219 | 1220 | print OUTPUT "\n$locale_start_spaces"; 1221 | 1222 | print OUTPUT "$default_spaces"; 1223 | 1224 | if ($default_translation) 1225 | { 1226 | $default_translation = entity_encode($default_translation); 1227 | print OUTPUT "$default_translation"; 1228 | } 1229 | 1230 | print OUTPUT "$short_spaces"; 1231 | 1232 | if ($short_translation) 1233 | { 1234 | $short_translation = entity_encode($short_translation); 1235 | print OUTPUT "$short_translation"; 1236 | } 1237 | 1238 | print OUTPUT "$long_spaces"; 1239 | 1240 | if ($long_translation) 1241 | { 1242 | $long_translation = entity_encode($long_translation); 1243 | print OUTPUT "$long_translation"; 1244 | } 1245 | 1246 | print OUTPUT "$locale_end_spaces"; 1247 | } 1248 | } 1249 | 1250 | print OUTPUT $source; 1251 | 1252 | close OUTPUT; 1253 | } 1254 | 1255 | sub rfc822deb_merge_translations 1256 | { 1257 | my %encodings = (); 1258 | for my $lang (keys %po_files_by_lang) { 1259 | $encodings{$lang} = ($UTF8_ARG ? 'UTF-8' : get_po_encoding($po_files_by_lang{$lang})); 1260 | } 1261 | 1262 | my $source; 1263 | 1264 | $Text::Wrap::huge = 'overflow'; 1265 | $Text::Wrap::break = qr/\n|\s(?=\S)/; 1266 | 1267 | { 1268 | local $/; # slurp mode 1269 | open INPUT, "<$FILE" or die "can't open $FILE: $!"; 1270 | $source = ; 1271 | close INPUT; 1272 | } 1273 | 1274 | open OUTPUT, ">${OUTFILE}" or die; 1275 | binmode (OUTPUT) if $^O eq 'MSWin32'; 1276 | 1277 | while ($source =~ /(^|\n+)(_*)([^:\s]+)(:[ \t]*)(.*?)(?=\n[\S\n]|$)/sg) 1278 | { 1279 | my $sep = $1; 1280 | my $non_translated_line = $3.$4; 1281 | my $string = $5; 1282 | my $underscore = length($2); 1283 | next if $underscore eq 0 && $non_translated_line =~ /^#/; 1284 | # Remove [] dummy strings 1285 | my $stripped = $string; 1286 | $stripped =~ s/\[\s[^\[\]]*\],/,/g if $underscore eq 2; 1287 | $stripped =~ s/\[\s[^\[\]]*\]$//; 1288 | $non_translated_line .= $stripped; 1289 | 1290 | print OUTPUT $sep.$non_translated_line; 1291 | 1292 | if ($underscore) 1293 | { 1294 | my @str_list = rfc822deb_split($underscore, $string); 1295 | 1296 | for my $lang (sort keys %po_files_by_lang) 1297 | { 1298 | my $is_translated = 1; 1299 | my $str_translated = ''; 1300 | my $first = 1; 1301 | 1302 | for my $str (@str_list) 1303 | { 1304 | my $translation = $translations{$lang, $str}; 1305 | 1306 | if (!$translation) 1307 | { 1308 | $is_translated = 0; 1309 | last; 1310 | } 1311 | 1312 | # $translation may also contain [] dummy 1313 | # strings, mostly to indicate an empty string 1314 | $translation =~ s/\[\s[^\[\]]*\]$//; 1315 | 1316 | if ($first) 1317 | { 1318 | if ($underscore eq 2) 1319 | { 1320 | $str_translated .= $translation; 1321 | } 1322 | else 1323 | { 1324 | $str_translated .= 1325 | Text::Tabs::expand($translation) . 1326 | "\n"; 1327 | } 1328 | } 1329 | else 1330 | { 1331 | if ($underscore eq 2) 1332 | { 1333 | $str_translated .= ', ' . $translation; 1334 | } 1335 | else 1336 | { 1337 | $str_translated .= Text::Tabs::expand( 1338 | Text::Wrap::wrap(' ', ' ', $translation)) . 1339 | "\n .\n"; 1340 | } 1341 | } 1342 | $first = 0; 1343 | 1344 | # To fix some problems with Text::Wrap::wrap 1345 | $str_translated =~ s/(\n )+\n/\n .\n/g; 1346 | } 1347 | next unless $is_translated; 1348 | 1349 | $str_translated =~ s/\n \.\n$//; 1350 | $str_translated =~ s/\s+$//; 1351 | 1352 | $_ = $non_translated_line; 1353 | s/^(\w+):\s*.*/$sep${1}-$lang.$encodings{$lang}: $str_translated/s; 1354 | print OUTPUT; 1355 | } 1356 | } 1357 | } 1358 | print OUTPUT "\n"; 1359 | 1360 | close OUTPUT; 1361 | close INPUT; 1362 | } 1363 | 1364 | sub rfc822deb_split 1365 | { 1366 | # Debian defines a special way to deal with rfc822-style files: 1367 | # when a value contain newlines, it consists of 1368 | # 1. a short form (first line) 1369 | # 2. a long description, all lines begin with a space, 1370 | # and paragraphs are separated by a single dot on a line 1371 | # This routine returns an array of all paragraphs, and reformat 1372 | # them. 1373 | # When first argument is 2, the string is a comma separated list of 1374 | # values. 1375 | my $type = shift; 1376 | my $text = shift; 1377 | $text =~ s/^[ \t]//mg; 1378 | return (split(/, */, $text, 0)) if $type ne 1; 1379 | return ($text) if $text !~ /\n/; 1380 | 1381 | $text =~ s/([^\n]*)\n//; 1382 | my @list = ($1); 1383 | my $str = ''; 1384 | 1385 | for my $line (split (/\n/, $text)) 1386 | { 1387 | chomp $line; 1388 | if ($line =~ /^\.\s*$/) 1389 | { 1390 | # New paragraph 1391 | $str =~ s/\s*$//; 1392 | push(@list, $str); 1393 | $str = ''; 1394 | } 1395 | elsif ($line =~ /^\s/) 1396 | { 1397 | # Line which must not be reformatted 1398 | $str .= "\n" if length ($str) && $str !~ /\n$/; 1399 | $line =~ s/\s+$//; 1400 | $str .= $line."\n"; 1401 | } 1402 | else 1403 | { 1404 | # Continuation line, remove newline 1405 | $str .= " " if length ($str) && $str !~ /\n$/; 1406 | $str .= $line; 1407 | } 1408 | } 1409 | 1410 | $str =~ s/\s*$//; 1411 | push(@list, $str) if length ($str); 1412 | 1413 | return @list; 1414 | } 1415 | 1416 | sub quoted_translation 1417 | { 1418 | my ($lang, $string) = @_; 1419 | 1420 | $string =~ s/\\\"/\"/g; 1421 | 1422 | my $translation = $translations{$lang, $string}; 1423 | $translation = $string if !$translation; 1424 | 1425 | $translation =~ s/\"/\\\"/g; 1426 | return $translation 1427 | } 1428 | 1429 | sub quoted_merge_translations 1430 | { 1431 | if (!$MULTIPLE_OUTPUT) { 1432 | print "Quoted only supports Multiple Output.\n"; 1433 | exit(1); 1434 | } 1435 | 1436 | for my $lang (sort keys %po_files_by_lang) { 1437 | if ( ! -d $lang ) { 1438 | mkdir $lang or -d $lang or die "Cannot create subdirectory $lang: $!\n"; 1439 | } 1440 | open INPUT, "<${FILE}" or die; 1441 | open OUTPUT, ">$lang/$OUTFILE" or die "Cannot open $lang/$OUTFILE: $!\n"; 1442 | binmode (OUTPUT) if $^O eq 'MSWin32'; 1443 | while () 1444 | { 1445 | s/\"(([^\"]|\\\")*[^\\\"])\"/"\"" . "ed_translation($lang, $1) . "\""/ge; 1446 | print OUTPUT; 1447 | } 1448 | close OUTPUT; 1449 | close INPUT; 1450 | } 1451 | } 1452 | -------------------------------------------------------------------------------- /intltool-update.in: -------------------------------------------------------------------------------- 1 | #!@INTLTOOL_PERL@ -w 2 | # -*- Mode: perl; indent-tabs-mode: nil; c-basic-offset: 4 -*- 3 | 4 | # 5 | # The Intltool Message Updater 6 | # 7 | # Copyright (C) 2000-2003 Free Software Foundation. 8 | # 9 | # Intltool is free software; you can redistribute it and/or 10 | # modify it under the terms of the GNU General Public License 11 | # version 2 published by the Free Software Foundation. 12 | # 13 | # Intltool is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | # General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program; if not, write to the Free Software 20 | # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21 | # 22 | # As a special exception to the GNU General Public License, if you 23 | # distribute this file as part of a program that contains a 24 | # configuration script generated by Autoconf, you may include it under 25 | # the same distribution terms that you use for the rest of that program. 26 | # 27 | # Authors: Kenneth Christiansen 28 | # Maciej Stachowiak 29 | # Darin Adler 30 | 31 | ## Release information 32 | my $PROGRAM = "intltool-update"; 33 | my $VERSION = "0.36.2"; 34 | my $PACKAGE = "intltool"; 35 | 36 | ## Loaded modules 37 | use strict; 38 | use Getopt::Long; 39 | use Cwd; 40 | use File::Copy; 41 | use File::Find; 42 | 43 | ## Scalars used by the option stuff 44 | my $HELP_ARG = 0; 45 | my $VERSION_ARG = 0; 46 | my $DIST_ARG = 0; 47 | my $POT_ARG = 0; 48 | my $HEADERS_ARG = 0; 49 | my $MAINTAIN_ARG = 0; 50 | my $REPORT_ARG = 0; 51 | my $VERBOSE = 0; 52 | my $GETTEXT_PACKAGE = ""; 53 | my $OUTPUT_FILE = ""; 54 | 55 | my @languages; 56 | my %varhash = (); 57 | my %po_files_by_lang = (); 58 | 59 | # Regular expressions to categorize file types. 60 | # FIXME: Please check if the following is correct 61 | 62 | my $xml_support = 63 | "xml(?:\\.in)*|". # http://www.w3.org/XML/ (Note: .in is not required) 64 | "ui|". # Bonobo specific - User Interface desc. files 65 | "lang|". # ? 66 | "glade2?(?:\\.in)*|". # Glade specific - User Interface desc. files (Note: .in is not required) 67 | "scm(?:\\.in)*|". # ? (Note: .in is not required) 68 | "oaf(?:\\.in)+|". # DEPRECATED: Replaces by Bonobo .server files 69 | "etspec|". # ? 70 | "server(?:\\.in)+|". # Bonobo specific 71 | "sheet(?:\\.in)+|". # ? 72 | "schemas(?:\\.in)+|". # GConf specific 73 | "pong(?:\\.in)+|". # DEPRECATED: PONG is not used [by GNOME] any longer. 74 | "kbd(?:\\.in)+|". # GOK specific. 75 | "policy(?:\\.in)+"; # PolicyKit files 76 | 77 | my $ini_support = 78 | "icon(?:\\.in)+|". # http://www.freedesktop.org/Standards/icon-theme-spec 79 | "desktop(?:\\.in)+|". # http://www.freedesktop.org/Standards/menu-spec 80 | "caves(?:\\.in)+|". # GNOME Games specific 81 | "directory(?:\\.in)+|". # http://www.freedesktop.org/Standards/menu-spec 82 | "soundlist(?:\\.in)+|". # GNOME specific 83 | "keys(?:\\.in)+|". # GNOME Mime database specific 84 | "theme(?:\\.in)+|". # http://www.freedesktop.org/Standards/icon-theme-spec 85 | "service(?:\\.in)+"; # DBus specific 86 | 87 | my $buildin_gettext_support = 88 | "c|y|cs|cc|cpp|c\\+\\+|h|hh|gob|py"; 89 | 90 | ## Always flush buffer when printing 91 | $| = 1; 92 | 93 | ## Sometimes the source tree will be rooted somewhere else. 94 | my $SRCDIR = $ENV{"srcdir"} || "."; 95 | my $POTFILES_in; 96 | 97 | $POTFILES_in = "<$SRCDIR/POTFILES.in"; 98 | 99 | my $devnull = ($^O eq 'MSWin32' ? 'NUL:' : '/dev/null'); 100 | 101 | ## Handle options 102 | GetOptions 103 | ( 104 | "help" => \$HELP_ARG, 105 | "version" => \$VERSION_ARG, 106 | "dist|d" => \$DIST_ARG, 107 | "pot|p" => \$POT_ARG, 108 | "headers|s" => \$HEADERS_ARG, 109 | "maintain|m" => \$MAINTAIN_ARG, 110 | "report|r" => \$REPORT_ARG, 111 | "verbose|x" => \$VERBOSE, 112 | "gettext-package|g=s" => \$GETTEXT_PACKAGE, 113 | "output-file|o=s" => \$OUTPUT_FILE, 114 | ) or &Console_WriteError_InvalidOption; 115 | 116 | &Console_Write_IntltoolHelp if $HELP_ARG; 117 | &Console_Write_IntltoolVersion if $VERSION_ARG; 118 | 119 | my $arg_count = ($DIST_ARG > 0) 120 | + ($POT_ARG > 0) 121 | + ($HEADERS_ARG > 0) 122 | + ($MAINTAIN_ARG > 0) 123 | + ($REPORT_ARG > 0); 124 | 125 | &Console_Write_IntltoolHelp if $arg_count > 1; 126 | 127 | my $PKGNAME = FindPackageName (); 128 | 129 | # --version and --help don't require a module name 130 | my $MODULE = $GETTEXT_PACKAGE || $PKGNAME || "unknown"; 131 | 132 | if ($POT_ARG) 133 | { 134 | &GenerateHeaders; 135 | &GeneratePOTemplate; 136 | } 137 | elsif ($HEADERS_ARG) 138 | { 139 | &GenerateHeaders; 140 | } 141 | elsif ($MAINTAIN_ARG) 142 | { 143 | &FindLeftoutFiles; 144 | } 145 | elsif ($REPORT_ARG) 146 | { 147 | &GenerateHeaders; 148 | &GeneratePOTemplate; 149 | &Console_Write_CoverageReport; 150 | } 151 | elsif ((defined $ARGV[0]) && $ARGV[0] =~ /^[a-z]/) 152 | { 153 | my $lang = $ARGV[0]; 154 | 155 | ## Report error if the language file supplied 156 | ## to the command line is non-existent 157 | &Console_WriteError_NotExisting("$SRCDIR/$lang.po") 158 | if ! -s "$SRCDIR/$lang.po"; 159 | 160 | if (!$DIST_ARG) 161 | { 162 | print "Working, please wait..." if $VERBOSE; 163 | &GenerateHeaders; 164 | &GeneratePOTemplate; 165 | } 166 | &POFile_Update ($lang, $OUTPUT_FILE); 167 | &Console_Write_TranslationStatus ($lang, $OUTPUT_FILE); 168 | } 169 | else 170 | { 171 | &Console_Write_IntltoolHelp; 172 | } 173 | 174 | exit; 175 | 176 | ######### 177 | 178 | sub Console_Write_IntltoolVersion 179 | { 180 | print <<_EOF_; 181 | ${PROGRAM} (${PACKAGE}) $VERSION 182 | Written by Kenneth Christiansen, Maciej Stachowiak, and Darin Adler. 183 | 184 | Copyright (C) 2000-2003 Free Software Foundation, Inc. 185 | This is free software; see the source for copying conditions. There is NO 186 | warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 187 | _EOF_ 188 | exit; 189 | } 190 | 191 | sub Console_Write_IntltoolHelp 192 | { 193 | print <<_EOF_; 194 | Usage: ${PROGRAM} [OPTION]... LANGCODE 195 | Updates PO template files and merge them with the translations. 196 | 197 | Mode of operation (only one is allowed): 198 | -p, --pot generate the PO template only 199 | -s, --headers generate the header files in POTFILES.in 200 | -m, --maintain search for left out files from POTFILES.in 201 | -r, --report display a status report for the module 202 | -d, --dist merge LANGCODE.po with existing PO template 203 | 204 | Extra options: 205 | -g, --gettext-package=NAME override PO template name, useful with --pot 206 | -o, --output-file=FILE write merged translation to FILE 207 | -x, --verbose display lots of feedback 208 | --help display this help and exit 209 | --version output version information and exit 210 | 211 | Examples of use: 212 | ${PROGRAM} --pot just create a new PO template 213 | ${PROGRAM} xy create new PO template and merge xy.po with it 214 | 215 | Report bugs to http://bugzilla.gnome.org/ (product name "$PACKAGE") 216 | or send email to . 217 | _EOF_ 218 | exit; 219 | } 220 | 221 | sub echo_n 222 | { 223 | my $str = shift; 224 | my $ret = `echo "$str"`; 225 | 226 | $ret =~ s/\n$//; # do we need the "s" flag? 227 | 228 | return $ret; 229 | } 230 | 231 | sub POFile_DetermineType ($) 232 | { 233 | my $type = $_; 234 | my $gettext_type; 235 | 236 | my $xml_regex = "(?:" . $xml_support . ")"; 237 | my $ini_regex = "(?:" . $ini_support . ")"; 238 | my $buildin_regex = "(?:" . $buildin_gettext_support . ")"; 239 | 240 | if ($type =~ /\[type: gettext\/([^\]].*)]/) 241 | { 242 | $gettext_type=$1; 243 | } 244 | elsif ($type =~ /schemas(\.in)+$/) 245 | { 246 | $gettext_type="schemas"; 247 | } 248 | elsif ($type =~ /glade2?(\.in)*$/) 249 | { 250 | $gettext_type="glade"; 251 | } 252 | elsif ($type =~ /scm(\.in)*$/) 253 | { 254 | $gettext_type="scheme"; 255 | } 256 | elsif ($type =~ /keys(\.in)+$/) 257 | { 258 | $gettext_type="keys"; 259 | } 260 | 261 | # bucket types 262 | 263 | elsif ($type =~ /$xml_regex$/) 264 | { 265 | $gettext_type="xml"; 266 | } 267 | elsif ($type =~ /$ini_regex$/) 268 | { 269 | $gettext_type="ini"; 270 | } 271 | elsif ($type =~ /$buildin_regex$/) 272 | { 273 | $gettext_type="buildin"; 274 | } 275 | else 276 | { 277 | $gettext_type="unknown"; 278 | } 279 | 280 | return "gettext\/$gettext_type"; 281 | } 282 | 283 | sub TextFile_DetermineEncoding ($) 284 | { 285 | my $gettext_code="ASCII"; # All files are ASCII by default 286 | my $filetype=`file $_ | cut -d ' ' -f 2`; 287 | 288 | if ($? eq "0") 289 | { 290 | if ($filetype =~ /^(ISO|UTF)/) 291 | { 292 | chomp ($gettext_code = $filetype); 293 | } 294 | elsif ($filetype =~ /^XML/) 295 | { 296 | $gettext_code="UTF-8"; # We asume that .glade and other .xml files are UTF-8 297 | } 298 | } 299 | 300 | return $gettext_code; 301 | } 302 | 303 | sub isNotValidMissing 304 | { 305 | my ($file) = @_; 306 | 307 | return if $file =~ /^\{arch\}\/.*$/; 308 | return if $file =~ /^$varhash{"PACKAGE"}-$varhash{"VERSION"}\/.*$/; 309 | } 310 | 311 | sub FindLeftoutFiles 312 | { 313 | my (@buf_i18n_plain, 314 | @buf_i18n_xml, 315 | @buf_i18n_xml_unmarked, 316 | @buf_i18n_ini, 317 | @buf_potfiles, 318 | @buf_potfiles_ignore, 319 | @buf_allfiles, 320 | @buf_allfiles_sorted, 321 | @buf_potfiles_sorted, 322 | @buf_potfiles_ignore_sorted 323 | ); 324 | 325 | ## Search and find all translatable files 326 | find sub { 327 | push @buf_i18n_plain, "$File::Find::name" if /\.($buildin_gettext_support)$/; 328 | push @buf_i18n_xml, "$File::Find::name" if /\.($xml_support)$/; 329 | push @buf_i18n_ini, "$File::Find::name" if /\.($ini_support)$/; 330 | push @buf_i18n_xml_unmarked, "$File::Find::name" if /\.(schemas(\.in)+)$/; 331 | }, ".."; 332 | find sub { 333 | push @buf_i18n_plain, "$File::Find::name" if /\.($buildin_gettext_support)$/; 334 | push @buf_i18n_xml, "$File::Find::name" if /\.($xml_support)$/; 335 | push @buf_i18n_ini, "$File::Find::name" if /\.($ini_support)$/; 336 | push @buf_i18n_xml_unmarked, "$File::Find::name" if /\.(schemas(\.in)+)$/; 337 | }, "$SRCDIR/.."; 338 | 339 | open POTFILES, $POTFILES_in or die "$PROGRAM: there's no POTFILES.in!\n"; 340 | @buf_potfiles = grep !/^(#|\s*$)/, ; 341 | close POTFILES; 342 | 343 | foreach (@buf_potfiles) { 344 | s/^\[.*]\s*//; 345 | } 346 | 347 | print "Searching for missing translatable files...\n" if $VERBOSE; 348 | 349 | ## Check if we should ignore some found files, when 350 | ## comparing with POTFILES.in 351 | foreach my $ignore ("POTFILES.skip", "POTFILES.ignore") 352 | { 353 | (-s "$SRCDIR/$ignore") or next; 354 | 355 | if ("$ignore" eq "POTFILES.ignore") 356 | { 357 | print "The usage of POTFILES.ignore is deprecated. Please consider moving the\n". 358 | "content of this file to POTFILES.skip.\n"; 359 | } 360 | 361 | print "Found $ignore: Ignoring files...\n" if $VERBOSE; 362 | open FILE, "<$SRCDIR/$ignore" or die "ERROR: Failed to open $SRCDIR/$ignore!\n"; 363 | 364 | while () 365 | { 366 | push @buf_potfiles_ignore, $_ unless /^(#|\s*$)/; 367 | } 368 | close FILE; 369 | 370 | @buf_potfiles_ignore_sorted = sort (@buf_potfiles_ignore); 371 | } 372 | 373 | foreach my $file (@buf_i18n_plain) 374 | { 375 | my $in_comment = 0; 376 | my $in_macro = 0; 377 | 378 | open FILE, "<$file"; 379 | while () 380 | { 381 | # Handle continued multi-line comment. 382 | if ($in_comment) 383 | { 384 | next unless s-.*\*/--; 385 | $in_comment = 0; 386 | } 387 | 388 | # Handle continued macro. 389 | if ($in_macro) 390 | { 391 | $in_macro = 0 unless /\\$/; 392 | next; 393 | } 394 | 395 | # Handle start of macro (or any preprocessor directive). 396 | if (/^\s*\#/) 397 | { 398 | $in_macro = 1 if /^([^\\]|\\.)*\\$/; 399 | next; 400 | } 401 | 402 | # Handle comments and quoted text. 403 | while (m-(/\*|//|\'|\")-) # \' and \" keep emacs perl mode happy 404 | { 405 | my $match = $1; 406 | if ($match eq "/*") 407 | { 408 | if (!s-/\*.*?\*/--) 409 | { 410 | s-/\*.*--; 411 | $in_comment = 1; 412 | } 413 | } 414 | elsif ($match eq "//") 415 | { 416 | s-//.*--; 417 | } 418 | else # ' or " 419 | { 420 | if (!s-$match([^\\]|\\.)*?$match-QUOTEDTEXT-) 421 | { 422 | warn "mismatched quotes at line $. in $file\n"; 423 | s-$match.*--; 424 | } 425 | } 426 | } 427 | 428 | if (/\w\.GetString *\(QUOTEDTEXT/) 429 | { 430 | if (defined isNotValidMissing (unpack("x3 A*", $file))) { 431 | ## Remove the first 3 chars and add newline 432 | push @buf_allfiles, unpack("x3 A*", $file) . "\n"; 433 | } 434 | last; 435 | } 436 | 437 | ## N_ Q_ and _ are the three macros defined in gi8n.h 438 | if (/[NQ]?_ *\(QUOTEDTEXT/) 439 | { 440 | if (defined isNotValidMissing (unpack("x3 A*", $file))) { 441 | ## Remove the first 3 chars and add newline 442 | push @buf_allfiles, unpack("x3 A*", $file) . "\n"; 443 | } 444 | last; 445 | } 446 | } 447 | close FILE; 448 | } 449 | 450 | foreach my $file (@buf_i18n_xml) 451 | { 452 | open FILE, "<$file"; 453 | 454 | while () 455 | { 456 | # FIXME: share the pattern matching code with intltool-extract 457 | if (/\s_[-A-Za-z0-9._:]+\s*=\s*\"([^"]+)\"/ || /<_[^>]+>/ || /translatable=\"yes\"/) 458 | { 459 | if (defined isNotValidMissing (unpack("x3 A*", $file))) { 460 | push @buf_allfiles, unpack("x3 A*", $file) . "\n"; 461 | } 462 | last; 463 | } 464 | } 465 | close FILE; 466 | } 467 | 468 | foreach my $file (@buf_i18n_ini) 469 | { 470 | open FILE, "<$file"; 471 | while () 472 | { 473 | if (/_(.*)=/) 474 | { 475 | if (defined isNotValidMissing (unpack("x3 A*", $file))) { 476 | push @buf_allfiles, unpack("x3 A*", $file) . "\n"; 477 | } 478 | last; 479 | } 480 | } 481 | close FILE; 482 | } 483 | 484 | foreach my $file (@buf_i18n_xml_unmarked) 485 | { 486 | if (defined isNotValidMissing (unpack("x3 A*", $file))) { 487 | push @buf_allfiles, unpack("x3 A*", $file) . "\n"; 488 | } 489 | } 490 | 491 | 492 | @buf_allfiles_sorted = sort (@buf_allfiles); 493 | @buf_potfiles_sorted = sort (@buf_potfiles); 494 | 495 | my %in2; 496 | foreach (@buf_potfiles_sorted) 497 | { 498 | s#^$SRCDIR/../##; 499 | s#^$SRCDIR/##; 500 | $in2{$_} = 1; 501 | } 502 | 503 | foreach (@buf_potfiles_ignore_sorted) 504 | { 505 | s#^$SRCDIR/../##; 506 | s#^$SRCDIR/##; 507 | $in2{$_} = 1; 508 | } 509 | 510 | my @result; 511 | 512 | foreach (@buf_allfiles_sorted) 513 | { 514 | my $dummy = $_; 515 | my $srcdir = $SRCDIR; 516 | 517 | $srcdir =~ s#^../##; 518 | $dummy =~ s#^$srcdir/../##; 519 | $dummy =~ s#^$srcdir/##; 520 | $dummy =~ s#_build/##; 521 | if (!exists($in2{$dummy})) 522 | { 523 | push @result, $dummy 524 | } 525 | } 526 | 527 | my @buf_potfiles_notexist; 528 | 529 | foreach (@buf_potfiles_sorted) 530 | { 531 | chomp (my $dummy = $_); 532 | if ("$dummy" ne "" and !(-f "$SRCDIR/../$dummy" or -f "../$dummy")) 533 | { 534 | push @buf_potfiles_notexist, $_; 535 | } 536 | } 537 | 538 | ## Save file with information about the files missing 539 | ## if any, and give information about this procedure. 540 | if (@result + @buf_potfiles_notexist > 0) 541 | { 542 | if (@result) 543 | { 544 | print "\n" if $VERBOSE; 545 | unlink "missing"; 546 | open OUT, ">missing"; 547 | print OUT @result; 548 | close OUT; 549 | warn "\e[1mThe following files contain translations and are currently not in use. Please\e[0m\n". 550 | "\e[1mconsider adding these to the POTFILES.in file, located in the po/ directory.\e[0m\n\n"; 551 | print STDERR @result, "\n"; 552 | warn "If some of these files are left out on purpose then please add them to\n". 553 | "POTFILES.skip instead of POTFILES.in. A file \e[1m'missing'\e[0m containing this list\n". 554 | "of left out files has been written in the current directory.\n"; 555 | } 556 | if (@buf_potfiles_notexist) 557 | { 558 | unlink "notexist"; 559 | open OUT, ">notexist"; 560 | print OUT @buf_potfiles_notexist; 561 | close OUT; 562 | warn "\n" if ($VERBOSE or @result); 563 | warn "\e[1mThe following files do not exist anymore:\e[0m\n\n"; 564 | warn @buf_potfiles_notexist, "\n"; 565 | warn "Please remove them from POTFILES.in. A file \e[1m'notexist'\e[0m\n". 566 | "containing this list of absent files has been written in the current directory.\n"; 567 | } 568 | } 569 | 570 | ## If there is nothing to complain about, notify the user 571 | else { 572 | print "\nAll files containing translations are present in POTFILES.in.\n" if $VERBOSE; 573 | } 574 | } 575 | 576 | sub Console_WriteError_InvalidOption 577 | { 578 | ## Handle invalid arguments 579 | print STDERR "Try `${PROGRAM} --help' for more information.\n"; 580 | exit 1; 581 | } 582 | 583 | sub isProgramInPath 584 | { 585 | my ($file) = @_; 586 | # If either a file exists, or when run it returns 0 exit status 587 | return 1 if ((-x $file) or (system("$file --version >$devnull") == 0)); 588 | return 0; 589 | } 590 | 591 | sub isGNUGettextTool 592 | { 593 | my ($file) = @_; 594 | # Check that we are using GNU gettext tools 595 | if (isProgramInPath ($file)) 596 | { 597 | my $version = `$file --version`; 598 | return 1 if ($version =~ m/.*\(GNU .*\).*/); 599 | } 600 | return 0; 601 | } 602 | 603 | sub GenerateHeaders 604 | { 605 | my $EXTRACT = $ENV{"INTLTOOL_EXTRACT"} || "intltool-extract"; 606 | 607 | ## Generate the .h header files, so we can allow glade and 608 | ## xml translation support 609 | if (! isProgramInPath ("$EXTRACT")) 610 | { 611 | print STDERR "\n *** The intltool-extract script wasn't found!" 612 | ."\n *** Without it, intltool-update can not generate files.\n"; 613 | exit; 614 | } 615 | else 616 | { 617 | open (FILE, $POTFILES_in) or die "$PROGRAM: POTFILES.in not found.\n"; 618 | 619 | while () 620 | { 621 | chomp; 622 | next if /^\[\s*encoding/; 623 | 624 | ## Find xml files in POTFILES.in and generate the 625 | ## files with help from the extract script 626 | 627 | my $gettext_type= &POFile_DetermineType ($1); 628 | 629 | if (/\.($xml_support|$ini_support)$/ || /^\[/) 630 | { 631 | s/^\[[^\[].*]\s*//; 632 | 633 | my $filename = "../$_"; 634 | 635 | if ($VERBOSE) 636 | { 637 | system ($EXTRACT, "--update", "--srcdir=$SRCDIR", 638 | "--type=$gettext_type", $filename); 639 | } 640 | else 641 | { 642 | system ($EXTRACT, "--update", "--type=$gettext_type", 643 | "--srcdir=$SRCDIR", "--quiet", $filename); 644 | } 645 | } 646 | } 647 | close FILE; 648 | } 649 | } 650 | 651 | # 652 | # Generate .pot file from POTFILES.in 653 | # 654 | sub GeneratePOTemplate 655 | { 656 | my $XGETTEXT = $ENV{"XGETTEXT"} || "xgettext"; 657 | my $XGETTEXT_ARGS = $ENV{"XGETTEXT_ARGS"} || ''; 658 | chomp $XGETTEXT; 659 | 660 | if (! isGNUGettextTool ("$XGETTEXT")) 661 | { 662 | print STDERR " *** GNU xgettext is not found on this system!\n". 663 | " *** Without it, intltool-update can not extract strings.\n"; 664 | exit; 665 | } 666 | 667 | print "Building $MODULE.pot...\n" if $VERBOSE; 668 | 669 | open INFILE, $POTFILES_in; 670 | unlink "POTFILES.in.temp"; 671 | open OUTFILE, ">POTFILES.in.temp" or die("Cannot open POTFILES.in.temp for writing"); 672 | 673 | my $gettext_support_nonascii = 0; 674 | 675 | # checks for GNU gettext >= 0.12 676 | my $dummy = `$XGETTEXT --version --from-code=UTF-8 >$devnull 2>$devnull`; 677 | if ($? == 0) 678 | { 679 | $gettext_support_nonascii = 1; 680 | } 681 | else 682 | { 683 | # urge everybody to upgrade gettext 684 | print STDERR "WARNING: This version of gettext does not support extracting non-ASCII\n". 685 | " strings. That means you should install a version of gettext\n". 686 | " that supports non-ASCII strings (such as GNU gettext >= 0.12),\n". 687 | " or have to let non-ASCII strings untranslated. (If there is any)\n"; 688 | } 689 | 690 | my $encoding = "ASCII"; 691 | my $forced_gettext_code; 692 | my @temp_headers; 693 | my $encoding_problem_is_reported = 0; 694 | 695 | while () 696 | { 697 | next if (/^#/ or /^\s*$/); 698 | 699 | chomp; 700 | 701 | my $gettext_code; 702 | 703 | if (/^\[\s*encoding:\s*(.*)\s*\]/) 704 | { 705 | $forced_gettext_code=$1; 706 | } 707 | elsif (/\.($xml_support|$ini_support)$/ || /^\[/) 708 | { 709 | s/^\[.*]\s*//; 710 | print OUTFILE "../$_.h\n"; 711 | push @temp_headers, "../$_.h"; 712 | $gettext_code = &TextFile_DetermineEncoding ("../$_.h") if ($gettext_support_nonascii and not defined $forced_gettext_code); 713 | } 714 | else 715 | { 716 | print OUTFILE "$SRCDIR/../$_\n"; 717 | $gettext_code = &TextFile_DetermineEncoding ("$SRCDIR/../$_") if ($gettext_support_nonascii and not defined $forced_gettext_code); 718 | } 719 | 720 | next if (! $gettext_support_nonascii); 721 | 722 | if (defined $forced_gettext_code) 723 | { 724 | $encoding=$forced_gettext_code; 725 | } 726 | elsif (defined $gettext_code and "$encoding" ne "$gettext_code") 727 | { 728 | if ($encoding eq "ASCII") 729 | { 730 | $encoding=$gettext_code; 731 | } 732 | elsif ($gettext_code ne "ASCII") 733 | { 734 | # Only report once because the message is quite long 735 | if (! $encoding_problem_is_reported) 736 | { 737 | print STDERR "WARNING: You should use the same file encoding for all your project files,\n". 738 | " but $PROGRAM thinks that most of the source files are in\n". 739 | " $encoding encoding, while \"$_\" is (likely) in\n". 740 | " $gettext_code encoding. If you are sure that all translatable strings\n". 741 | " are in same encoding (say UTF-8), please \e[1m*prepend*\e[0m the following\n". 742 | " line to POTFILES.in:\n\n". 743 | " [encoding: UTF-8]\n\n". 744 | " and make sure that configure.in/ac checks for $PACKAGE >= 0.27 .\n". 745 | "(such warning message will only be reported once.)\n"; 746 | $encoding_problem_is_reported = 1; 747 | } 748 | } 749 | } 750 | } 751 | 752 | close OUTFILE; 753 | close INFILE; 754 | 755 | unlink "$MODULE.pot"; 756 | my @xgettext_argument=("$XGETTEXT", 757 | "--add-comments", 758 | "--directory\=.", 759 | "--default-domain\=$MODULE", 760 | "--flag\=g_strdup_printf:1:c-format", 761 | "--flag\=g_string_printf:2:c-format", 762 | "--flag\=g_string_append_printf:2:c-format", 763 | "--flag\=g_error_new:3:c-format", 764 | "--flag\=g_set_error:4:c-format", 765 | "--flag\=g_markup_printf_escaped:1:c-format", 766 | "--flag\=g_log:3:c-format", 767 | "--flag\=g_print:1:c-format", 768 | "--flag\=g_printerr:1:c-format", 769 | "--flag\=g_printf:1:c-format", 770 | "--flag\=g_fprintf:2:c-format", 771 | "--flag\=g_sprintf:2:c-format", 772 | "--flag\=g_snprintf:3:c-format", 773 | "--flag\=g_scanner_error:2:c-format", 774 | "--flag\=g_scanner_warn:2:c-format", 775 | "--output\=$MODULE\.pot", 776 | "--files-from\=\.\/POTFILES\.in\.temp"); 777 | my $XGETTEXT_KEYWORDS = &FindPOTKeywords; 778 | push @xgettext_argument, $XGETTEXT_KEYWORDS; 779 | my $MSGID_BUGS_ADDRESS = &FindMakevarsBugAddress; 780 | push @xgettext_argument, "--msgid-bugs-address\=$MSGID_BUGS_ADDRESS" if $MSGID_BUGS_ADDRESS; 781 | push @xgettext_argument, "--from-code\=$encoding" if ($gettext_support_nonascii); 782 | push @xgettext_argument, $XGETTEXT_ARGS if $XGETTEXT_ARGS; 783 | my $xgettext_command = join ' ', @xgettext_argument; 784 | 785 | # intercept xgettext error message 786 | print "Running $xgettext_command\n" if $VERBOSE; 787 | my $xgettext_error_msg = `$xgettext_command 2>\&1`; 788 | my $command_failed = $?; 789 | 790 | unlink "POTFILES.in.temp"; 791 | 792 | print "Removing generated header (.h) files..." if $VERBOSE; 793 | unlink foreach (@temp_headers); 794 | print "done.\n" if $VERBOSE; 795 | 796 | if (! $command_failed) 797 | { 798 | if (! -e "$MODULE.pot") 799 | { 800 | print "None of the files in POTFILES.in contain strings marked for translation.\n" if $VERBOSE; 801 | } 802 | else 803 | { 804 | print "Wrote $MODULE.pot\n" if $VERBOSE; 805 | } 806 | } 807 | else 808 | { 809 | if ($xgettext_error_msg =~ /--from-code/) 810 | { 811 | # replace non-ASCII error message with a more useful one. 812 | print STDERR "ERROR: xgettext failed to generate PO template file because there is non-ASCII\n". 813 | " string marked for translation. Please make sure that all strings marked\n". 814 | " for translation are in uniform encoding (say UTF-8), then \e[1m*prepend*\e[0m the\n". 815 | " following line to POTFILES.in and rerun $PROGRAM:\n\n". 816 | " [encoding: UTF-8]\n\n"; 817 | } 818 | else 819 | { 820 | print STDERR "$xgettext_error_msg"; 821 | if (-e "$MODULE.pot") 822 | { 823 | # is this possible? 824 | print STDERR "ERROR: xgettext failed but still managed to generate PO template file.\n". 825 | " Please consult error message above if there is any.\n"; 826 | } 827 | else 828 | { 829 | print STDERR "ERROR: xgettext failed to generate PO template file. Please consult\n". 830 | " error message above if there is any.\n"; 831 | } 832 | } 833 | exit (1); 834 | } 835 | } 836 | 837 | sub POFile_Update 838 | { 839 | -f "$MODULE.pot" or die "$PROGRAM: $MODULE.pot does not exist.\n"; 840 | 841 | my $MSGMERGE = $ENV{"MSGMERGE"} || "msgmerge"; 842 | my ($lang, $outfile) = @_; 843 | 844 | if (! isGNUGettextTool ("$MSGMERGE")) 845 | { 846 | print STDERR " *** GNU msgmerge is not found on this system!\n". 847 | " *** Without it, intltool-update can not extract strings.\n"; 848 | exit; 849 | } 850 | 851 | print "Merging $SRCDIR/$lang.po with $MODULE.pot..." if $VERBOSE; 852 | 853 | my $infile = "$SRCDIR/$lang.po"; 854 | $outfile = "$SRCDIR/$lang.po" if ($outfile eq ""); 855 | 856 | # I think msgmerge won't overwrite old file if merge is not successful 857 | system ("$MSGMERGE", "-o", $outfile, $infile, "$MODULE.pot"); 858 | } 859 | 860 | sub Console_WriteError_NotExisting 861 | { 862 | my ($file) = @_; 863 | 864 | ## Report error if supplied language file is non-existing 865 | print STDERR "$PROGRAM: $file does not exist!\n"; 866 | print STDERR "Try '$PROGRAM --help' for more information.\n"; 867 | exit; 868 | } 869 | 870 | sub GatherPOFiles 871 | { 872 | my @po_files = glob ("./*.po"); 873 | 874 | @languages = map (&POFile_GetLanguage, @po_files); 875 | 876 | foreach my $lang (@languages) 877 | { 878 | $po_files_by_lang{$lang} = shift (@po_files); 879 | } 880 | } 881 | 882 | sub POFile_GetLanguage ($) 883 | { 884 | s/^(.*\/)?(.+)\.po$/$2/; 885 | return $_; 886 | } 887 | 888 | sub Console_Write_TranslationStatus 889 | { 890 | my ($lang, $output_file) = @_; 891 | my $MSGFMT = $ENV{"MSGFMT"} || "msgfmt"; 892 | 893 | if (! isGNUGettextTool ("$MSGFMT")) 894 | { 895 | print STDERR " *** GNU msgfmt is not found on this system!\n". 896 | " *** Without it, intltool-update can not extract strings.\n"; 897 | exit; 898 | } 899 | 900 | $output_file = "$SRCDIR/$lang.po" if ($output_file eq ""); 901 | 902 | system ("$MSGFMT", "-o", "$devnull", "--verbose", $output_file); 903 | } 904 | 905 | sub Console_Write_CoverageReport 906 | { 907 | my $MSGFMT = $ENV{"MSGFMT"} || "msgfmt"; 908 | 909 | if (! isGNUGettextTool ("$MSGFMT")) 910 | { 911 | print STDERR " *** GNU msgfmt is not found on this system!\n". 912 | " *** Without it, intltool-update can not extract strings.\n"; 913 | exit; 914 | } 915 | 916 | &GatherPOFiles; 917 | 918 | foreach my $lang (@languages) 919 | { 920 | print STDERR "$lang: "; 921 | &POFile_Update ($lang, ""); 922 | } 923 | 924 | print STDERR "\n\n * Current translation support in $MODULE \n\n"; 925 | 926 | foreach my $lang (@languages) 927 | { 928 | print STDERR "$lang: "; 929 | system ("$MSGFMT", "-o", "$devnull", "--verbose", "$SRCDIR/$lang.po"); 930 | } 931 | } 932 | 933 | sub SubstituteVariable 934 | { 935 | my ($str) = @_; 936 | 937 | # always need to rewind file whenever it has been accessed 938 | seek (CONF, 0, 0); 939 | 940 | # cache each variable. varhash is global to we can add 941 | # variables elsewhere. 942 | while () 943 | { 944 | if (/^(\w+)=(.*)$/) 945 | { 946 | ($varhash{$1} = $2) =~ s/^["'](.*)["']$/$1/; 947 | } 948 | } 949 | 950 | if ($str =~ /^(.*)\${?([A-Z_]+)}?(.*)$/) 951 | { 952 | my $rest = $3; 953 | my $untouched = $1; 954 | my $sub = ""; 955 | # Ignore recursive definitions of variables 956 | $sub = $varhash{$2} if defined $varhash{$2} and $varhash{$2} !~ /\${?$2}?/; 957 | 958 | return SubstituteVariable ("$untouched$sub$rest"); 959 | } 960 | 961 | # We're using Perl backticks ` and "echo -n" here in order to 962 | # expand any shell escapes (such as backticks themselves) in every variable 963 | return echo_n ($str); 964 | } 965 | 966 | sub CONF_Handle_Open 967 | { 968 | my $base_dirname = getcwd(); 969 | $base_dirname =~ s@.*/@@; 970 | 971 | my ($conf_in, $src_dir); 972 | 973 | if ($base_dirname =~ /^po(-.+)?$/) 974 | { 975 | if (-f "Makevars") 976 | { 977 | my $makefile_source; 978 | 979 | local (*IN); 980 | open (IN, ") 983 | { 984 | if (/^top_builddir[ \t]*=/) 985 | { 986 | $src_dir = $_; 987 | $src_dir =~ s/^top_builddir[ \t]*=[ \t]*([^ \t\n\r]*)/$1/; 988 | 989 | chomp $src_dir; 990 | if (-f "$src_dir" . "/configure.ac") { 991 | $conf_in = "$src_dir" . "/configure.ac" . "\n"; 992 | } else { 993 | $conf_in = "$src_dir" . "/configure.in" . "\n"; 994 | } 995 | last; 996 | } 997 | } 998 | close IN; 999 | 1000 | $conf_in || die "Cannot find top_builddir in Makevars."; 1001 | } 1002 | elsif (-f "$SRCDIR/../configure.ac") 1003 | { 1004 | $conf_in = "$SRCDIR/../configure.ac"; 1005 | } 1006 | elsif (-f "$SRCDIR/../configure.in") 1007 | { 1008 | $conf_in = "$SRCDIR/../configure.in"; 1009 | } 1010 | else 1011 | { 1012 | my $makefile_source; 1013 | 1014 | local (*IN); 1015 | open (IN, ") 1018 | { 1019 | if (/^top_srcdir[ \t]*=/) 1020 | { 1021 | $src_dir = $_; 1022 | $src_dir =~ s/^top_srcdir[ \t]*=[ \t]*([^ \t\n\r]*)/$1/; 1023 | 1024 | chomp $src_dir; 1025 | $conf_in = "$src_dir" . "/configure.in" . "\n"; 1026 | 1027 | last; 1028 | } 1029 | } 1030 | close IN; 1031 | 1032 | $conf_in || die "Cannot find top_srcdir in Makefile."; 1033 | } 1034 | 1035 | open (CONF, "<$conf_in"); 1036 | } 1037 | else 1038 | { 1039 | print STDERR "$PROGRAM: Unable to proceed.\n" . 1040 | "Make sure to run this script inside the po directory.\n"; 1041 | exit; 1042 | } 1043 | } 1044 | 1045 | sub FindPackageName 1046 | { 1047 | my $version; 1048 | my $domain = &FindMakevarsDomain; 1049 | my $name = $domain || "untitled"; 1050 | 1051 | &CONF_Handle_Open; 1052 | 1053 | my $conf_source; { 1054 | local (*IN); 1055 | open (IN, "<&CONF") || return $name; 1056 | seek (IN, 0, 0); 1057 | local $/; # slurp mode 1058 | $conf_source = ; 1059 | close IN; 1060 | } 1061 | 1062 | # priority for getting package name: 1063 | # 1. GETTEXT_PACKAGE 1064 | # 2. first argument of AC_INIT (with >= 2 arguments) 1065 | # 3. first argument of AM_INIT_AUTOMAKE (with >= 2 argument) 1066 | 1067 | # /^AM_INIT_AUTOMAKE\([\s\[]*([^,\)\s\]]+)/m 1068 | # the \s makes this not work, why? 1069 | if ($conf_source =~ /^AM_INIT_AUTOMAKE\(([^,\)]+),([^,\)]+)/m) 1070 | { 1071 | ($name, $version) = ($1, $2); 1072 | $name =~ s/[\[\]\s]//g; 1073 | $version =~ s/[\[\]\s]//g; 1074 | $varhash{"PACKAGE_NAME"} = $name if (not $name =~ /\${?AC_PACKAGE_NAME}?/); 1075 | $varhash{"PACKAGE"} = $name if (not $name =~ /\${?PACKAGE}?/); 1076 | $varhash{"PACKAGE_VERSION"} = $version if (not $name =~ /\${?AC_PACKAGE_VERSION}?/); 1077 | $varhash{"VERSION"} = $version if (not $name =~ /\${?VERSION}?/); 1078 | } 1079 | 1080 | if ($conf_source =~ /^AC_INIT\(([^,\)]+),([^,\)]+)/m) 1081 | { 1082 | ($name, $version) = ($1, $2); 1083 | $name =~ s/[\[\]\s]//g; 1084 | $version =~ s/[\[\]\s]//g; 1085 | $varhash{"PACKAGE_NAME"} = $name if (not $name =~ /\${?AC_PACKAGE_NAME}?/); 1086 | $varhash{"PACKAGE"} = $name if (not $name =~ /\${?PACKAGE}?/); 1087 | $varhash{"PACKAGE_VERSION"} = $version if (not $name =~ /\${?AC_PACKAGE_VERSION}?/); 1088 | $varhash{"VERSION"} = $version if (not $name =~ /\${?VERSION}?/); 1089 | } 1090 | 1091 | # \s makes this not work, why? 1092 | $name = $1 if $conf_source =~ /^GETTEXT_PACKAGE=\[?([^\n\]]+)/m; 1093 | 1094 | # m4 macros AC_PACKAGE_NAME, AC_PACKAGE_VERSION etc. have same value 1095 | # as corresponding $PACKAGE_NAME, $PACKAGE_VERSION etc. shell variables. 1096 | $name =~ s/\bAC_PACKAGE_/\$PACKAGE_/g; 1097 | 1098 | $name = $domain if $domain; 1099 | 1100 | $name = SubstituteVariable ($name); 1101 | $name =~ s/^["'](.*)["']$/$1/; 1102 | 1103 | return $name if $name; 1104 | } 1105 | 1106 | 1107 | sub FindPOTKeywords 1108 | { 1109 | 1110 | my $keywords = "--keyword\=\_ --keyword\=N\_ --keyword\=U\_ --keyword\=Q\_"; 1111 | my $varname = "XGETTEXT_OPTIONS"; 1112 | my $make_source; { 1113 | local (*IN); 1114 | open (IN, "; 1118 | close IN; 1119 | } 1120 | 1121 | $keywords = $1 if $make_source =~ /^$varname[ ]*=\[?([^\n\]]+)/m; 1122 | 1123 | return $keywords; 1124 | } 1125 | 1126 | sub FindMakevarsDomain 1127 | { 1128 | 1129 | my $domain = ""; 1130 | my $makevars_source; { 1131 | local (*IN); 1132 | open (IN, "; 1136 | close IN; 1137 | } 1138 | 1139 | $domain = $1 if $makevars_source =~ /^DOMAIN[ ]*=\[?([^\n\]\$]+)/m; 1140 | $domain =~ s/^\s+//; 1141 | $domain =~ s/\s+$//; 1142 | 1143 | return $domain; 1144 | } 1145 | 1146 | sub FindMakevarsBugAddress 1147 | { 1148 | 1149 | my $address = ""; 1150 | my $makevars_source; { 1151 | local (*IN); 1152 | open (IN, "; 1156 | close IN; 1157 | } 1158 | 1159 | $address = $1 if $makevars_source =~ /^MSGID_BUGS_ADDRESS[ ]*=\[?([^\n\]\$]+)/m; 1160 | $address =~ s/^\s+//; 1161 | $address =~ s/\s+$//; 1162 | 1163 | return $address; 1164 | } 1165 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | INCLUDES = -I$(top_srcdir) 2 | 3 | AM_CPPFLAGS = \ 4 | -DPKGDATADIR=\"$(pkgdatadir)\" \ 5 | -DDATADIR=\""$(datadir)"\" -D_GNU_SOURCE 6 | 7 | AM_CFLAGS = -Wall -pedantic -std=c99 8 | #@DEPENDENCIES_CFLAGS@ 9 | 10 | # add the name of your application 11 | bin_PROGRAMS = bootchart-lite 12 | 13 | # add the sources to compile for your application 14 | bootchart_lite_SOURCES = \ 15 | bootchart-lite.c 16 | 17 | #fbootchart_LDADD = @DEPENDENCIES_LIBS@ 18 | 19 | #MAINTAINERCLEANFILES = config.h.in Makefile.in 20 | 21 | -------------------------------------------------------------------------------- /src/bootchart-lite.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2008 Fred Chien 3 | * 4 | * This program is free software; you can redistribute it and/or modify it under 5 | * the terms of the GNU General Public License as published by the Free Software 6 | * Foundation; either version 2 of the License, or (at your option) any later 7 | * version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 11 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 12 | * details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, write to the Free Software Foundation, Inc., 51 16 | * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #define LOGTMP "/etc/bootchart-lite" 28 | #define PROC "/proc" 29 | #define PROC_UPTIME "/proc/uptime" 30 | #define PROC_STAT "/proc/stat" 31 | #define PROC_DISKSTATS "/proc/diskstats" 32 | #define PROC_CMDLINE "/proc/cmdline" 33 | #define PROC_VERSION "/proc/version" 34 | #define BUFSIZE 1024 35 | #define EXIT_PROC "quicklauncher" 36 | 37 | static char exit_proc[] = { "(" EXIT_PROC ")" }; 38 | static char buffer[BUFSIZE]; 39 | static char uptime[10]; 40 | static int uptime_count; 41 | /* proc fd */ 42 | static int p_uptime_fd = 0; 43 | static int p_stat_fd = 0; 44 | static int p_diskstats_fd = 0; 45 | /* log fd */ 46 | static int header_fd; 47 | static int stat_fd; 48 | static int diskstats_fd; 49 | static int ps_fd; 50 | static int running = 1; 51 | static int exit_proc_count = sizeof(exit_proc) - 1; 52 | static int check_maxlen = sizeof(exit_proc) + 5; 53 | 54 | void USR1(int i) { 55 | running = 0; 56 | signal(SIGUSR1, USR1); 57 | } 58 | 59 | int 60 | check_exitproc(const char *source) 61 | { 62 | int i; 63 | int j; 64 | 65 | for (i=0;i0) { 140 | buffer[count] = '\0'; 141 | write(fd_dest, buffer, count); 142 | } 143 | 144 | return 0; 145 | } 146 | 147 | void 148 | fetch_data_ps(int fd_dest) 149 | { 150 | DIR *dir; 151 | struct dirent *file; 152 | int fd_src; 153 | int count; 154 | char path[17]; 155 | 156 | /* write uptime */ 157 | write(fd_dest, uptime, uptime_count); 158 | write(fd_dest, "\n", 1); 159 | 160 | /* open directory */ 161 | if ((dir=opendir(PROC))==NULL) 162 | exit(1); 163 | 164 | sprintf(path, PROC "/self/stat"); 165 | fd_src = open(path, O_RDONLY); 166 | while((count=read(fd_src, buffer, BUFSIZE-1))>0) { 167 | buffer[count] = '\0'; 168 | write(fd_dest, buffer, count); 169 | } 170 | 171 | close(fd_src); 172 | 173 | while((file=readdir(dir))) { 174 | if (*file->d_name>'9'||*file->d_name<'1') 175 | continue; 176 | 177 | sprintf(path, PROC "/%s/stat", file->d_name); 178 | 179 | if ((fd_src=open(path, O_RDONLY))==-1) 180 | continue; 181 | 182 | while((count=read(fd_src, buffer, BUFSIZE-1))>0) { 183 | buffer[count] = '\0'; 184 | 185 | running = running ? check_exitproc(&buffer) : 0; 186 | 187 | write(fd_dest, buffer, count); 188 | } 189 | 190 | close(fd_src); 191 | } 192 | 193 | write(fd_dest, "\n", 1); 194 | 195 | closedir(dir); 196 | } 197 | 198 | int 199 | main(int argc, char **argv) 200 | { 201 | pid_t pid; 202 | 203 | /* Run daemon in the background */ 204 | pid = fork(); 205 | if (pid>0) { 206 | /* execute init */ 207 | if (argc>1) { 208 | char *new_argv[] = { "/sbin/init", argv[1], (char *)0 }; 209 | execv("/sbin/init", new_argv); 210 | } else { 211 | char *new_argv[] = { "/sbin/init", (char *)0 }; 212 | execv("/sbin/init", new_argv); 213 | } 214 | 215 | return 0; 216 | } 217 | 218 | /* signal handler */ 219 | signal(SIGUSR1, USR1); 220 | 221 | while(!get_uptime()) { 222 | /* 0.1 second */ 223 | usleep(100000); 224 | } 225 | 226 | /* open destination file first */ 227 | if ((stat_fd=open(LOGTMP "/proc_stat.log", O_WRONLY | O_TRUNC | O_CREAT, 0755))==-1) { 228 | printf("fbootchart: cannot create %s\n", LOGTMP "/proc_stat.log"); 229 | exit(1); 230 | } 231 | 232 | if ((diskstats_fd=open(LOGTMP "/proc_diskstats.log", O_WRONLY | O_TRUNC | O_CREAT, 0755))==-1) { 233 | printf("fbootchart: cannot create %s\n", LOGTMP "/proc_diskstats.log"); 234 | exit(1); 235 | } 236 | 237 | if ((ps_fd=open(LOGTMP "/proc_ps.log", O_WRONLY | O_TRUNC | O_CREAT, 0755))==-1) { 238 | printf("fbootchart: cannot create %s\n", LOGTMP "/proc_ps.log"); 239 | exit(1); 240 | } 241 | 242 | while(running) { 243 | get_uptime(); 244 | fetch_data_ps(ps_fd); 245 | fetch_data_with_uptime(p_stat_fd, stat_fd, PROC_STAT); 246 | fetch_data_with_uptime(p_diskstats_fd, diskstats_fd, PROC_DISKSTATS); 247 | 248 | /* 0.1 second */ 249 | usleep(100000); 250 | } 251 | 252 | close(p_uptime_fd); 253 | close(p_stat_fd); 254 | close(p_diskstats_fd); 255 | close(ps_fd); 256 | close(diskstats_fd); 257 | close(stat_fd); 258 | 259 | /* create header file */ 260 | if ((header_fd=open(LOGTMP "/header", O_WRONLY | O_TRUNC | O_CREAT))==-1) { 261 | printf("fbootchart: cannot create %s\n", LOGTMP "/header"); 262 | exit(1); 263 | } 264 | 265 | write(header_fd, "version = 0.8\n", 14); 266 | write(header_fd, "title = Boot Chart by fbootchart\n", 33); 267 | write(header_fd, "system.uname = ", 15); 268 | fetch_data(0, header_fd, PROC_VERSION); 269 | write(header_fd, "system.release = not support yet\n", 33); 270 | write(header_fd, "system.cpu = not support yet\n", 29); 271 | write(header_fd, "system.kernel.options = ", 24); 272 | fetch_data(0, header_fd, PROC_CMDLINE); 273 | close(header_fd); 274 | 275 | return 0; 276 | } 277 | --------------------------------------------------------------------------------