├── .gitignore ├── .travis.yml ├── INSTALL ├── LICENSE ├── Makefile.am ├── README.md ├── autogen.sh ├── bser.c ├── bser.h ├── bser_parse.c ├── bser_parse.h ├── bser_private.h ├── bser_write.c ├── bser_write.h ├── configure.ac ├── proto.h ├── tests ├── Makefile.am ├── bser2json.c ├── check_bser.c ├── check_watchman.c ├── json2bser.c ├── run_tests.sh ├── t │ ├── array_array.bser │ ├── array_array.json │ ├── array_deep.bser │ ├── array_deep.json │ ├── array_empty.bser │ ├── array_empty.json │ ├── array_hemogenous.bser │ ├── array_hemogenous.json │ ├── array_int.bser │ ├── array_int.json │ ├── array_string.bser │ ├── array_string.json │ ├── compact_array.bser │ ├── compact_array.json │ ├── compact_array_nuls.bser │ ├── compact_array_nuls.json │ ├── false.bser │ ├── false.json │ ├── int_huge.bser │ ├── int_huge.json │ ├── int_large.bser │ ├── int_large.json │ ├── int_neg.bser │ ├── int_neg.json │ ├── int_short.bser │ ├── int_short.json │ ├── int_short_neg.bser │ ├── int_short_neg.json │ ├── int_small.bser │ ├── int_small.json │ ├── int_small_signbit.bser │ ├── int_small_signbit.json │ ├── null.bser │ ├── null.json │ ├── object.bser │ ├── object.json │ ├── object_big.bser │ ├── object_big.json │ ├── object_empty.bser │ ├── object_empty.json │ ├── real.bser │ ├── real.json │ ├── string_medium.bser │ ├── string_medium.json │ ├── string_small.bser │ ├── string_small.json │ ├── true.bser │ └── true.json ├── testBserEncoding.sh └── testBserParsing.sh ├── watchman.c └── watchman.h /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.a 3 | *.dylib 4 | *.la 5 | *.lo 6 | *.so 7 | *.o 8 | Makefile 9 | Makefile.in 10 | aclocal.m4 11 | autom4te.cache/ 12 | compile 13 | config.h 14 | config.h.in 15 | config.guess 16 | config.sub 17 | config.log 18 | config.status 19 | configure 20 | configure.lineno 21 | cscope.out 22 | depcomp 23 | install-sh 24 | libtool 25 | ltmain.sh 26 | missing 27 | m4/ 28 | stamp-h1 29 | .deps/ 30 | .libs/ 31 | .dirstamp 32 | tests/check_watchman 33 | tests/check_watchman.log 34 | tests/check_watchman.trs 35 | tests/check_bser 36 | tests/check_bser.log 37 | tests/check_bser.trs 38 | tests/test-suite.log 39 | test-driver 40 | tests/json2bser 41 | tests/bser2json 42 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | before_install: 2 | - sudo apt-get update -qq 3 | - sudo apt-get install -qq libjansson-dev check automake autoconf libtool 4 | - git clone https://github.com/facebook/watchman.git 5 | - cd watchman 6 | - ./autogen.sh && ./configure && make && sudo make install 7 | - cd .. 8 | - watchman get-sockname 9 | language: c 10 | script: ./autogen.sh && ./configure && make && make check 11 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | Installation Instructions 2 | ************************* 3 | 4 | Copyright (C) 1994-1996, 1999-2002, 2004-2011 Free Software Foundation, 5 | Inc. 6 | 7 | Copying and distribution of this file, with or without modification, 8 | are permitted in any medium without royalty provided the copyright 9 | notice and this notice are preserved. This file is offered as-is, 10 | without warranty of any kind. 11 | 12 | Basic Installation 13 | ================== 14 | 15 | To start, run libtoolize && aclocal && autoconf && autoheader && automake --add-missing. Then follow the following instructions. 16 | 17 | Briefly, the shell commands `./configure; make; make install' should 18 | configure, build, and install this package. The following 19 | more-detailed instructions are generic; see the `README' file for 20 | instructions specific to this package. Some packages provide this 21 | `INSTALL' file but do not implement all of the features documented 22 | below. The lack of an optional feature in a given package is not 23 | necessarily a bug. More recommendations for GNU packages can be found 24 | in *note Makefile Conventions: (standards)Makefile Conventions. 25 | 26 | The `configure' shell script attempts to guess correct values for 27 | various system-dependent variables used during compilation. It uses 28 | those values to create a `Makefile' in each directory of the package. 29 | It may also create one or more `.h' files containing system-dependent 30 | definitions. Finally, it creates a shell script `config.status' that 31 | you can run in the future to recreate the current configuration, and a 32 | file `config.log' containing compiler output (useful mainly for 33 | debugging `configure'). 34 | 35 | It can also use an optional file (typically called `config.cache' 36 | and enabled with `--cache-file=config.cache' or simply `-C') that saves 37 | the results of its tests to speed up reconfiguring. Caching is 38 | disabled by default to prevent problems with accidental use of stale 39 | cache files. 40 | 41 | If you need to do unusual things to compile the package, please try 42 | to figure out how `configure' could check whether to do them, and mail 43 | diffs or instructions to the address given in the `README' so they can 44 | be considered for the next release. If you are using the cache, and at 45 | some point `config.cache' contains results you don't want to keep, you 46 | may remove or edit it. 47 | 48 | The file `configure.ac' (or `configure.in') is used to create 49 | `configure' by a program called `autoconf'. You need `configure.ac' if 50 | you want to change it or regenerate `configure' using a newer version 51 | of `autoconf'. 52 | 53 | The simplest way to compile this package is: 54 | 55 | 1. `cd' to the directory containing the package's source code and type 56 | `./configure' to configure the package for your system. 57 | 58 | Running `configure' might take a while. While running, it prints 59 | some messages telling which features it is checking for. 60 | 61 | 2. Type `make' to compile the package. 62 | 63 | 3. Optionally, type `make check' to run any self-tests that come with 64 | the package, generally using the just-built uninstalled binaries. 65 | 66 | 4. Type `make install' to install the programs and any data files and 67 | documentation. When installing into a prefix owned by root, it is 68 | recommended that the package be configured and built as a regular 69 | user, and only the `make install' phase executed with root 70 | privileges. 71 | 72 | 5. Optionally, type `make installcheck' to repeat any self-tests, but 73 | this time using the binaries in their final installed location. 74 | This target does not install anything. Running this target as a 75 | regular user, particularly if the prior `make install' required 76 | root privileges, verifies that the installation completed 77 | correctly. 78 | 79 | 6. You can remove the program binaries and object files from the 80 | source code directory by typing `make clean'. To also remove the 81 | files that `configure' created (so you can compile the package for 82 | a different kind of computer), type `make distclean'. There is 83 | also a `make maintainer-clean' target, but that is intended mainly 84 | for the package's developers. If you use it, you may have to get 85 | all sorts of other programs in order to regenerate files that came 86 | with the distribution. 87 | 88 | 7. Often, you can also type `make uninstall' to remove the installed 89 | files again. In practice, not all packages have tested that 90 | uninstallation works correctly, even though it is required by the 91 | GNU Coding Standards. 92 | 93 | 8. Some packages, particularly those that use Automake, provide `make 94 | distcheck', which can by used by developers to test that all other 95 | targets like `make install' and `make uninstall' work correctly. 96 | This target is generally not run by end users. 97 | 98 | Compilers and Options 99 | ===================== 100 | 101 | Some systems require unusual options for compilation or linking that 102 | the `configure' script does not know about. Run `./configure --help' 103 | for details on some of the pertinent environment variables. 104 | 105 | You can give `configure' initial values for configuration parameters 106 | by setting variables in the command line or in the environment. Here 107 | is an example: 108 | 109 | ./configure CC=c99 CFLAGS=-g LIBS=-lposix 110 | 111 | *Note Defining Variables::, for more details. 112 | 113 | Compiling For Multiple Architectures 114 | ==================================== 115 | 116 | You can compile the package for more than one kind of computer at the 117 | same time, by placing the object files for each architecture in their 118 | own directory. To do this, you can use GNU `make'. `cd' to the 119 | directory where you want the object files and executables to go and run 120 | the `configure' script. `configure' automatically checks for the 121 | source code in the directory that `configure' is in and in `..'. This 122 | is known as a "VPATH" build. 123 | 124 | With a non-GNU `make', it is safer to compile the package for one 125 | architecture at a time in the source code directory. After you have 126 | installed the package for one architecture, use `make distclean' before 127 | reconfiguring for another architecture. 128 | 129 | On MacOS X 10.5 and later systems, you can create libraries and 130 | executables that work on multiple system types--known as "fat" or 131 | "universal" binaries--by specifying multiple `-arch' options to the 132 | compiler but only a single `-arch' option to the preprocessor. Like 133 | this: 134 | 135 | ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ 136 | CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ 137 | CPP="gcc -E" CXXCPP="g++ -E" 138 | 139 | This is not guaranteed to produce working output in all cases, you 140 | may have to build one architecture at a time and combine the results 141 | using the `lipo' tool if you have problems. 142 | 143 | Installation Names 144 | ================== 145 | 146 | By default, `make install' installs the package's commands under 147 | `/usr/local/bin', include files under `/usr/local/include', etc. You 148 | can specify an installation prefix other than `/usr/local' by giving 149 | `configure' the option `--prefix=PREFIX', where PREFIX must be an 150 | absolute file name. 151 | 152 | You can specify separate installation prefixes for 153 | architecture-specific files and architecture-independent files. If you 154 | pass the option `--exec-prefix=PREFIX' to `configure', the package uses 155 | PREFIX as the prefix for installing programs and libraries. 156 | Documentation and other data files still use the regular prefix. 157 | 158 | In addition, if you use an unusual directory layout you can give 159 | options like `--bindir=DIR' to specify different values for particular 160 | kinds of files. Run `configure --help' for a list of the directories 161 | you can set and what kinds of files go in them. In general, the 162 | default for these options is expressed in terms of `${prefix}', so that 163 | specifying just `--prefix' will affect all of the other directory 164 | specifications that were not explicitly provided. 165 | 166 | The most portable way to affect installation locations is to pass the 167 | correct locations to `configure'; however, many packages provide one or 168 | both of the following shortcuts of passing variable assignments to the 169 | `make install' command line to change installation locations without 170 | having to reconfigure or recompile. 171 | 172 | The first method involves providing an override variable for each 173 | affected directory. For example, `make install 174 | prefix=/alternate/directory' will choose an alternate location for all 175 | directory configuration variables that were expressed in terms of 176 | `${prefix}'. Any directories that were specified during `configure', 177 | but not in terms of `${prefix}', must each be overridden at install 178 | time for the entire installation to be relocated. The approach of 179 | makefile variable overrides for each directory variable is required by 180 | the GNU Coding Standards, and ideally causes no recompilation. 181 | However, some platforms have known limitations with the semantics of 182 | shared libraries that end up requiring recompilation when using this 183 | method, particularly noticeable in packages that use GNU Libtool. 184 | 185 | The second method involves providing the `DESTDIR' variable. For 186 | example, `make install DESTDIR=/alternate/directory' will prepend 187 | `/alternate/directory' before all installation names. The approach of 188 | `DESTDIR' overrides is not required by the GNU Coding Standards, and 189 | does not work on platforms that have drive letters. On the other hand, 190 | it does better at avoiding recompilation issues, and works well even 191 | when some directory options were not specified in terms of `${prefix}' 192 | at `configure' time. 193 | 194 | Optional Features 195 | ================= 196 | 197 | If the package supports it, you can cause programs to be installed 198 | with an extra prefix or suffix on their names by giving `configure' the 199 | option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. 200 | 201 | Some packages pay attention to `--enable-FEATURE' options to 202 | `configure', where FEATURE indicates an optional part of the package. 203 | They may also pay attention to `--with-PACKAGE' options, where PACKAGE 204 | is something like `gnu-as' or `x' (for the X Window System). The 205 | `README' should mention any `--enable-' and `--with-' options that the 206 | package recognizes. 207 | 208 | For packages that use the X Window System, `configure' can usually 209 | find the X include and library files automatically, but if it doesn't, 210 | you can use the `configure' options `--x-includes=DIR' and 211 | `--x-libraries=DIR' to specify their locations. 212 | 213 | Some packages offer the ability to configure how verbose the 214 | execution of `make' will be. For these packages, running `./configure 215 | --enable-silent-rules' sets the default to minimal output, which can be 216 | overridden with `make V=1'; while running `./configure 217 | --disable-silent-rules' sets the default to verbose, which can be 218 | overridden with `make V=0'. 219 | 220 | Particular systems 221 | ================== 222 | 223 | On HP-UX, the default C compiler is not ANSI C compatible. If GNU 224 | CC is not installed, it is recommended to use the following options in 225 | order to use an ANSI C compiler: 226 | 227 | ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" 228 | 229 | and if that doesn't work, install pre-built binaries of GCC for HP-UX. 230 | 231 | HP-UX `make' updates targets which have the same time stamps as 232 | their prerequisites, which makes it generally unusable when shipped 233 | generated files such as `configure' are involved. Use GNU `make' 234 | instead. 235 | 236 | On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot 237 | parse its `' header file. The option `-nodtk' can be used as 238 | a workaround. If GNU CC is not installed, it is therefore recommended 239 | to try 240 | 241 | ./configure CC="cc" 242 | 243 | and if that doesn't work, try 244 | 245 | ./configure CC="cc -nodtk" 246 | 247 | On Solaris, don't put `/usr/ucb' early in your `PATH'. This 248 | directory contains several dysfunctional programs; working variants of 249 | these programs are available in `/usr/bin'. So, if you need `/usr/ucb' 250 | in your `PATH', put it _after_ `/usr/bin'. 251 | 252 | On Haiku, software installed for all users goes in `/boot/common', 253 | not `/usr/local'. It is recommended to use the following options: 254 | 255 | ./configure --prefix=/boot/common 256 | 257 | Specifying the System Type 258 | ========================== 259 | 260 | There may be some features `configure' cannot figure out 261 | automatically, but needs to determine by the type of machine the package 262 | will run on. Usually, assuming the package is built to be run on the 263 | _same_ architectures, `configure' can figure that out, but if it prints 264 | a message saying it cannot guess the machine type, give it the 265 | `--build=TYPE' option. TYPE can either be a short name for the system 266 | type, such as `sun4', or a canonical name which has the form: 267 | 268 | CPU-COMPANY-SYSTEM 269 | 270 | where SYSTEM can have one of these forms: 271 | 272 | OS 273 | KERNEL-OS 274 | 275 | See the file `config.sub' for the possible values of each field. If 276 | `config.sub' isn't included in this package, then this package doesn't 277 | need to know the machine type. 278 | 279 | If you are _building_ compiler tools for cross-compiling, you should 280 | use the option `--target=TYPE' to select the type of system they will 281 | produce code for. 282 | 283 | If you want to _use_ a cross compiler, that generates code for a 284 | platform different from the build platform, you should specify the 285 | "host" platform (i.e., that on which the generated programs will 286 | eventually be run) with `--host=TYPE'. 287 | 288 | Sharing Defaults 289 | ================ 290 | 291 | If you want to set default values for `configure' scripts to share, 292 | you can create a site shell script called `config.site' that gives 293 | default values for variables like `CC', `cache_file', and `prefix'. 294 | `configure' looks for `PREFIX/share/config.site' if it exists, then 295 | `PREFIX/etc/config.site' if it exists. Or, you can set the 296 | `CONFIG_SITE' environment variable to the location of the site script. 297 | A warning: not all `configure' scripts look for a site script. 298 | 299 | Defining Variables 300 | ================== 301 | 302 | Variables not defined in a site shell script can be set in the 303 | environment passed to `configure'. However, some packages may run 304 | configure again during the build, and the customized values of these 305 | variables may be lost. In order to avoid this problem, you should set 306 | them in the `configure' command line, using `VAR=value'. For example: 307 | 308 | ./configure CC=/usr/local2/bin/gcc 309 | 310 | causes the specified `gcc' to be used as the C compiler (unless it is 311 | overridden in the site shell script). 312 | 313 | Unfortunately, this technique does not work for `CONFIG_SHELL' due to 314 | an Autoconf bug. Until the bug is fixed you can use this workaround: 315 | 316 | CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash 317 | 318 | `configure' Invocation 319 | ====================== 320 | 321 | `configure' recognizes the following options to control how it 322 | operates. 323 | 324 | `--help' 325 | `-h' 326 | Print a summary of all of the options to `configure', and exit. 327 | 328 | `--help=short' 329 | `--help=recursive' 330 | Print a summary of the options unique to this package's 331 | `configure', and exit. The `short' variant lists options used 332 | only in the top level, while the `recursive' variant lists options 333 | also present in any nested packages. 334 | 335 | `--version' 336 | `-V' 337 | Print the version of Autoconf used to generate the `configure' 338 | script, and exit. 339 | 340 | `--cache-file=FILE' 341 | Enable the cache: use and save the results of the tests in FILE, 342 | traditionally `config.cache'. FILE defaults to `/dev/null' to 343 | disable caching. 344 | 345 | `--config-cache' 346 | `-C' 347 | Alias for `--cache-file=config.cache'. 348 | 349 | `--quiet' 350 | `--silent' 351 | `-q' 352 | Do not print messages saying which checks are being made. To 353 | suppress all normal output, redirect it to `/dev/null' (any error 354 | messages will still be shown). 355 | 356 | `--srcdir=DIR' 357 | Look for the package's source code in directory DIR. Usually 358 | `configure' can determine that directory automatically. 359 | 360 | `--prefix=DIR' 361 | Use DIR as the installation prefix. *note Installation Names:: 362 | for more details, including other options available for fine-tuning 363 | the installation locations. 364 | 365 | `--no-create' 366 | `-n' 367 | Run the configure checks, but stop before creating any output 368 | files. 369 | 370 | `configure' also accepts some other, not widely useful, options. Run 371 | `configure --help' for more details. 372 | 373 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Twitter, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = . tests 2 | 3 | ACLOCAL_AMFLAGS=-I m4 4 | libwatchman_la_SOURCES = watchman.c bser.c bser_parse.c bser_write.c 5 | libwatchman_la_LDFLAGS= -ljansson -version-info 1:0:0 6 | 7 | lib_LTLIBRARIES = libwatchman.la 8 | 9 | include_HEADERS = watchman.h 10 | 11 | EXTRA_DIST = LICENSE 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libwatchman [![Build Status](https://secure.travis-ci.org/twitter/libwatchman.png)](http://travis-ci.org/twitter/libwatchman) 2 | 3 | This is a C interface to watchman: https://www.github.com/facebook/watchman 4 | 5 | You'll need jansson installed in order to use it, and check installed 6 | to run the tests. 7 | 8 | Watchman is a wrapper around inotify, kevents, etc. 9 | 10 | Using libwatchman is very straightforward: establish a connection with 11 | watchman_connect, do some commands, then disconnect with 12 | watchman_connection_close. These functions handle memory 13 | management for you. 14 | 15 | To set up a watch on a directory, use watchman_watch. 16 | 17 | To make a query, first construct an expression using the 18 | watchman_*_expression functions, then use watchman_do_query. It's OK 19 | to pass in a NULL value for the query parameter. You have to free 20 | expressions yourself, using watchman_free_expression, but freeing an 21 | expression will free all of its child expressions. 22 | 23 | A word of warning: stat fields are only valid for results returned 24 | from watchman. You can choose these results by specifying flags for 25 | the query. If you do not specify flags for the query, then you will 26 | only get the default fields: name, exists, newer, size, mode 27 | 28 | Memory management: 29 | 30 | Watchman makes copies of all strings it has been given. Using the 31 | watchman_free_* functions will free these (as well as any other data 32 | that watchman has allocated). Watchman never frees anything that it 33 | hasn't created. 34 | 35 | ## License 36 | Copyright 2014-2015 Twitter, Inc and other contributors 37 | 38 | Licensed under the MIT license 39 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # a u t o g e n . s h 3 | # 4 | # Copyright (c) 2005-2009 United States Government as represented by 5 | # the U.S. Army Research Laboratory. 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions 9 | # are met: 10 | # 11 | # 1. Redistributions of source code must retain the above copyright 12 | # notice, this list of conditions and the following disclaimer. 13 | # 14 | # 2. Redistributions in binary form must reproduce the above 15 | # copyright notice, this list of conditions and the following 16 | # disclaimer in the documentation and/or other materials provided 17 | # with the distribution. 18 | # 19 | # 3. The name of the author may not be used to endorse or promote 20 | # products derived from this software without specific prior written 21 | # permission. 22 | # 23 | # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 24 | # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 25 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 | # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 27 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 29 | # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 30 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 31 | # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 32 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 33 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | # 35 | ### 36 | # 37 | # Script for automatically preparing the sources for compilation by 38 | # performing the myriad of necessary steps. The script attempts to 39 | # detect proper version support, and outputs warnings about particular 40 | # systems that have autotool peculiarities. 41 | # 42 | # Basically, if everything is set up and installed correctly, the 43 | # script will validate that minimum versions of the GNU Build System 44 | # tools are installed, account for several common configuration 45 | # issues, and then simply run autoreconf for you. 46 | # 47 | # If autoreconf fails, which can happen for many valid configurations, 48 | # this script proceeds to run manual preparation steps effectively 49 | # providing a POSIX shell script (mostly complete) reimplementation of 50 | # autoreconf. 51 | # 52 | # The AUTORECONF, AUTOCONF, AUTOMAKE, LIBTOOLIZE, ACLOCAL, AUTOHEADER 53 | # environment variables and corresponding _OPTIONS variables (e.g. 54 | # AUTORECONF_OPTIONS) may be used to override the default automatic 55 | # detection behaviors. Similarly the _VERSION variables will override 56 | # the minimum required version numbers. 57 | # 58 | # Examples: 59 | # 60 | # To obtain help on usage: 61 | # ./autogen.sh --help 62 | # 63 | # To obtain verbose output: 64 | # ./autogen.sh --verbose 65 | # 66 | # To skip autoreconf and prepare manually: 67 | # AUTORECONF=false ./autogen.sh 68 | # 69 | # To verbosely try running with an older (unsupported) autoconf: 70 | # AUTOCONF_VERSION=2.50 ./autogen.sh --verbose 71 | # 72 | # Author: 73 | # Christopher Sean Morrison 74 | # 75 | # Patches: 76 | # Sebastian Pipping 77 | # 78 | ###################################################################### 79 | 80 | # set to minimum acceptable version of autoconf 81 | if [ "x$AUTOCONF_VERSION" = "x" ] ; then 82 | AUTOCONF_VERSION=2.52 83 | fi 84 | # set to minimum acceptable version of automake 85 | if [ "x$AUTOMAKE_VERSION" = "x" ] ; then 86 | AUTOMAKE_VERSION=1.6.0 87 | fi 88 | # set to minimum acceptable version of libtool 89 | if [ "x$LIBTOOL_VERSION" = "x" ] ; then 90 | LIBTOOL_VERSION=1.4.2 91 | fi 92 | 93 | 94 | ################## 95 | # ident function # 96 | ################## 97 | ident ( ) { 98 | # extract copyright from header 99 | __copyright="`grep Copyright $AUTOGEN_SH | head -${HEAD_N}1 | awk '{print $4}'`" 100 | if [ "x$__copyright" = "x" ] ; then 101 | __copyright="`date +%Y`" 102 | fi 103 | 104 | # extract version from CVS Id string 105 | __id="$Id: autogen.sh 33925 2009-03-01 23:27:06Z brlcad $" 106 | __version="`echo $__id | sed 's/.*\([0-9][0-9][0-9][0-9]\)[-\/]\([0-9][0-9]\)[-\/]\([0-9][0-9]\).*/\1\2\3/'`" 107 | if [ "x$__version" = "x" ] ; then 108 | __version="" 109 | fi 110 | 111 | echo "autogen.sh build preparation script by Christopher Sean Morrison" 112 | echo " + config.guess download patch by Sebastian Pipping (2008-12-03)" 113 | echo "revised 3-clause BSD-style license, copyright (c) $__copyright" 114 | echo "script version $__version, ISO/IEC 9945 POSIX shell script" 115 | } 116 | 117 | 118 | ################## 119 | # USAGE FUNCTION # 120 | ################## 121 | usage ( ) { 122 | echo "Usage: $AUTOGEN_SH [-h|--help] [-v|--verbose] [-q|--quiet] [-d|--download] [--version]" 123 | echo " --help Help on $NAME_OF_AUTOGEN usage" 124 | echo " --verbose Verbose progress output" 125 | echo " --quiet Quiet suppressed progress output" 126 | echo " --download Download the latest config.guess from gnulib" 127 | echo " --version Only perform GNU Build System version checks" 128 | echo 129 | echo "Description: This script will validate that minimum versions of the" 130 | echo "GNU Build System tools are installed and then run autoreconf for you." 131 | echo "Should autoreconf fail, manual preparation steps will be run" 132 | echo "potentially accounting for several common preparation issues. The" 133 | 134 | echo "AUTORECONF, AUTOCONF, AUTOMAKE, LIBTOOLIZE, ACLOCAL, AUTOHEADER," 135 | echo "PROJECT, & CONFIGURE environment variables and corresponding _OPTIONS" 136 | echo "variables (e.g. AUTORECONF_OPTIONS) may be used to override the" 137 | echo "default automatic detection behavior." 138 | echo 139 | 140 | ident 141 | 142 | return 0 143 | } 144 | 145 | 146 | ########################## 147 | # VERSION_ERROR FUNCTION # 148 | ########################## 149 | version_error ( ) { 150 | if [ "x$1" = "x" ] ; then 151 | echo "INTERNAL ERROR: version_error was not provided a version" 152 | exit 1 153 | fi 154 | if [ "x$2" = "x" ] ; then 155 | echo "INTERNAL ERROR: version_error was not provided an application name" 156 | exit 1 157 | fi 158 | $ECHO 159 | $ECHO "ERROR: To prepare the ${PROJECT} build system from scratch," 160 | $ECHO " at least version $1 of $2 must be installed." 161 | $ECHO 162 | $ECHO "$NAME_OF_AUTOGEN does not need to be run on the same machine that will" 163 | $ECHO "run configure or make. Either the GNU Autotools will need to be installed" 164 | $ECHO "or upgraded on this system, or $NAME_OF_AUTOGEN must be run on the source" 165 | $ECHO "code on another system and then transferred to here. -- Cheers!" 166 | $ECHO 167 | } 168 | 169 | ########################## 170 | # VERSION_CHECK FUNCTION # 171 | ########################## 172 | version_check ( ) { 173 | if [ "x$1" = "x" ] ; then 174 | echo "INTERNAL ERROR: version_check was not provided a minimum version" 175 | exit 1 176 | fi 177 | _min="$1" 178 | if [ "x$2" = "x" ] ; then 179 | echo "INTERNAL ERROR: version check was not provided a comparison version" 180 | exit 1 181 | fi 182 | _cur="$2" 183 | 184 | # needed to handle versions like 1.10 and 1.4-p6 185 | _min="`echo ${_min}. | sed 's/[^0-9]/./g' | sed 's/\.\././g'`" 186 | _cur="`echo ${_cur}. | sed 's/[^0-9]/./g' | sed 's/\.\././g'`" 187 | 188 | _min_major="`echo $_min | cut -d. -f1`" 189 | _min_minor="`echo $_min | cut -d. -f2`" 190 | _min_patch="`echo $_min | cut -d. -f3`" 191 | 192 | _cur_major="`echo $_cur | cut -d. -f1`" 193 | _cur_minor="`echo $_cur | cut -d. -f2`" 194 | _cur_patch="`echo $_cur | cut -d. -f3`" 195 | 196 | if [ "x$_min_major" = "x" ] ; then 197 | _min_major=0 198 | fi 199 | if [ "x$_min_minor" = "x" ] ; then 200 | _min_minor=0 201 | fi 202 | if [ "x$_min_patch" = "x" ] ; then 203 | _min_patch=0 204 | fi 205 | if [ "x$_cur_minor" = "x" ] ; then 206 | _cur_major=0 207 | fi 208 | if [ "x$_cur_minor" = "x" ] ; then 209 | _cur_minor=0 210 | fi 211 | if [ "x$_cur_patch" = "x" ] ; then 212 | _cur_patch=0 213 | fi 214 | 215 | $VERBOSE_ECHO "Checking if ${_cur_major}.${_cur_minor}.${_cur_patch} is greater than ${_min_major}.${_min_minor}.${_min_patch}" 216 | 217 | if [ $_min_major -lt $_cur_major ] ; then 218 | return 0 219 | elif [ $_min_major -eq $_cur_major ] ; then 220 | if [ $_min_minor -lt $_cur_minor ] ; then 221 | return 0 222 | elif [ $_min_minor -eq $_cur_minor ] ; then 223 | if [ $_min_patch -lt $_cur_patch ] ; then 224 | return 0 225 | elif [ $_min_patch -eq $_cur_patch ] ; then 226 | return 0 227 | fi 228 | fi 229 | fi 230 | return 1 231 | } 232 | 233 | 234 | ###################################### 235 | # LOCATE_CONFIGURE_TEMPLATE FUNCTION # 236 | ###################################### 237 | locate_configure_template ( ) { 238 | _pwd="`pwd`" 239 | if test -f "./configure.ac" ; then 240 | echo "./configure.ac" 241 | elif test -f "./configure.in" ; then 242 | echo "./configure.in" 243 | elif test -f "$_pwd/configure.ac" ; then 244 | echo "$_pwd/configure.ac" 245 | elif test -f "$_pwd/configure.in" ; then 246 | echo "$_pwd/configure.in" 247 | elif test -f "$PATH_TO_AUTOGEN/configure.ac" ; then 248 | echo "$PATH_TO_AUTOGEN/configure.ac" 249 | elif test -f "$PATH_TO_AUTOGEN/configure.in" ; then 250 | echo "$PATH_TO_AUTOGEN/configure.in" 251 | fi 252 | } 253 | 254 | 255 | ################## 256 | # argument check # 257 | ################## 258 | ARGS="$*" 259 | PATH_TO_AUTOGEN="`dirname $0`" 260 | NAME_OF_AUTOGEN="`basename $0`" 261 | AUTOGEN_SH="$PATH_TO_AUTOGEN/$NAME_OF_AUTOGEN" 262 | 263 | LIBTOOL_M4="${PATH_TO_AUTOGEN}/misc/libtool.m4" 264 | 265 | if [ "x$HELP" = "x" ] ; then 266 | HELP=no 267 | fi 268 | if [ "x$QUIET" = "x" ] ; then 269 | QUIET=no 270 | fi 271 | if [ "x$VERBOSE" = "x" ] ; then 272 | VERBOSE=no 273 | fi 274 | if [ "x$VERSION_ONLY" = "x" ] ; then 275 | VERSION_ONLY=no 276 | fi 277 | if [ "x$DOWNLOAD" = "x" ] ; then 278 | DOWNLOAD=no 279 | fi 280 | if [ "x$AUTORECONF_OPTIONS" = "x" ] ; then 281 | AUTORECONF_OPTIONS="-i -f" 282 | fi 283 | if [ "x$AUTOCONF_OPTIONS" = "x" ] ; then 284 | AUTOCONF_OPTIONS="-f" 285 | fi 286 | if [ "x$AUTOMAKE_OPTIONS" = "x" ] ; then 287 | AUTOMAKE_OPTIONS="-a -c -f" 288 | fi 289 | ALT_AUTOMAKE_OPTIONS="-a -c" 290 | if [ "x$LIBTOOLIZE_OPTIONS" = "x" ] ; then 291 | LIBTOOLIZE_OPTIONS="--automake -c -f" 292 | fi 293 | ALT_LIBTOOLIZE_OPTIONS="--automake --copy --force" 294 | if [ "x$ACLOCAL_OPTIONS" = "x" ] ; then 295 | ACLOCAL_OPTIONS="" 296 | fi 297 | if [ "x$AUTOHEADER_OPTIONS" = "x" ] ; then 298 | AUTOHEADER_OPTIONS="" 299 | fi 300 | if [ "x$CONFIG_GUESS_URL" = "x" ] ; then 301 | CONFIG_GUESS_URL="https://git.savannah.gnu.org/gitweb/?p=gnulib.git;a=blob_plain;f=build-aux/config.guess;hb=HEAD" 302 | fi 303 | for arg in $ARGS ; do 304 | case "x$arg" in 305 | x--help) HELP=yes ;; 306 | x-[hH]) HELP=yes ;; 307 | x--quiet) QUIET=yes ;; 308 | x-[qQ]) QUIET=yes ;; 309 | x--verbose) VERBOSE=yes ;; 310 | x-[dD]) DOWNLOAD=yes ;; 311 | x--download) DOWNLOAD=yes ;; 312 | x-[vV]) VERBOSE=yes ;; 313 | x--version) VERSION_ONLY=yes ;; 314 | *) 315 | echo "Unknown option: $arg" 316 | echo 317 | usage 318 | exit 1 319 | ;; 320 | esac 321 | done 322 | 323 | 324 | ##################### 325 | # environment check # 326 | ##################### 327 | 328 | # sanity check before recursions potentially begin 329 | if [ ! -f "$AUTOGEN_SH" ] ; then 330 | echo "INTERNAL ERROR: $AUTOGEN_SH does not exist" 331 | if [ ! "x$0" = "x$AUTOGEN_SH" ] ; then 332 | echo "INTERNAL ERROR: dirname/basename inconsistency: $0 != $AUTOGEN_SH" 333 | fi 334 | exit 1 335 | fi 336 | 337 | # force locale setting to C so things like date output as expected 338 | LC_ALL=C 339 | 340 | # commands that this script expects 341 | for __cmd in echo head tail pwd ; do 342 | echo "test" | $__cmd > /dev/null 2>&1 343 | if [ $? != 0 ] ; then 344 | echo "INTERNAL ERROR: '${__cmd}' command is required" 345 | exit 2 346 | fi 347 | done 348 | echo "test" | grep "test" > /dev/null 2>&1 349 | if test ! x$? = x0 ; then 350 | echo "INTERNAL ERROR: grep command is required" 351 | exit 1 352 | fi 353 | echo "test" | sed "s/test/test/" > /dev/null 2>&1 354 | if test ! x$? = x0 ; then 355 | echo "INTERNAL ERROR: sed command is required" 356 | exit 1 357 | fi 358 | 359 | 360 | # determine the behavior of echo 361 | case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in 362 | *c*,-n*) ECHO_N= ECHO_C=' 363 | ' ECHO_T=' ' ;; 364 | *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; 365 | *) ECHO_N= ECHO_C='\c' ECHO_T= ;; 366 | esac 367 | 368 | # determine the behavior of head 369 | case "x`echo 'head' | head -n 1 2>&1`" in 370 | *xhead*) HEAD_N="n " ;; 371 | *) HEAD_N="" ;; 372 | esac 373 | 374 | # determine the behavior of tail 375 | case "x`echo 'tail' | tail -n 1 2>&1`" in 376 | *xtail*) TAIL_N="n " ;; 377 | *) TAIL_N="" ;; 378 | esac 379 | 380 | VERBOSE_ECHO=: 381 | ECHO=: 382 | if [ "x$QUIET" = "xyes" ] ; then 383 | if [ "x$VERBOSE" = "xyes" ] ; then 384 | echo "Verbose output quelled by quiet option. Further output disabled." 385 | fi 386 | else 387 | ECHO=echo 388 | if [ "x$VERBOSE" = "xyes" ] ; then 389 | echo "Verbose output enabled" 390 | VERBOSE_ECHO=echo 391 | fi 392 | fi 393 | 394 | 395 | # allow a recursive run to disable further recursions 396 | if [ "x$RUN_RECURSIVE" = "x" ] ; then 397 | RUN_RECURSIVE=yes 398 | fi 399 | 400 | 401 | ################################################ 402 | # check for help arg and bypass version checks # 403 | ################################################ 404 | if [ "x`echo $ARGS | sed 's/.*[hH][eE][lL][pP].*/help/'`" = "xhelp" ] ; then 405 | HELP=yes 406 | fi 407 | if [ "x$HELP" = "xyes" ] ; then 408 | usage 409 | $ECHO "---" 410 | $ECHO "Help was requested. No preparation or configuration will be performed." 411 | exit 0 412 | fi 413 | 414 | 415 | ####################### 416 | # set up signal traps # 417 | ####################### 418 | untrap_abnormal ( ) { 419 | for sig in 1 2 13 15; do 420 | trap - $sig 421 | done 422 | } 423 | 424 | # do this cleanup whenever we exit. 425 | trap ' 426 | # start from the root 427 | if test -d "$START_PATH" ; then 428 | cd "$START_PATH" 429 | fi 430 | 431 | # restore/delete backup files 432 | if test "x$PFC_INIT" = "x1" ; then 433 | recursive_restore 434 | fi 435 | ' 0 436 | 437 | # trap SIGHUP (1), SIGINT (2), SIGPIPE (13), SIGTERM (15) 438 | for sig in 1 2 13 15; do 439 | trap ' 440 | $ECHO "" 441 | $ECHO "Aborting $NAME_OF_AUTOGEN: caught signal '$sig'" 442 | 443 | # start from the root 444 | if test -d "$START_PATH" ; then 445 | cd "$START_PATH" 446 | fi 447 | 448 | # clean up on abnormal exit 449 | $VERBOSE_ECHO "rm -rf autom4te.cache" 450 | rm -rf autom4te.cache 451 | 452 | if test -f "acinclude.m4.$$.backup" ; then 453 | $VERBOSE_ECHO "cat acinclude.m4.$$.backup > acinclude.m4" 454 | chmod u+w acinclude.m4 455 | cat acinclude.m4.$$.backup > acinclude.m4 456 | 457 | $VERBOSE_ECHO "rm -f acinclude.m4.$$.backup" 458 | rm -f acinclude.m4.$$.backup 459 | fi 460 | 461 | { (exit 1); exit 1; } 462 | ' $sig 463 | done 464 | 465 | 466 | ############################# 467 | # look for a configure file # 468 | ############################# 469 | if [ "x$CONFIGURE" = "x" ] ; then 470 | CONFIGURE="`locate_configure_template`" 471 | if [ ! "x$CONFIGURE" = "x" ] ; then 472 | $VERBOSE_ECHO "Found a configure template: $CONFIGURE" 473 | fi 474 | else 475 | $ECHO "Using CONFIGURE environment variable override: $CONFIGURE" 476 | fi 477 | if [ "x$CONFIGURE" = "x" ] ; then 478 | if [ "x$VERSION_ONLY" = "xyes" ] ; then 479 | CONFIGURE=/dev/null 480 | else 481 | $ECHO 482 | $ECHO "A configure.ac or configure.in file could not be located implying" 483 | $ECHO "that the GNU Build System is at least not used in this directory. In" 484 | $ECHO "any case, there is nothing to do here without one of those files." 485 | $ECHO 486 | $ECHO "ERROR: No configure.in or configure.ac file found in `pwd`" 487 | exit 1 488 | fi 489 | fi 490 | 491 | #################### 492 | # get project name # 493 | #################### 494 | if [ "x$PROJECT" = "x" ] ; then 495 | PROJECT="`grep AC_INIT $CONFIGURE | grep -v '.*#.*AC_INIT' | tail -${TAIL_N}1 | sed 's/^[ ]*AC_INIT(\([^,)]*\).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`" 496 | if [ "x$PROJECT" = "xAC_INIT" ] ; then 497 | # projects might be using the older/deprecated arg-less AC_INIT .. look for AM_INIT_AUTOMAKE instead 498 | PROJECT="`grep AM_INIT_AUTOMAKE $CONFIGURE | grep -v '.*#.*AM_INIT_AUTOMAKE' | tail -${TAIL_N}1 | sed 's/^[ ]*AM_INIT_AUTOMAKE(\([^,)]*\).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`" 499 | fi 500 | if [ "x$PROJECT" = "xAM_INIT_AUTOMAKE" ] ; then 501 | PROJECT="project" 502 | fi 503 | if [ "x$PROJECT" = "x" ] ; then 504 | PROJECT="project" 505 | fi 506 | else 507 | $ECHO "Using PROJECT environment variable override: $PROJECT" 508 | fi 509 | $ECHO "Preparing the $PROJECT build system...please wait" 510 | $ECHO 511 | 512 | 513 | ######################## 514 | # check for autoreconf # 515 | ######################## 516 | HAVE_AUTORECONF=no 517 | if [ "x$AUTORECONF" = "x" ] ; then 518 | for AUTORECONF in autoreconf ; do 519 | $VERBOSE_ECHO "Checking autoreconf version: $AUTORECONF --version" 520 | $AUTORECONF --version > /dev/null 2>&1 521 | if [ $? = 0 ] ; then 522 | HAVE_AUTORECONF=yes 523 | break 524 | fi 525 | done 526 | else 527 | HAVE_AUTORECONF=yes 528 | $ECHO "Using AUTORECONF environment variable override: $AUTORECONF" 529 | fi 530 | 531 | 532 | ########################## 533 | # autoconf version check # 534 | ########################## 535 | _acfound=no 536 | if [ "x$AUTOCONF" = "x" ] ; then 537 | for AUTOCONF in autoconf ; do 538 | $VERBOSE_ECHO "Checking autoconf version: $AUTOCONF --version" 539 | $AUTOCONF --version > /dev/null 2>&1 540 | if [ $? = 0 ] ; then 541 | _acfound=yes 542 | break 543 | fi 544 | done 545 | else 546 | _acfound=yes 547 | $ECHO "Using AUTOCONF environment variable override: $AUTOCONF" 548 | fi 549 | 550 | _report_error=no 551 | if [ ! "x$_acfound" = "xyes" ] ; then 552 | $ECHO "ERROR: Unable to locate GNU Autoconf." 553 | _report_error=yes 554 | else 555 | _version="`$AUTOCONF --version | head -${HEAD_N}1 | sed 's/[^0-9]*\([0-9\.][0-9\.]*\)/\1/'`" 556 | if [ "x$_version" = "x" ] ; then 557 | _version="0.0.0" 558 | fi 559 | $ECHO "Found GNU Autoconf version $_version" 560 | version_check "$AUTOCONF_VERSION" "$_version" 561 | if [ $? -ne 0 ] ; then 562 | _report_error=yes 563 | fi 564 | fi 565 | if [ "x$_report_error" = "xyes" ] ; then 566 | version_error "$AUTOCONF_VERSION" "GNU Autoconf" 567 | exit 1 568 | fi 569 | 570 | 571 | ########################## 572 | # automake version check # 573 | ########################## 574 | _amfound=no 575 | if [ "x$AUTOMAKE" = "x" ] ; then 576 | for AUTOMAKE in automake ; do 577 | $VERBOSE_ECHO "Checking automake version: $AUTOMAKE --version" 578 | $AUTOMAKE --version > /dev/null 2>&1 579 | if [ $? = 0 ] ; then 580 | _amfound=yes 581 | break 582 | fi 583 | done 584 | else 585 | _amfound=yes 586 | $ECHO "Using AUTOMAKE environment variable override: $AUTOMAKE" 587 | fi 588 | 589 | 590 | _report_error=no 591 | if [ ! "x$_amfound" = "xyes" ] ; then 592 | $ECHO 593 | $ECHO "ERROR: Unable to locate GNU Automake." 594 | _report_error=yes 595 | else 596 | _version="`$AUTOMAKE --version | head -${HEAD_N}1 | sed 's/[^0-9]*\([0-9\.][0-9\.]*\)/\1/'`" 597 | if [ "x$_version" = "x" ] ; then 598 | _version="0.0.0" 599 | fi 600 | $ECHO "Found GNU Automake version $_version" 601 | version_check "$AUTOMAKE_VERSION" "$_version" 602 | if [ $? -ne 0 ] ; then 603 | _report_error=yes 604 | fi 605 | fi 606 | if [ "x$_report_error" = "xyes" ] ; then 607 | version_error "$AUTOMAKE_VERSION" "GNU Automake" 608 | exit 1 609 | fi 610 | 611 | 612 | ######################## 613 | # check for libtoolize # 614 | ######################## 615 | HAVE_LIBTOOLIZE=yes 616 | HAVE_ALT_LIBTOOLIZE=no 617 | _ltfound=no 618 | if [ "x$LIBTOOLIZE" = "x" ] ; then 619 | LIBTOOLIZE=libtoolize 620 | $VERBOSE_ECHO "Checking libtoolize version: $LIBTOOLIZE --version" 621 | $LIBTOOLIZE --version > /dev/null 2>&1 622 | if [ ! $? = 0 ] ; then 623 | HAVE_LIBTOOLIZE=no 624 | $ECHO 625 | if [ "x$HAVE_AUTORECONF" = "xno" ] ; then 626 | $ECHO "Warning: libtoolize does not appear to be available." 627 | else 628 | $ECHO "Warning: libtoolize does not appear to be available. This means that" 629 | $ECHO "the automatic build preparation via autoreconf will probably not work." 630 | $ECHO "Preparing the build by running each step individually, however, should" 631 | $ECHO "work and will be done automatically for you if autoreconf fails." 632 | fi 633 | 634 | # look for some alternates 635 | for tool in glibtoolize libtoolize15 libtoolize14 libtoolize13 ; do 636 | $VERBOSE_ECHO "Checking libtoolize alternate: $tool --version" 637 | _glibtoolize="`$tool --version > /dev/null 2>&1`" 638 | if [ $? = 0 ] ; then 639 | $VERBOSE_ECHO "Found $tool --version" 640 | _glti="`which $tool`" 641 | if [ "x$_glti" = "x" ] ; then 642 | $VERBOSE_ECHO "Cannot find $tool with which" 643 | continue; 644 | fi 645 | if test ! -f "$_glti" ; then 646 | $VERBOSE_ECHO "Cannot use $tool, $_glti is not a file" 647 | continue; 648 | fi 649 | _gltidir="`dirname $_glti`" 650 | if [ "x$_gltidir" = "x" ] ; then 651 | $VERBOSE_ECHO "Cannot find $tool path with dirname of $_glti" 652 | continue; 653 | fi 654 | if test ! -d "$_gltidir" ; then 655 | $VERBOSE_ECHO "Cannot use $tool, $_gltidir is not a directory" 656 | continue; 657 | fi 658 | HAVE_ALT_LIBTOOLIZE=yes 659 | LIBTOOLIZE="$tool" 660 | $ECHO 661 | $ECHO "Fortunately, $tool was found which means that your system may simply" 662 | $ECHO "have a non-standard or incomplete GNU Autotools install. If you have" 663 | $ECHO "sufficient system access, it may be possible to quell this warning by" 664 | $ECHO "running:" 665 | $ECHO 666 | sudo -V > /dev/null 2>&1 667 | if [ $? = 0 ] ; then 668 | $ECHO " sudo ln -s $_glti $_gltidir/libtoolize" 669 | $ECHO 670 | else 671 | $ECHO " ln -s $_glti $_gltidir/libtoolize" 672 | $ECHO 673 | $ECHO "Run that as root or with proper permissions to the $_gltidir directory" 674 | $ECHO 675 | fi 676 | _ltfound=yes 677 | break 678 | fi 679 | done 680 | else 681 | _ltfound=yes 682 | fi 683 | else 684 | _ltfound=yes 685 | $ECHO "Using LIBTOOLIZE environment variable override: $LIBTOOLIZE" 686 | fi 687 | 688 | 689 | ############################ 690 | # libtoolize version check # 691 | ############################ 692 | _report_error=no 693 | if [ ! "x$_ltfound" = "xyes" ] ; then 694 | $ECHO 695 | $ECHO "ERROR: Unable to locate GNU Libtool." 696 | _report_error=yes 697 | else 698 | _version="`$LIBTOOLIZE --version | head -${HEAD_N}1 | sed 's/[^0-9]*\([0-9\.][0-9\.]*\)/\1/'`" 699 | if [ "x$_version" = "x" ] ; then 700 | _version="0.0.0" 701 | fi 702 | $ECHO "Found GNU Libtool version $_version" 703 | version_check "$LIBTOOL_VERSION" "$_version" 704 | if [ $? -ne 0 ] ; then 705 | _report_error=yes 706 | fi 707 | fi 708 | if [ "x$_report_error" = "xyes" ] ; then 709 | version_error "$LIBTOOL_VERSION" "GNU Libtool" 710 | exit 1 711 | fi 712 | 713 | 714 | ##################### 715 | # check for aclocal # 716 | ##################### 717 | if [ "x$ACLOCAL" = "x" ] ; then 718 | for ACLOCAL in aclocal ; do 719 | $VERBOSE_ECHO "Checking aclocal version: $ACLOCAL --version" 720 | $ACLOCAL --version > /dev/null 2>&1 721 | if [ $? = 0 ] ; then 722 | break 723 | fi 724 | done 725 | else 726 | $ECHO "Using ACLOCAL environment variable override: $ACLOCAL" 727 | fi 728 | 729 | 730 | ######################## 731 | # check for autoheader # 732 | ######################## 733 | if [ "x$AUTOHEADER" = "x" ] ; then 734 | for AUTOHEADER in autoheader ; do 735 | $VERBOSE_ECHO "Checking autoheader version: $AUTOHEADER --version" 736 | $AUTOHEADER --version > /dev/null 2>&1 737 | if [ $? = 0 ] ; then 738 | break 739 | fi 740 | done 741 | else 742 | $ECHO "Using AUTOHEADER environment variable override: $AUTOHEADER" 743 | fi 744 | 745 | 746 | ######################### 747 | # check if version only # 748 | ######################### 749 | $VERBOSE_ECHO "Checking whether to only output version information" 750 | if [ "x$VERSION_ONLY" = "xyes" ] ; then 751 | $ECHO 752 | ident 753 | $ECHO "---" 754 | $ECHO "Version requested. No preparation or configuration will be performed." 755 | exit 0 756 | fi 757 | 758 | 759 | ################################# 760 | # PROTECT_FROM_CLOBBER FUNCTION # 761 | ################################# 762 | protect_from_clobber ( ) { 763 | PFC_INIT=1 764 | 765 | # protect COPYING & INSTALL from overwrite by automake. the 766 | # automake force option will (inappropriately) ignore the existing 767 | # contents of a COPYING and/or INSTALL files (depending on the 768 | # version) instead of just forcing *missing* files like it does 769 | # for AUTHORS, NEWS, and README. this is broken but extremely 770 | # prevalent behavior, so we protect against it by keeping a backup 771 | # of the file that can later be restored. 772 | 773 | for file in COPYING INSTALL ; do 774 | if test -f ${file} ; then 775 | if test -f ${file}.$$.protect_from_automake.backup ; then 776 | $VERBOSE_ECHO "Already backed up ${file} in `pwd`" 777 | else 778 | $VERBOSE_ECHO "Backing up ${file} in `pwd`" 779 | $VERBOSE_ECHO "cp -p ${file} ${file}.$$.protect_from_automake.backup" 780 | cp -p ${file} ${file}.$$.protect_from_automake.backup 781 | fi 782 | fi 783 | done 784 | } 785 | 786 | 787 | ############################## 788 | # RECURSIVE_PROTECT FUNCTION # 789 | ############################## 790 | recursive_protect ( ) { 791 | 792 | # for projects using recursive configure, run the build 793 | # preparation steps for the subdirectories. this function assumes 794 | # START_PATH was set to pwd before recursion begins so that 795 | # relative paths work. 796 | 797 | # git 'r done, protect COPYING and INSTALL from being clobbered 798 | protect_from_clobber 799 | 800 | if test -d autom4te.cache ; then 801 | $VERBOSE_ECHO "Found an autom4te.cache directory, deleting it" 802 | $VERBOSE_ECHO "rm -rf autom4te.cache" 803 | rm -rf autom4te.cache 804 | fi 805 | 806 | # find configure template 807 | _configure="`locate_configure_template`" 808 | if [ "x$_configure" = "x" ] ; then 809 | return 810 | fi 811 | # $VERBOSE_ECHO "Looking for configure template found `pwd`/$_configure" 812 | 813 | # look for subdirs 814 | # $VERBOSE_ECHO "Looking for subdirs in `pwd`" 815 | _det_config_subdirs="`grep AC_CONFIG_SUBDIRS $_configure | grep -v '.*#.*AC_CONFIG_SUBDIRS' | sed 's/^[ ]*AC_CONFIG_SUBDIRS(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`" 816 | CHECK_DIRS="" 817 | for dir in $_det_config_subdirs ; do 818 | if test -d "`pwd`/$dir" ; then 819 | CHECK_DIRS="$CHECK_DIRS \"`pwd`/$dir\"" 820 | fi 821 | done 822 | 823 | # process subdirs 824 | if [ ! "x$CHECK_DIRS" = "x" ] ; then 825 | $VERBOSE_ECHO "Recursively scanning the following directories:" 826 | $VERBOSE_ECHO " $CHECK_DIRS" 827 | for dir in $CHECK_DIRS ; do 828 | $VERBOSE_ECHO "Protecting files from automake in $dir" 829 | cd "$START_PATH" 830 | eval "cd $dir" 831 | 832 | # recursively git 'r done 833 | recursive_protect 834 | done 835 | fi 836 | } # end of recursive_protect 837 | 838 | 839 | ############################# 840 | # RESTORE_CLOBBERED FUNCION # 841 | ############################# 842 | restore_clobbered ( ) { 843 | 844 | # The automake (and autoreconf by extension) -f/--force-missing 845 | # option may overwrite COPYING and INSTALL even if they do exist. 846 | # Here we restore the files if necessary. 847 | 848 | spacer=no 849 | 850 | for file in COPYING INSTALL ; do 851 | if test -f ${file}.$$.protect_from_automake.backup ; then 852 | if test -f ${file} ; then 853 | # compare entire content, restore if needed 854 | if test "x`cat ${file}`" != "x`cat ${file}.$$.protect_from_automake.backup`" ; then 855 | if test "x$spacer" = "xno" ; then 856 | $VERBOSE_ECHO 857 | spacer=yes 858 | fi 859 | # restore the backup 860 | $VERBOSE_ECHO "Restoring ${file} from backup (automake -f likely clobbered it)" 861 | $VERBOSE_ECHO "rm -f ${file}" 862 | rm -f ${file} 863 | $VERBOSE_ECHO "mv ${file}.$$.protect_from_automake.backup ${file}" 864 | mv ${file}.$$.protect_from_automake.backup ${file} 865 | fi # check contents 866 | elif test -f ${file}.$$.protect_from_automake.backup ; then 867 | $VERBOSE_ECHO "mv ${file}.$$.protect_from_automake.backup ${file}" 868 | mv ${file}.$$.protect_from_automake.backup ${file} 869 | fi # -f ${file} 870 | 871 | # just in case 872 | $VERBOSE_ECHO "rm -f ${file}.$$.protect_from_automake.backup" 873 | rm -f ${file}.$$.protect_from_automake.backup 874 | fi # -f ${file}.$$.protect_from_automake.backup 875 | done 876 | 877 | CONFIGURE="`locate_configure_template`" 878 | if [ "x$CONFIGURE" = "x" ] ; then 879 | return 880 | fi 881 | 882 | _aux_dir="`grep AC_CONFIG_AUX_DIR $CONFIGURE | grep -v '.*#.*AC_CONFIG_AUX_DIR' | tail -${TAIL_N}1 | sed 's/^[ ]*AC_CONFIG_AUX_DIR(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`" 883 | if test ! -d "$_aux_dir" ; then 884 | _aux_dir=. 885 | fi 886 | 887 | for file in config.guess config.sub ltmain.sh ; do 888 | if test -f "${_aux_dir}/${file}" ; then 889 | $VERBOSE_ECHO "rm -f \"${_aux_dir}/${file}.backup\"" 890 | rm -f "${_aux_dir}/${file}.backup" 891 | fi 892 | done 893 | } # end of restore_clobbered 894 | 895 | 896 | ############################## 897 | # RECURSIVE_RESTORE FUNCTION # 898 | ############################## 899 | recursive_restore ( ) { 900 | 901 | # restore COPYING and INSTALL from backup if they were clobbered 902 | # for each directory recursively. 903 | 904 | # git 'r undone 905 | restore_clobbered 906 | 907 | # find configure template 908 | _configure="`locate_configure_template`" 909 | if [ "x$_configure" = "x" ] ; then 910 | return 911 | fi 912 | 913 | # look for subdirs 914 | _det_config_subdirs="`grep AC_CONFIG_SUBDIRS $_configure | grep -v '.*#.*AC_CONFIG_SUBDIRS' | sed 's/^[ ]*AC_CONFIG_SUBDIRS(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`" 915 | CHECK_DIRS="" 916 | for dir in $_det_config_subdirs ; do 917 | if test -d "`pwd`/$dir" ; then 918 | CHECK_DIRS="$CHECK_DIRS \"`pwd`/$dir\"" 919 | fi 920 | done 921 | 922 | # process subdirs 923 | if [ ! "x$CHECK_DIRS" = "x" ] ; then 924 | $VERBOSE_ECHO "Recursively scanning the following directories:" 925 | $VERBOSE_ECHO " $CHECK_DIRS" 926 | for dir in $CHECK_DIRS ; do 927 | $VERBOSE_ECHO "Checking files for automake damage in $dir" 928 | cd "$START_PATH" 929 | eval "cd $dir" 930 | 931 | # recursively git 'r undone 932 | recursive_restore 933 | done 934 | fi 935 | } # end of recursive_restore 936 | 937 | 938 | ####################### 939 | # INITIALIZE FUNCTION # 940 | ####################### 941 | initialize ( ) { 942 | 943 | # this routine performs a variety of directory-specific 944 | # initializations. some are sanity checks, some are preventive, 945 | # and some are necessary setup detection. 946 | # 947 | # this function sets: 948 | # CONFIGURE 949 | # SEARCH_DIRS 950 | # CONFIG_SUBDIRS 951 | 952 | ################################## 953 | # check for a configure template # 954 | ################################## 955 | CONFIGURE="`locate_configure_template`" 956 | if [ "x$CONFIGURE" = "x" ] ; then 957 | $ECHO 958 | $ECHO "A configure.ac or configure.in file could not be located implying" 959 | $ECHO "that the GNU Build System is at least not used in this directory. In" 960 | $ECHO "any case, there is nothing to do here without one of those files." 961 | $ECHO 962 | $ECHO "ERROR: No configure.in or configure.ac file found in `pwd`" 963 | exit 1 964 | fi 965 | 966 | ##################### 967 | # detect an aux dir # 968 | ##################### 969 | _aux_dir="`grep AC_CONFIG_AUX_DIR $CONFIGURE | grep -v '.*#.*AC_CONFIG_AUX_DIR' | tail -${TAIL_N}1 | sed 's/^[ ]*AC_CONFIG_AUX_DIR(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`" 970 | if test ! -d "$_aux_dir" ; then 971 | _aux_dir=. 972 | else 973 | $VERBOSE_ECHO "Detected auxillary directory: $_aux_dir" 974 | fi 975 | 976 | ################################ 977 | # detect a recursive configure # 978 | ################################ 979 | CONFIG_SUBDIRS="" 980 | _det_config_subdirs="`grep AC_CONFIG_SUBDIRS $CONFIGURE | grep -v '.*#.*AC_CONFIG_SUBDIRS' | sed 's/^[ ]*AC_CONFIG_SUBDIRS(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`" 981 | for dir in $_det_config_subdirs ; do 982 | if test -d "`pwd`/$dir" ; then 983 | $VERBOSE_ECHO "Detected recursive configure directory: `pwd`/$dir" 984 | CONFIG_SUBDIRS="$CONFIG_SUBDIRS `pwd`/$dir" 985 | fi 986 | done 987 | 988 | ########################################################### 989 | # make sure certain required files exist for GNU projects # 990 | ########################################################### 991 | _marker_found="" 992 | _marker_found_message_intro='Detected non-GNU marker "' 993 | _marker_found_message_mid='" in ' 994 | for marker in foreign cygnus ; do 995 | _marker_found_message=${_marker_found_message_intro}${marker}${_marker_found_message_mid} 996 | _marker_found="`grep 'AM_INIT_AUTOMAKE.*'${marker} $CONFIGURE`" 997 | if [ ! "x$_marker_found" = "x" ] ; then 998 | $VERBOSE_ECHO "${_marker_found_message}`basename \"$CONFIGURE\"`" 999 | break 1000 | fi 1001 | if test -f "`dirname \"$CONFIGURE\"/Makefile.am`" ; then 1002 | _marker_found="`grep 'AUTOMAKE_OPTIONS.*'${marker} Makefile.am`" 1003 | if [ ! "x$_marker_found" = "x" ] ; then 1004 | $VERBOSE_ECHO "${_marker_found_message}Makefile.am" 1005 | break 1006 | fi 1007 | fi 1008 | done 1009 | if [ "x${_marker_found}" = "x" ] ; then 1010 | _suggest_foreign=no 1011 | for file in AUTHORS COPYING ChangeLog INSTALL NEWS README ; do 1012 | if [ ! -f $file ] ; then 1013 | $VERBOSE_ECHO "Touching ${file} since it does not exist" 1014 | _suggest_foreign=yes 1015 | touch $file 1016 | fi 1017 | done 1018 | 1019 | if [ "x${_suggest_foreign}" = "xyes" ] ; then 1020 | $ECHO 1021 | $ECHO "Warning: Several files expected of projects that conform to the GNU" 1022 | $ECHO "coding standards were not found. The files were automatically added" 1023 | $ECHO "for you since you do not have a 'foreign' declaration specified." 1024 | $ECHO 1025 | $ECHO "Considered adding 'foreign' to AM_INIT_AUTOMAKE in `basename \"$CONFIGURE\"`" 1026 | if test -f "`dirname \"$CONFIGURE\"/Makefile.am`" ; then 1027 | $ECHO "or to AUTOMAKE_OPTIONS in your top-level Makefile.am file." 1028 | fi 1029 | $ECHO 1030 | fi 1031 | fi 1032 | 1033 | ################################################## 1034 | # make sure certain generated files do not exist # 1035 | ################################################## 1036 | for file in config.guess config.sub ltmain.sh ; do 1037 | if test -f "${_aux_dir}/${file}" ; then 1038 | $VERBOSE_ECHO "mv -f \"${_aux_dir}/${file}\" \"${_aux_dir}/${file}.backup\"" 1039 | mv -f "${_aux_dir}/${file}" "${_aux_dir}/${file}.backup" 1040 | fi 1041 | done 1042 | 1043 | ############################ 1044 | # search alternate m4 dirs # 1045 | ############################ 1046 | SEARCH_DIRS="" 1047 | for dir in m4 ; do 1048 | if [ -d $dir ] ; then 1049 | $VERBOSE_ECHO "Found extra aclocal search directory: $dir" 1050 | SEARCH_DIRS="$SEARCH_DIRS -I $dir" 1051 | fi 1052 | done 1053 | 1054 | ###################################### 1055 | # remove any previous build products # 1056 | ###################################### 1057 | if test -d autom4te.cache ; then 1058 | $VERBOSE_ECHO "Found an autom4te.cache directory, deleting it" 1059 | $VERBOSE_ECHO "rm -rf autom4te.cache" 1060 | rm -rf autom4te.cache 1061 | fi 1062 | # tcl/tk (and probably others) have a customized aclocal.m4, so can't delete it 1063 | # if test -f aclocal.m4 ; then 1064 | # $VERBOSE_ECHO "Found an aclocal.m4 file, deleting it" 1065 | # $VERBOSE_ECHO "rm -f aclocal.m4" 1066 | # rm -f aclocal.m4 1067 | # fi 1068 | 1069 | } # end of initialize() 1070 | 1071 | 1072 | ############## 1073 | # initialize # 1074 | ############## 1075 | 1076 | # stash path 1077 | START_PATH="`pwd`" 1078 | 1079 | # Before running autoreconf or manual steps, some prep detection work 1080 | # is necessary or useful. Only needs to occur once per directory, but 1081 | # does need to traverse the entire subconfigure hierarchy to protect 1082 | # files from being clobbered even by autoreconf. 1083 | recursive_protect 1084 | 1085 | # start from where we started 1086 | cd "$START_PATH" 1087 | 1088 | # get ready to process 1089 | initialize 1090 | 1091 | 1092 | ######################################### 1093 | # DOWNLOAD_GNULIB_CONFIG_GUESS FUNCTION # 1094 | ######################################### 1095 | 1096 | # TODO - should make sure wget/curl exist and/or work before trying to 1097 | # use them. 1098 | 1099 | download_gnulib_config_guess () { 1100 | # abuse gitweb to download gnulib's latest config.guess via HTTP 1101 | config_guess_temp="config.guess.$$.download" 1102 | ret=1 1103 | for __cmd in wget curl fetch ; do 1104 | $VERBOSE_ECHO "Checking for command ${__cmd}" 1105 | ${__cmd} --version > /dev/null 2>&1 1106 | ret=$? 1107 | if [ ! $ret = 0 ] ; then 1108 | continue 1109 | fi 1110 | 1111 | __cmd_version=`${__cmd} --version | head -n 1 | sed -e 's/^[^0-9]\+//' -e 's/ .*//'` 1112 | $VERBOSE_ECHO "Found ${__cmd} ${__cmd_version}" 1113 | 1114 | opts="" 1115 | case ${__cmd} in 1116 | wget) 1117 | opts="-O" 1118 | ;; 1119 | curl) 1120 | opts="-o" 1121 | ;; 1122 | fetch) 1123 | opts="-t 5 -f" 1124 | ;; 1125 | esac 1126 | 1127 | $VERBOSE_ECHO "Running $__cmd \"${CONFIG_GUESS_URL}\" $opts \"${config_guess_temp}\"" 1128 | eval "$__cmd \"${CONFIG_GUESS_URL}\" $opts \"${config_guess_temp}\"" > /dev/null 2>&1 1129 | if [ $? = 0 ] ; then 1130 | mv -f "${config_guess_temp}" ${_aux_dir}/config.guess 1131 | ret=0 1132 | break 1133 | fi 1134 | done 1135 | 1136 | if [ ! $ret = 0 ] ; then 1137 | $ECHO "Warning: config.guess download failed from: $CONFIG_GUESS_URL" 1138 | rm -f "${config_guess_temp}" 1139 | fi 1140 | } 1141 | 1142 | 1143 | ############################## 1144 | # LIBTOOLIZE_NEEDED FUNCTION # 1145 | ############################## 1146 | libtoolize_needed () { 1147 | ret=1 # means no, don't need libtoolize 1148 | for feature in AC_PROG_LIBTOOL AM_PROG_LIBTOOL LT_INIT ; do 1149 | $VERBOSE_ECHO "Searching for $feature in $CONFIGURE" 1150 | found="`grep \"^$feature.*\" $CONFIGURE`" 1151 | if [ ! "x$found" = "x" ] ; then 1152 | ret=0 # means yes, need to run libtoolize 1153 | break 1154 | fi 1155 | done 1156 | return ${ret} 1157 | } 1158 | 1159 | 1160 | 1161 | ############################################ 1162 | # prepare build via autoreconf or manually # 1163 | ############################################ 1164 | reconfigure_manually=no 1165 | if [ "x$HAVE_AUTORECONF" = "xyes" ] ; then 1166 | $ECHO 1167 | $ECHO $ECHO_N "Automatically preparing build ... $ECHO_C" 1168 | 1169 | $VERBOSE_ECHO "$AUTORECONF $SEARCH_DIRS $AUTORECONF_OPTIONS" 1170 | autoreconf_output="`$AUTORECONF $SEARCH_DIRS $AUTORECONF_OPTIONS 2>&1`" 1171 | ret=$? 1172 | $VERBOSE_ECHO "$autoreconf_output" 1173 | 1174 | if [ ! $ret = 0 ] ; then 1175 | if [ "x$HAVE_ALT_LIBTOOLIZE" = "xyes" ] ; then 1176 | if [ ! "x`echo \"$autoreconf_output\" | grep libtoolize | grep \"No such file or directory\"`" = "x" ] ; then 1177 | $ECHO 1178 | $ECHO "Warning: autoreconf failed but due to what is usually a common libtool" 1179 | $ECHO "misconfiguration issue. This problem is encountered on systems that" 1180 | $ECHO "have installed libtoolize under a different name without providing a" 1181 | $ECHO "symbolic link or without setting the LIBTOOLIZE environment variable." 1182 | $ECHO 1183 | $ECHO "Restarting the preparation steps with LIBTOOLIZE set to $LIBTOOLIZE" 1184 | 1185 | export LIBTOOLIZE 1186 | RUN_RECURSIVE=no 1187 | export RUN_RECURSIVE 1188 | untrap_abnormal 1189 | 1190 | $VERBOSE_ECHO sh $AUTOGEN_SH "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9" 1191 | sh "$AUTOGEN_SH" "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9" 1192 | exit $? 1193 | fi 1194 | fi 1195 | 1196 | $ECHO "Warning: $AUTORECONF failed" 1197 | 1198 | if test -f ltmain.sh ; then 1199 | $ECHO "libtoolize being run by autoreconf is not creating ltmain.sh in the auxillary directory like it should" 1200 | fi 1201 | 1202 | $ECHO "Attempting to run the preparation steps individually" 1203 | reconfigure_manually=yes 1204 | else 1205 | if [ "x$DOWNLOAD" = "xyes" ] ; then 1206 | if libtoolize_needed ; then 1207 | download_gnulib_config_guess 1208 | fi 1209 | fi 1210 | fi 1211 | else 1212 | reconfigure_manually=yes 1213 | fi 1214 | 1215 | 1216 | ############################ 1217 | # LIBTOOL_FAILURE FUNCTION # 1218 | ############################ 1219 | libtool_failure ( ) { 1220 | 1221 | # libtool is rather error-prone in comparison to the other 1222 | # autotools and this routine attempts to compensate for some 1223 | # common failures. the output after a libtoolize failure is 1224 | # parsed for an error related to AC_PROG_LIBTOOL and if found, we 1225 | # attempt to inject a project-provided libtool.m4 file. 1226 | 1227 | _autoconf_output="$1" 1228 | 1229 | if [ "x$RUN_RECURSIVE" = "xno" ] ; then 1230 | # we already tried the libtool.m4, don't try again 1231 | return 1 1232 | fi 1233 | 1234 | if test -f "$LIBTOOL_M4" ; then 1235 | found_libtool="`$ECHO $_autoconf_output | grep AC_PROG_LIBTOOL`" 1236 | if test ! "x$found_libtool" = "x" ; then 1237 | if test -f acinclude.m4 ; then 1238 | rm -f acinclude.m4.$$.backup 1239 | $VERBOSE_ECHO "cat acinclude.m4 > acinclude.m4.$$.backup" 1240 | cat acinclude.m4 > acinclude.m4.$$.backup 1241 | fi 1242 | $VERBOSE_ECHO "cat \"$LIBTOOL_M4\" >> acinclude.m4" 1243 | chmod u+w acinclude.m4 1244 | cat "$LIBTOOL_M4" >> acinclude.m4 1245 | 1246 | # don't keep doing this 1247 | RUN_RECURSIVE=no 1248 | export RUN_RECURSIVE 1249 | untrap_abnormal 1250 | 1251 | $ECHO 1252 | $ECHO "Restarting the preparation steps with libtool macros in acinclude.m4" 1253 | $VERBOSE_ECHO sh $AUTOGEN_SH "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9" 1254 | sh "$AUTOGEN_SH" "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9" 1255 | exit $? 1256 | fi 1257 | fi 1258 | } 1259 | 1260 | 1261 | ########################### 1262 | # MANUAL_AUTOGEN FUNCTION # 1263 | ########################### 1264 | manual_autogen ( ) { 1265 | 1266 | ################################################## 1267 | # Manual preparation steps taken are as follows: # 1268 | # aclocal [-I m4] # 1269 | # libtoolize --automake -c -f # 1270 | # aclocal [-I m4] # 1271 | # autoconf -f # 1272 | # autoheader # 1273 | # automake -a -c -f # 1274 | ################################################## 1275 | 1276 | ########### 1277 | # aclocal # 1278 | ########### 1279 | $VERBOSE_ECHO "$ACLOCAL $SEARCH_DIRS $ACLOCAL_OPTIONS" 1280 | aclocal_output="`$ACLOCAL $SEARCH_DIRS $ACLOCAL_OPTIONS 2>&1`" 1281 | ret=$? 1282 | $VERBOSE_ECHO "$aclocal_output" 1283 | if [ ! $ret = 0 ] ; then $ECHO "ERROR: $ACLOCAL failed" && exit 2 ; fi 1284 | 1285 | ############## 1286 | # libtoolize # 1287 | ############## 1288 | if libtoolize_needed ; then 1289 | if [ "x$HAVE_LIBTOOLIZE" = "xyes" ] ; then 1290 | $VERBOSE_ECHO "$LIBTOOLIZE $LIBTOOLIZE_OPTIONS" 1291 | libtoolize_output="`$LIBTOOLIZE $LIBTOOLIZE_OPTIONS 2>&1`" 1292 | ret=$? 1293 | $VERBOSE_ECHO "$libtoolize_output" 1294 | 1295 | if [ ! $ret = 0 ] ; then $ECHO "ERROR: $LIBTOOLIZE failed" && exit 2 ; fi 1296 | else 1297 | if [ "x$HAVE_ALT_LIBTOOLIZE" = "xyes" ] ; then 1298 | $VERBOSE_ECHO "$LIBTOOLIZE $ALT_LIBTOOLIZE_OPTIONS" 1299 | libtoolize_output="`$LIBTOOLIZE $ALT_LIBTOOLIZE_OPTIONS 2>&1`" 1300 | ret=$? 1301 | $VERBOSE_ECHO "$libtoolize_output" 1302 | 1303 | if [ ! $ret = 0 ] ; then $ECHO "ERROR: $LIBTOOLIZE failed" && exit 2 ; fi 1304 | fi 1305 | fi 1306 | 1307 | ########### 1308 | # aclocal # 1309 | ########### 1310 | # re-run again as instructed by libtoolize 1311 | $VERBOSE_ECHO "$ACLOCAL $SEARCH_DIRS $ACLOCAL_OPTIONS" 1312 | aclocal_output="`$ACLOCAL $SEARCH_DIRS $ACLOCAL_OPTIONS 2>&1`" 1313 | ret=$? 1314 | $VERBOSE_ECHO "$aclocal_output" 1315 | 1316 | # libtoolize might put ltmain.sh in the wrong place 1317 | if test -f ltmain.sh ; then 1318 | if test ! -f "${_aux_dir}/ltmain.sh" ; then 1319 | $ECHO 1320 | $ECHO "Warning: $LIBTOOLIZE is creating ltmain.sh in the wrong directory" 1321 | $ECHO 1322 | $ECHO "Fortunately, the problem can be worked around by simply copying the" 1323 | $ECHO "file to the appropriate location (${_aux_dir}/). This has been done for you." 1324 | $ECHO 1325 | $VERBOSE_ECHO "cp -p ltmain.sh \"${_aux_dir}/ltmain.sh\"" 1326 | cp -p ltmain.sh "${_aux_dir}/ltmain.sh" 1327 | $ECHO $ECHO_N "Continuing build preparation ... $ECHO_C" 1328 | fi 1329 | fi # ltmain.sh 1330 | 1331 | if [ "x$DOWNLOAD" = "xyes" ] ; then 1332 | download_gnulib_config_guess 1333 | fi 1334 | fi # libtoolize_needed 1335 | 1336 | ############ 1337 | # autoconf # 1338 | ############ 1339 | $VERBOSE_ECHO 1340 | $VERBOSE_ECHO "$AUTOCONF $AUTOCONF_OPTIONS" 1341 | autoconf_output="`$AUTOCONF $AUTOCONF_OPTIONS 2>&1`" 1342 | ret=$? 1343 | $VERBOSE_ECHO "$autoconf_output" 1344 | 1345 | if [ ! $ret = 0 ] ; then 1346 | # retry without the -f and check for usage of macros that are too new 1347 | ac2_59_macros="AC_C_RESTRICT AC_INCLUDES_DEFAULT AC_LANG_ASSERT AC_LANG_WERROR AS_SET_CATFILE" 1348 | ac2_55_macros="AC_COMPILER_IFELSE AC_FUNC_MBRTOWC AC_HEADER_STDBOOL AC_LANG_CONFTEST AC_LANG_SOURCE AC_LANG_PROGRAM AC_LANG_CALL AC_LANG_FUNC_TRY_LINK AC_MSG_FAILURE AC_PREPROC_IFELSE" 1349 | ac2_54_macros="AC_C_BACKSLASH_A AC_CONFIG_LIBOBJ_DIR AC_GNU_SOURCE AC_PROG_EGREP AC_PROG_FGREP AC_REPLACE_FNMATCH AC_FUNC_FNMATCH_GNU AC_FUNC_REALLOC AC_TYPE_MBSTATE_T" 1350 | 1351 | macros_to_search="" 1352 | ac_major="`echo ${AUTOCONF_VERSION}. | cut -d. -f1 | sed 's/[^0-9]//g'`" 1353 | ac_minor="`echo ${AUTOCONF_VERSION}. | cut -d. -f2 | sed 's/[^0-9]//g'`" 1354 | 1355 | if [ $ac_major -lt 2 ] ; then 1356 | macros_to_search="$ac2_59_macros $ac2_55_macros $ac2_54_macros" 1357 | else 1358 | if [ $ac_minor -lt 54 ] ; then 1359 | macros_to_search="$ac2_59_macros $ac2_55_macros $ac2_54_macros" 1360 | elif [ $ac_minor -lt 55 ] ; then 1361 | macros_to_search="$ac2_59_macros $ac2_55_macros" 1362 | elif [ $ac_minor -lt 59 ] ; then 1363 | macros_to_search="$ac2_59_macros" 1364 | fi 1365 | fi 1366 | 1367 | configure_ac_macros=__none__ 1368 | for feature in $macros_to_search ; do 1369 | $VERBOSE_ECHO "Searching for $feature in $CONFIGURE" 1370 | found="`grep \"^$feature.*\" $CONFIGURE`" 1371 | if [ ! "x$found" = "x" ] ; then 1372 | if [ "x$configure_ac_macros" = "x__none__" ] ; then 1373 | configure_ac_macros="$feature" 1374 | else 1375 | configure_ac_macros="$feature $configure_ac_macros" 1376 | fi 1377 | fi 1378 | done 1379 | if [ ! "x$configure_ac_macros" = "x__none__" ] ; then 1380 | $ECHO 1381 | $ECHO "Warning: Unsupported macros were found in $CONFIGURE" 1382 | $ECHO 1383 | $ECHO "The `basename \"$CONFIGURE\"` file was scanned in order to determine if any" 1384 | $ECHO "unsupported macros are used that exceed the minimum version" 1385 | $ECHO "settings specified within this file. As such, the following macros" 1386 | $ECHO "should be removed from configure.ac or the version numbers in this" 1387 | $ECHO "file should be increased:" 1388 | $ECHO 1389 | $ECHO "$configure_ac_macros" 1390 | $ECHO 1391 | $ECHO $ECHO_N "Ignorantly continuing build preparation ... $ECHO_C" 1392 | fi 1393 | 1394 | ################### 1395 | # autoconf, retry # 1396 | ################### 1397 | $VERBOSE_ECHO 1398 | $VERBOSE_ECHO "$AUTOCONF" 1399 | autoconf_output="`$AUTOCONF 2>&1`" 1400 | ret=$? 1401 | $VERBOSE_ECHO "$autoconf_output" 1402 | 1403 | if [ ! $ret = 0 ] ; then 1404 | # test if libtool is busted 1405 | libtool_failure "$autoconf_output" 1406 | 1407 | # let the user know what went wrong 1408 | cat < 3 | #include 4 | #include 5 | 6 | #include "bser.h" 7 | #include "bser_private.h" 8 | 9 | int 10 | bser_is_integer(bser_t* bser) 11 | { 12 | bser_parse_if_necessary(bser); 13 | return bser->type == BSER_TAG_INT64; 14 | } 15 | 16 | int 17 | bser_is_real(bser_t* bser) 18 | { 19 | bser_parse_if_necessary(bser); 20 | return bser->type == BSER_TAG_REAL; 21 | } 22 | 23 | int 24 | bser_is_true(bser_t* bser) 25 | { 26 | bser_parse_if_necessary(bser); 27 | return bser->type == BSER_TAG_TRUE; 28 | } 29 | 30 | int 31 | bser_is_false(bser_t* bser) 32 | { 33 | bser_parse_if_necessary(bser); 34 | return bser->type == BSER_TAG_FALSE; 35 | } 36 | 37 | int 38 | bser_is_boolean(bser_t* bser) 39 | { 40 | return bser_is_true(bser) || bser_is_false(bser); 41 | } 42 | 43 | int 44 | bser_is_null(bser_t* bser) 45 | { 46 | bser_parse_if_necessary(bser); 47 | return bser->type == BSER_TAG_NULL; 48 | } 49 | 50 | int 51 | bser_is_string(bser_t* bser) 52 | { 53 | bser_parse_if_necessary(bser); 54 | return bser->type == BSER_TAG_STRING; 55 | } 56 | 57 | int 58 | bser_is_array(bser_t* bser) 59 | { 60 | bser_parse_if_necessary(bser); 61 | return bser->type == BSER_TAG_ARRAY; 62 | } 63 | 64 | int 65 | bser_is_object(bser_t* bser) 66 | { 67 | bser_parse_if_necessary(bser); 68 | return bser->type == BSER_TAG_OBJECT; 69 | } 70 | 71 | int 72 | bser_is_error(bser_t* bser) 73 | { 74 | bser_parse_if_necessary(bser); 75 | return bser->type == BSER_TAG_ERROR; 76 | } 77 | 78 | int64_t bser_integer_value(bser_t* bser) 79 | { 80 | bser_parse_if_necessary(bser); 81 | assert(bser_is_integer(bser)); 82 | return bser->value.integer; 83 | } 84 | 85 | double bser_real_value(bser_t* bser) 86 | { 87 | bser_parse_if_necessary(bser); 88 | assert(bser_is_real(bser)); 89 | return bser->value.real; 90 | } 91 | 92 | const char* bser_string_value(bser_t* bser, size_t* length_ret) 93 | { 94 | bser_parse_if_necessary(bser); 95 | assert(bser_is_string(bser)); 96 | *length_ret = bser->value.string.length; 97 | return bser->value.string.chars; 98 | } 99 | 100 | size_t bser_array_size(bser_t* bser) 101 | { 102 | bser_parse_if_necessary(bser); 103 | assert(bser_is_array(bser)); 104 | return bser->value.array.length; 105 | } 106 | 107 | size_t bser_object_size(bser_t* bser) 108 | { 109 | bser_parse_if_necessary(bser); 110 | assert(bser_is_object(bser)); 111 | return bser->value.object.length; 112 | } 113 | 114 | const char* bser_error_message(bser_t* bser) 115 | { 116 | assert(bser_is_error(bser)); 117 | return bser->value.error_message; 118 | } 119 | 120 | bser_t* 121 | bser_new_integer(int64_t value, bser_t* fill) 122 | { 123 | if (fill == NULL) { 124 | fill = bser_alloc(); 125 | } 126 | fill->type = BSER_TAG_INT64; 127 | fill->value.integer = value; 128 | return fill; 129 | } 130 | 131 | bser_t* 132 | bser_new_real(double value, bser_t* fill) 133 | { 134 | if (fill == NULL) { 135 | fill = bser_alloc(); 136 | } 137 | fill->type = BSER_TAG_REAL; 138 | fill->value.real = value; 139 | return fill; 140 | } 141 | 142 | bser_t* 143 | bser_new_true(bser_t* fill) 144 | { 145 | if (fill == NULL) { 146 | fill = bser_alloc(); 147 | } 148 | fill->type = BSER_TAG_TRUE; 149 | return fill; 150 | } 151 | 152 | bser_t* 153 | bser_new_false(bser_t* fill) 154 | { 155 | if (fill == NULL) { 156 | fill = bser_alloc(); 157 | } 158 | fill->type = BSER_TAG_FALSE; 159 | return fill; 160 | } 161 | 162 | bser_t* 163 | bser_new_null(bser_t* fill) 164 | { 165 | if (fill == NULL) { 166 | fill = bser_alloc(); 167 | } 168 | fill->type = BSER_TAG_NULL; 169 | return fill; 170 | } 171 | 172 | bser_t* 173 | bser_new_string(const char* chars, size_t len, bser_t* fill) 174 | { 175 | if (fill == NULL) { 176 | fill = bser_alloc(); 177 | } 178 | fill->type = BSER_TAG_STRING; 179 | fill->value.string.chars = chars; 180 | fill->value.string.length = len; 181 | return fill; 182 | } 183 | 184 | bser_t* 185 | bser_new_array(bser_t* elems, size_t len, bser_t* fill) 186 | { 187 | if (fill == NULL) { 188 | fill = bser_alloc(); 189 | } 190 | fill->type = BSER_TAG_ARRAY; 191 | fill->value.array.elements = elems; 192 | fill->value.array.length = len; 193 | return fill; 194 | } 195 | 196 | bser_t* 197 | bser_new_object(bser_key_value_pair_t* fields, 198 | size_t length, bser_t* fill) 199 | { 200 | if (fill == NULL) { 201 | fill = bser_alloc(); 202 | } 203 | fill->type = BSER_TAG_OBJECT; 204 | fill->value.object.fields = fields; 205 | fill->value.object.length = length; 206 | return fill; 207 | } 208 | 209 | static void 210 | bser_free_array_contents(bser_t* bser) 211 | { 212 | assert(bser_is_array(bser)); 213 | for (int i = 0; i < bser->value.array.length; ++i) { 214 | bser_free_contents(&bser->value.array.elements[i]); 215 | } 216 | free(bser->value.array.elements); 217 | } 218 | 219 | static void 220 | bser_free_object_contents(bser_t* bser) 221 | { 222 | assert(bser_is_object(bser)); 223 | for (int i = 0; i < bser->value.object.length; ++i) { 224 | bser_free_contents(&bser->value.object.fields[i].value); 225 | } 226 | free(bser->value.object.fields); 227 | } 228 | 229 | void 230 | bser_free_contents(bser_t* bser) 231 | { 232 | if (bser_is_array(bser)) { 233 | bser_free_array_contents(bser); 234 | } else if (bser_is_object(bser)) { 235 | bser_free_object_contents(bser); 236 | } 237 | bser->type = BSER_TAG_ERROR; 238 | bser->value.error_message = ""; 239 | } 240 | 241 | void 242 | bser_free(bser_t* bser) { 243 | bser_free_contents(bser); 244 | free(bser); 245 | } 246 | 247 | int 248 | bser_string_strcmp(const char* match, bser_t* bser) 249 | { 250 | int cmp; 251 | assert(bser_is_string(bser)); 252 | bser_parse_if_necessary(bser); 253 | 254 | size_t match_len = strlen(match); 255 | size_t bser_len = bser->value.string.length; 256 | const char* bser_value = bser->value.string.chars; 257 | 258 | if (match_len > bser_len) { 259 | cmp = memcmp(match, bser_value, bser_len); 260 | cmp = cmp == 0 ? (unsigned char)match[bser_len] : cmp; 261 | } else if (bser_len > match_len) { 262 | cmp = memcmp(match, bser_value, match_len); 263 | cmp = cmp == 0 ? (unsigned char)bser_value[match_len] : cmp; 264 | } else { 265 | cmp = memcmp(match, bser_value, match_len); 266 | } 267 | return cmp; 268 | } 269 | 270 | bser_t* 271 | bser_object_get(bser_t* bser, const char* key) 272 | { 273 | assert(bser_is_object(bser)); 274 | for (int i = 0; i < bser->value.object.length; ++i) { 275 | bser_key_value_pair_t* pair = bser_object_pair_at(bser, i); 276 | bser_parse_if_necessary(&pair->key); 277 | assert(bser_is_string(&pair->key)); 278 | bser_parse_if_necessary(&pair->value); 279 | if (!bser_string_strcmp(key, &pair->key)) { 280 | if (bser_is_no_field(&pair->value)) { 281 | return NULL; 282 | } else { 283 | return &pair->value; 284 | } 285 | } 286 | } 287 | return NULL; 288 | } 289 | 290 | bser_t* 291 | bser_array_get(bser_t* bser, size_t index) 292 | { 293 | bser_parse_if_necessary(bser); 294 | assert(bser_is_array(bser)); 295 | assert(index < bser->value.array.length); 296 | bser_t* element = &bser->value.array.elements[index]; 297 | 298 | /* Can't return an unparsed element unless all elements prior 299 | * have been parsed. */ 300 | bser_parse_array_elements_to(bser, index); 301 | return element; 302 | } 303 | 304 | bser_t* 305 | bser_object_key_at(bser_t* bser, size_t index) 306 | { 307 | bser_parse_if_necessary(bser); 308 | assert(bser_is_object(bser)); 309 | bser_key_value_pair_t* field = bser_object_pair_at(bser, index); 310 | if (bser_is_unparsed(&field->key)) { 311 | bser_parse_object_fields_to(bser, index); 312 | } 313 | return &field->key; 314 | } 315 | 316 | bser_t* 317 | bser_object_value_at(bser_t* bser, size_t index) 318 | { 319 | bser_parse_if_necessary(bser); 320 | assert(bser_is_object(bser)); 321 | bser_key_value_pair_t* field = bser_object_pair_at(bser, index); 322 | if (bser_is_unparsed(&field->value)) { 323 | bser_parse_object_fields_to(bser, index); 324 | bser_parse_if_necessary(&field->key); 325 | } 326 | if (bser_is_no_field(&field->value)) { 327 | return NULL; 328 | } else { 329 | return &field->value; 330 | } 331 | } 332 | 333 | static void fill_in_error(const char* msg, json_error_t* err) { 334 | memset(err, 0, sizeof(*err)); 335 | strncpy(err->text, msg, JSON_ERROR_TEXT_LENGTH - 1); 336 | } 337 | 338 | json_t* 339 | bser2json(bser_t* bser, json_error_t* err) 340 | { 341 | if (bser_is_integer(bser)) { 342 | json_int_t v = bser_integer_value(bser); 343 | return json_integer(v); 344 | } else if (bser_is_real(bser)) { 345 | double v = bser_real_value(bser); 346 | return json_real(v); 347 | } else if (bser_is_true(bser)) { 348 | return json_true(); 349 | } else if (bser_is_false(bser)) { 350 | return json_false(); 351 | } else if (bser_is_null(bser)) { 352 | return json_null(); 353 | } else if (bser_is_string(bser)) { 354 | size_t length; 355 | const char* str = bser_string_value(bser, &length); 356 | char* dup = strndup(str, length); 357 | json_t* string = json_string(dup); 358 | free(dup); 359 | return string; 360 | } else if (bser_is_array(bser)) { 361 | size_t length = bser_array_size(bser); 362 | json_t* array = json_array(); 363 | for (int i = 0; i < length; ++i) { 364 | json_t* elem = bser2json(bser_array_get(bser, i), err); 365 | if (elem == NULL) { 366 | return NULL; 367 | } 368 | json_array_append_new(array, elem); 369 | } 370 | return array; 371 | } else if (bser_is_object(bser)) { 372 | size_t length = bser_object_size(bser); 373 | json_t* object = json_object(); 374 | for (int i = 0; i < length; ++i) { 375 | size_t key_length; 376 | bser_t* key = bser_object_key_at(bser, i); 377 | assert(bser_is_string(key)); 378 | bser_t* value = bser_object_value_at(bser, i); 379 | if (!bser_is_no_field(value)) { 380 | const char* key_chars = bser_string_value(key, &key_length); 381 | assert(key_chars != NULL && *key_chars != '\0'); 382 | char* key_dup = strndup(key_chars, key_length); 383 | json_t* field_value = bser2json(value, err); 384 | if (field_value == NULL) { 385 | return NULL; 386 | } 387 | json_object_set_new(object, key_dup, field_value); 388 | free(key_dup); 389 | } 390 | } 391 | return object; 392 | } else if (bser_is_error(bser)) { 393 | fill_in_error(bser_error_message(bser), err); 394 | return NULL; 395 | } else { 396 | fill_in_error("Unknown bser node type", err); 397 | return NULL; 398 | } 399 | } 400 | -------------------------------------------------------------------------------- /bser.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBWATCHMAN_BSER_H_ 2 | #define LIBWATCHMAN_BSER_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "bser_private.h" 10 | 11 | /** 12 | * BSER is a binary protocol for transferring JSON-like data. It supports 13 | * a subset of JSON functionality in a much more compact and easily parsed 14 | * form. When watchman receives a request in BSER format, it will respond 15 | * in kind. See https://facebook.github.io/watchman/docs/bser.html for 16 | * description of the protocol. 17 | */ 18 | 19 | /* ctors - if 'fill' is NULL a new object will be allocated and returned, 20 | * otherwise, fill is initailized and returned */ 21 | bser_t* bser_new_integer(int64_t value, bser_t* fill); 22 | bser_t* bser_new_real(double value, bser_t* fill); 23 | bser_t* bser_new_true(bser_t* fill); 24 | bser_t* bser_new_false(bser_t* fill); 25 | bser_t* bser_new_null(bser_t* fill); 26 | bser_t* bser_new_string(const char* chars, size_t len, bser_t* fill); 27 | bser_t* bser_new_array(bser_t* elems, size_t len, bser_t* fill); 28 | bser_t* bser_new_object(bser_key_value_pair_t* fields, 29 | size_t length, bser_t* fill); 30 | 31 | /* Recursively frees memory allocated under 'bser', 32 | * without deallocating 'bser' itself. */ 33 | void bser_free_contents(bser_t* bser); 34 | 35 | /* Frees 'bser' and all memory allocated beneath it. */ 36 | void bser_free(bser_t* bser); 37 | 38 | /* basic type queries */ 39 | int bser_is_integer(bser_t* bser); 40 | int bser_is_real(bser_t* bser); 41 | int bser_is_true(bser_t* bser); 42 | int bser_is_false(bser_t* bser); 43 | int bser_is_boolean(bser_t* bser); 44 | int bser_is_null(bser_t* bser); 45 | int bser_is_string(bser_t* bser); 46 | int bser_is_array(bser_t* bser); 47 | int bser_is_object(bser_t* bser); 48 | /* Parse error resulted in a error node, or node has been freed */ 49 | int bser_is_error(bser_t* bser); 50 | 51 | /* value retrieval -- assumes type checking has occurred already */ 52 | int64_t bser_integer_value(bser_t* bser); 53 | 54 | double bser_real_value(bser_t* bser); 55 | 56 | const char* bser_string_value(bser_t* bser, size_t* length_ret); 57 | /* Like strcmp(), but matches a bser string with a char* */ 58 | int bser_string_strcmp(const char* match, bser_t* bser); 59 | 60 | size_t bser_array_size(bser_t* bser); 61 | bser_t* bser_array_get(bser_t* bser, size_t index); 62 | 63 | size_t bser_object_size(bser_t* bser); 64 | bser_t* bser_object_get(bser_t* bser, const char* key); 65 | bser_t* bser_object_key_at(bser_t* bser, size_t index); 66 | bser_t* bser_object_value_at(bser_t* bser, size_t index); 67 | 68 | /* Retrieve error message if the node is an error node */ 69 | const char* bser_error_message(bser_t* bser); 70 | 71 | /* Convert a BSER representation into a json (jansson) representation. 72 | * Upon an error, NULL is returned and 'err' is filled in with the error 73 | * message. */ 74 | json_t* bser2json(bser_t* bser, json_error_t* err); 75 | 76 | #endif /* ndef LIBWATCHMAN_BSER_H */ 77 | -------------------------------------------------------------------------------- /bser_parse.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "bser.h" 5 | 6 | /** 7 | * BSER parsing is done lazily, and does not copy strings out of the buffer. 8 | * This is accomplished by initializing each new object that we encounter 9 | * as an "unparsed" object, which contains only a pointer to the data buffer 10 | * (which itself has a cursor). The first time an object is queried or a 11 | * value is requested of it, it accesses the buffer and reads only the data 12 | * it needs to satisfy the request. For unit types, this usually means reading 13 | * the tag and all the data. For strings, the length is read and a pointer 14 | * to the string data in the buffer is stored in the object. 15 | * 16 | * Parsing occurs in the order that the data appears in the buffer. That is, 17 | * if an object's data appears earlier in the buffer than a different object, 18 | * then the first object is guaranteed to be parsed first. The buffer is a 19 | * stream and cannot be randomly accessed. The in-order parsing happens in 20 | * the implementation of the 'array' and 'object' types, which are the only 21 | * composite types in the system. When these are initially parsed, the read 22 | * only the header information (including the size) and allocate an array to 23 | * use for storage and this storage is initialized to all unparsed object. 24 | * However, when an element or a field is accessed, the query methods ensures 25 | * that all elements or fields before the requested field are fully-parsed 26 | * (from first to last). 27 | */ 28 | 29 | static bser_t* 30 | new_unparsed(bser_buffer_t* buffer, bser_t* fill) 31 | { 32 | if (fill == NULL) { 33 | fill = bser_alloc(); 34 | } 35 | fill->type = BSER_TAG_UNPARSED; 36 | fill->value.unparsed = buffer; 37 | return fill; 38 | } 39 | 40 | static bser_t* 41 | new_error(const char* message, bser_t* fill) 42 | { 43 | if (fill == NULL) { 44 | fill = bser_alloc(); 45 | } 46 | fill->type = BSER_TAG_ERROR; 47 | fill->value.error_message = message; 48 | return fill; 49 | } 50 | 51 | static void 52 | new_compact_object(bser_buffer_t* buffer, bser_t* header, bser_t* fill) 53 | { 54 | assert(bser_is_array(header)); 55 | size_t sz = bser_array_size(header); 56 | bser_key_value_pair_t* fields = malloc(sizeof(*fields) * sz); 57 | if (fields == NULL) { 58 | new_error("Could not allocate enough memory to hold " 59 | "compact object fields", fill); 60 | } else { 61 | for (int i = 0; i < sz; ++i) { 62 | fields[i].key = *(bser_array_get(header, i)); 63 | new_unparsed(buffer, &fields[i].value); 64 | } 65 | bser_new_object(fields, sz, fill); 66 | } 67 | } 68 | 69 | static bser_buffer_t* 70 | new_read_buffer(void* data, size_t buflen, int offset) 71 | { 72 | bser_buffer_t* buffer = malloc(sizeof(*buffer)); 73 | buffer->data = data; 74 | buffer->datalen = buflen; 75 | buffer->cursor = offset; 76 | return buffer; 77 | } 78 | 79 | bser_t* 80 | bser_parse_content(void* data, size_t buflen, bser_t* fill) 81 | { 82 | bser_buffer_t* read_buffer = new_read_buffer(data, buflen, 0); 83 | /* Create lazily-parsed node that will self-parse when it is accessed */ 84 | return new_unparsed(read_buffer, fill); 85 | } 86 | 87 | static int 88 | integer_from_file(FILE* fp, int64_t* value) 89 | { 90 | int8_t v8 = 0; 91 | int16_t v16 = 0; 92 | int32_t v32 = 0; 93 | int64_t v64 = 0; 94 | 95 | switch (fgetc(fp)) { 96 | case BSER_TAG_INT8: 97 | if (fread(&v8, sizeof(v8), 1, fp) == 1) { 98 | *value = v8; 99 | return 0; 100 | } 101 | case BSER_TAG_INT16: 102 | if (fread(&v16, sizeof(v16), 1, fp) == 1) { 103 | *value = v16; 104 | return 0; 105 | } 106 | case BSER_TAG_INT32: 107 | if (fread(&v32, sizeof(v32), 1, fp) == 1) { 108 | *value = v32; 109 | return 0; 110 | } 111 | case BSER_TAG_INT64: 112 | if (fread(&v64, sizeof(v64), 1, fp) == 1) { 113 | *value = v64; 114 | return 0; 115 | } 116 | } 117 | return -1; 118 | } 119 | 120 | bser_t* 121 | bser_parse_buffer(void* buffer, size_t buflen, bser_t* fill) 122 | { 123 | uint8_t* magic = buffer; 124 | if (buflen < 2 || magic[0] != 0 || magic[1] != 1) { 125 | return new_error("Could not read bser magic values", fill); 126 | } else { 127 | bser_t length; 128 | bser_buffer_t* read_buffer = new_read_buffer(buffer, buflen, 2); 129 | new_unparsed(read_buffer, &length); 130 | if (!bser_is_integer(&length)) { 131 | return new_error("Could not read bser length", fill); 132 | } else { 133 | size_t sz = bser_integer_value(&length); 134 | if (read_buffer->cursor + sz > buflen) { 135 | return new_error("Truncated buffer", fill); 136 | } else { 137 | return new_unparsed(read_buffer, fill); 138 | } 139 | } 140 | } 141 | } 142 | 143 | bser_t* 144 | bser_parse_from_file(FILE* fp, bser_t* fill) 145 | { 146 | void* buf; 147 | uint8_t magic[2]; 148 | int64_t size; 149 | 150 | int status = fread(magic, 1, 2, fp); 151 | if (status != 2 || magic[0] != 0 || magic[1] != 1) { 152 | return new_error("Could not read bser magic values", fill); 153 | } else if (integer_from_file(fp, &size)) { 154 | return new_error("Could not read bser length", fill); 155 | } else if (size <= 0) { 156 | return new_error("Invalid bser length", fill); 157 | } else { 158 | buf = malloc(size); 159 | if (buf == NULL) { 160 | return new_error("Could not allocate memory to hold data", fill); 161 | } else if (fread(buf, 1, size, fp) != size) { 162 | return new_error("Could not read full bser data", fill); 163 | } else { 164 | return bser_parse_content(buf, size, fill); 165 | } 166 | } 167 | } 168 | 169 | static void 170 | parse_array(bser_t* fill, bser_buffer_t* buffer) 171 | { 172 | bser_t length; 173 | new_unparsed(buffer, &length); 174 | if (bser_is_integer(&length)) { 175 | size_t sz = (size_t)bser_integer_value(&length); 176 | bser_t* array = malloc(sizeof(*array) * sz); 177 | if (array == NULL) { 178 | new_error("Could not allocate enough memory to hold " 179 | "array elements", fill); 180 | } else { 181 | for (int i = 0; i < sz; ++i) { 182 | new_unparsed(buffer, &array[i]); 183 | } 184 | bser_new_array(array, sz, fill); 185 | } 186 | } else { 187 | new_error("Array does not have an integer length", fill); 188 | } 189 | } 190 | 191 | static void 192 | parse_object(bser_t* fill, bser_buffer_t* buffer) 193 | { 194 | bser_t length; 195 | new_unparsed(buffer, &length); 196 | if (bser_is_integer(&length)) { 197 | size_t sz = (size_t)bser_integer_value(&length); 198 | bser_key_value_pair_t* array = malloc(sizeof(*array) * sz); 199 | if (array == NULL) { 200 | new_error("Could not allocate enough memory to hold " 201 | "object fields", fill); 202 | } else { 203 | for (int i = 0; i < sz; ++i) { 204 | new_unparsed(buffer, &array[i].key); 205 | new_unparsed(buffer, &array[i].value); 206 | } 207 | bser_new_object(array, sz, fill); 208 | } 209 | } else { 210 | new_error("Object does not have an integer length", fill); 211 | } 212 | } 213 | 214 | static void 215 | parse_string(bser_t* fill, bser_buffer_t* buffer) 216 | { 217 | bser_t length; 218 | new_unparsed(buffer, &length); 219 | if (bser_is_integer(&length)) { 220 | size_t sz = (size_t)bser_integer_value(&length); 221 | const char* chars = (const char*)buffer->data + buffer->cursor; 222 | buffer->cursor += sz; 223 | bser_new_string(chars, sz, fill); 224 | } else { 225 | new_error("String does not have an integer length", fill); 226 | } 227 | } 228 | 229 | static void 230 | parse_compact_array(bser_t* fill, bser_buffer_t* buffer) 231 | { 232 | bser_t header; 233 | new_unparsed(buffer, &header); 234 | if (bser_is_array(&header)) { 235 | size_t header_length = bser_array_size(&header); 236 | for (int i = 0; i < header_length; ++i) { 237 | bser_parse_if_necessary(bser_array_get(&header, i)); 238 | } 239 | bser_t length; 240 | new_unparsed(buffer, &length); 241 | if (bser_is_integer(&length)) { 242 | size_t sz = (size_t)bser_integer_value(&length); 243 | bser_t* array = malloc(sizeof(*array) * sz); 244 | if (array == NULL) { 245 | new_error("Could not allocate enough memory to hold " 246 | "compact array elements", fill); 247 | } else { 248 | bser_new_array(array, sz, fill); 249 | for (int i = 0; i < sz; ++i) { 250 | new_compact_object(buffer, &header, &array[i]); 251 | } 252 | } 253 | } else { 254 | new_error("Compact array does not have an integer length", fill); 255 | } 256 | } else { 257 | new_error("Compact array does not have a header array", fill); 258 | } 259 | bser_free_contents(&header); 260 | } 261 | 262 | void 263 | bser_parse_generic(bser_t* fill, bser_buffer_t* buffer) 264 | { 265 | if (buffer->cursor >= buffer->datalen) { 266 | fill->type = BSER_TAG_ERROR; 267 | fill->value.error_message = "out of data"; 268 | } else { 269 | uint8_t* as_int = buffer->data; 270 | uint8_t tag = as_int[buffer->cursor++]; 271 | void* data = (char *)buffer->data + buffer->cursor; 272 | 273 | switch (tag) { 274 | case BSER_TAG_ARRAY: 275 | parse_array(fill, buffer); 276 | break; 277 | case BSER_TAG_OBJECT: 278 | parse_object(fill, buffer); 279 | break; 280 | case BSER_TAG_STRING: 281 | parse_string(fill, buffer); 282 | break; 283 | case BSER_TAG_INT8: 284 | bser_new_integer(*(int8_t*)data, fill); 285 | buffer->cursor += sizeof(int8_t); 286 | break; 287 | case BSER_TAG_INT16: 288 | bser_new_integer(*(int16_t*)data, fill); 289 | buffer->cursor += sizeof(int16_t); 290 | break; 291 | case BSER_TAG_INT32: 292 | bser_new_integer(*(int32_t*)data, fill); 293 | buffer->cursor += sizeof(int32_t); 294 | break; 295 | case BSER_TAG_INT64: 296 | bser_new_integer(*(int64_t*)data, fill); 297 | buffer->cursor += sizeof(int64_t); 298 | break; 299 | case BSER_TAG_REAL: 300 | bser_new_real(*(double*)data, fill); 301 | buffer->cursor += sizeof(double); 302 | break; 303 | case BSER_TAG_TRUE: 304 | bser_new_true(fill); 305 | break; 306 | case BSER_TAG_FALSE: 307 | bser_new_false(fill); 308 | break; 309 | case BSER_TAG_NULL: 310 | bser_new_null(fill); 311 | break; 312 | case BSER_TAG_COMPACT_ARRAY: 313 | parse_compact_array(fill, buffer); 314 | break; 315 | case BSER_TAG_NO_FIELD: 316 | fill->type = BSER_TAG_NO_FIELD; 317 | break; 318 | default: 319 | new_error("unknown tag in data stream", fill); 320 | break; 321 | } 322 | } 323 | } 324 | 325 | static int 326 | is_fully_parsed(bser_t* bser) 327 | { 328 | size_t index; 329 | if (bser_is_unparsed(bser)) { 330 | return 0; 331 | } else if (bser_is_array(bser) && (index = bser_array_size(bser)) > 0) { 332 | return is_fully_parsed(&bser->value.array.elements[index - 1]); 333 | } else if (bser_is_object(bser) && (index = bser_object_size(bser)) > 0) { 334 | return is_fully_parsed(&bser->value.object.fields[index - 1].value); 335 | } 336 | return 1; 337 | } 338 | 339 | static void 340 | fully_parse(bser_t* bser) 341 | { 342 | bser_parse_if_necessary(bser); 343 | 344 | if (!is_fully_parsed(bser)) { 345 | if (bser_is_array(bser)) { 346 | for (int i = 0; i < bser->value.array.length; ++i) { 347 | fully_parse(&bser->value.array.elements[i]); 348 | } 349 | } else if (bser_is_object(bser)) { 350 | for (int i = 0; i < bser->value.object.length; ++i) { 351 | struct bser_key_value_pair* pair = 352 | &bser->value.object.fields[i]; 353 | bser_parse_if_necessary(&pair->key); 354 | fully_parse(&pair->value); 355 | } 356 | } 357 | } 358 | } 359 | 360 | void 361 | bser_parse_array_elements_to(bser_t* array, size_t limit) 362 | { 363 | assert(bser_is_array(array)); 364 | if (limit > 0) { 365 | /* Search back to find first parsed, and parse all from there */ 366 | size_t index = limit - 1; 367 | while (!is_fully_parsed(&array->value.array.elements[index]) && index > 0) { 368 | --index; 369 | } 370 | for (int i = index; i < limit; ++i) { 371 | fully_parse(&array->value.array.elements[i]); 372 | } 373 | } 374 | } 375 | 376 | void 377 | bser_parse_object_fields_to(bser_t* obj, size_t limit) 378 | { 379 | assert(bser_is_object(obj)); 380 | if (limit > 0) { 381 | /* Search back to find first parsed, and parse all from there */ 382 | size_t index = limit - 1; 383 | while (!is_fully_parsed(&obj->value.object.fields[index].value) && 384 | index > 0) { 385 | --index; 386 | } 387 | 388 | for (int i = index; i < limit; ++i) { 389 | bser_key_value_pair_t* pair = bser_object_pair_at(obj, i); 390 | bser_parse_if_necessary(&pair->key); 391 | fully_parse(&pair->value); 392 | } 393 | } 394 | } 395 | -------------------------------------------------------------------------------- /bser_parse.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBWATCHMAN_BSER_PARSE_H_ 2 | #define LIBWATCHMAN_BSER_PARSE_H_ 3 | 4 | #include 5 | 6 | #include "bser.h" 7 | 8 | /* Fills in 'bser' with the top-level object parsed from a bser data buffer. 9 | * If 'fill' is NULL, it will be allocated. */ 10 | bser_t* bser_parse_content(uint8_t* buffer, size_t buflen, bser_t* fill); 11 | 12 | /* Fills in 'bser' with the top-level object parsed from a bser PDU. The 13 | * header must start with a '\00' '\01' and then contain a bser-encoded 14 | * integer which indicates the length of the content data. */ 15 | bser_t* bser_parse_buffer(uint8_t* buffer, size_t buflen, bser_t* fill); 16 | 17 | /* Fills in 'bser' with top-level object parsed from a PDU read from a file. 18 | * If 'fill' is NULL, it will be allocated. */ 19 | bser_t* bser_parse_from_file(FILE* file, bser_t* fill); 20 | 21 | #endif /* ndef LIBWATCHMAN_BSER_PARSE_H_ */ 22 | -------------------------------------------------------------------------------- /bser_private.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBWATCHMAN_BSER_PRIVATE_H_ 2 | #define LIBWATCHMAN_BSER_PRIVATE_H_ 3 | 4 | #include 5 | #include 6 | 7 | enum { 8 | BSER_TAG_ARRAY = 0x00, 9 | BSER_TAG_OBJECT = 0x01, 10 | BSER_TAG_STRING = 0x02, 11 | BSER_TAG_INT8 = 0x03, 12 | BSER_TAG_INT16 = 0x04, 13 | BSER_TAG_INT32 = 0x05, 14 | BSER_TAG_INT64 = 0x06, 15 | BSER_TAG_REAL = 0x07, 16 | BSER_TAG_TRUE = 0x08, 17 | BSER_TAG_FALSE = 0x09, 18 | BSER_TAG_NULL = 0x0a, 19 | BSER_TAG_COMPACT_ARRAY = 0x0b, 20 | BSER_TAG_NO_FIELD = 0x0c, 21 | BSER_TAG_UNPARSED = 0x0d, 22 | BSER_TAG_ERROR = 0x0e, 23 | BSER_NUM_TAGS = 0x0f 24 | }; 25 | 26 | typedef struct bser_buffer { 27 | void* data; 28 | size_t datalen; 29 | size_t cursor; 30 | } bser_buffer_t; 31 | 32 | struct bser_key_value_pair; 33 | struct bser_buffer; 34 | 35 | typedef struct bser { 36 | uint8_t type; 37 | union { 38 | int64_t integer; 39 | double real; 40 | struct { 41 | const char* chars; 42 | size_t length; 43 | } string; 44 | struct { 45 | struct bser* elements; 46 | size_t length; 47 | } array; 48 | struct { 49 | struct bser_key_value_pair* fields; 50 | size_t length; 51 | } object; 52 | struct bser_buffer* unparsed; 53 | const char* error_message; 54 | } value; 55 | } bser_t; 56 | 57 | typedef struct bser_key_value_pair { 58 | struct bser key; 59 | struct bser value; 60 | } bser_key_value_pair_t; 61 | 62 | static inline bser_t* bser_alloc(void) 63 | { 64 | return (bser_t*)malloc(sizeof(bser_t)); 65 | } 66 | 67 | void bser_parse_generic(bser_t* fill, struct bser_buffer* buffer); 68 | 69 | static inline void bser_parse_if_necessary(bser_t* bser) 70 | { 71 | if (bser->type == BSER_TAG_UNPARSED) { 72 | bser_parse_generic(bser, bser->value.unparsed); 73 | } 74 | } 75 | 76 | static inline int bser_is_unparsed(bser_t* bser) 77 | { 78 | return bser->type == BSER_TAG_UNPARSED; 79 | } 80 | static inline int bser_is_no_field(bser_t* bser) 81 | { 82 | return bser->type == BSER_TAG_NO_FIELD; 83 | } 84 | 85 | void bser_parse_array_elements_to(struct bser* array, size_t limit); 86 | void bser_parse_object_fields_to(struct bser* array, size_t limit); 87 | 88 | static inline bser_key_value_pair_t* bser_object_pair_at( 89 | struct bser* bser, size_t index) 90 | { 91 | return &bser->value.object.fields[index]; 92 | } 93 | 94 | #endif /* ndef LIBWATCHMAN_BSER_PRIVATE_H_ */ 95 | -------------------------------------------------------------------------------- /bser_write.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "bser.h" 6 | #include "bser_write.h" 7 | 8 | #define SIZE_U8 sizeof(uint8_t) 9 | #define SIZE_S8 sizeof(int8_t) 10 | #define SIZE_S16 sizeof(int16_t) 11 | #define SIZE_S32 sizeof(int32_t) 12 | #define SIZE_S64 sizeof(int64_t) 13 | #define SIZE_DBL sizeof(double) 14 | #define SIZE_MAGIC sizeof(char[2]) 15 | 16 | /* Interface for writing to a stream using a file or char[], or null */ 17 | typedef struct stream { 18 | /* Returns the number bytes were successfully written to the stream */ 19 | size_t (*write)(struct stream*, const void* buffer, size_t bytes); 20 | } stream_t; 21 | 22 | static size_t write_json(json_t* json, stream_t* stream); 23 | 24 | static size_t 25 | write_integer(json_t* json, stream_t* stream) 26 | { 27 | uint8_t tag; 28 | size_t bytes = 0; 29 | 30 | assert(json_is_integer(json)); 31 | json_int_t v = json_integer_value(json); 32 | 33 | int8_t i8 = (int8_t)v; 34 | if (i8 == v) { 35 | tag = BSER_TAG_INT8; 36 | if (stream->write(stream, &tag, SIZE_U8) == SIZE_U8 && 37 | stream->write(stream, &i8, SIZE_S8) == SIZE_S8) { 38 | bytes = SIZE_U8 + SIZE_S8; 39 | } 40 | } else { 41 | int16_t i16 = (int16_t)v; 42 | if (i16 == v) { 43 | tag = BSER_TAG_INT16; 44 | if (stream->write(stream, &tag, SIZE_U8) == SIZE_U8 && 45 | stream->write(stream, &i16, SIZE_S16) == SIZE_S16) { 46 | bytes = SIZE_U8 + SIZE_S16; 47 | } 48 | } else { 49 | int32_t i32 = (int32_t)v; 50 | if (i32 == v) { 51 | tag = BSER_TAG_INT32; 52 | if (stream->write(stream, &tag, SIZE_U8) == SIZE_U8 && 53 | stream->write(stream, &i32, SIZE_S32) == SIZE_S32) { 54 | bytes = SIZE_U8 + SIZE_S32; 55 | } 56 | } else { 57 | tag = BSER_TAG_INT64; 58 | if (stream->write(stream, &tag, SIZE_U8) == SIZE_U8 && 59 | stream->write(stream, &v, SIZE_S64) == SIZE_S64) { 60 | bytes = SIZE_U8 + SIZE_S64; 61 | } 62 | } 63 | } 64 | } 65 | return bytes; 66 | } 67 | 68 | static size_t 69 | write_string(json_t* json, stream_t* stream) 70 | { 71 | size_t bytes = 0; 72 | assert(json_is_string(json)); 73 | const char* chars = json_string_value(json); 74 | size_t len = strlen(chars); 75 | uint8_t tag = BSER_TAG_STRING; 76 | 77 | if (stream->write(stream, &tag, SIZE_U8) == SIZE_U8) { 78 | json_t* length_node = json_integer(len); 79 | size_t len_bytes = write_integer(length_node, stream); 80 | json_decref(length_node); 81 | if (len_bytes > 0 && 82 | stream->write(stream, chars, len) == len) { 83 | bytes = SIZE_U8 + len_bytes + len; 84 | } 85 | } 86 | return bytes; 87 | } 88 | 89 | static size_t 90 | write_object(json_t* json, stream_t* stream) 91 | { 92 | uint8_t tag = BSER_TAG_OBJECT; 93 | size_t bytes = 0; 94 | 95 | assert(json_is_object(json)); 96 | 97 | if (stream->write(stream, &tag, SIZE_U8) == SIZE_U8) { 98 | size_t total_bytes = SIZE_U8; 99 | 100 | json_t* length_node = json_integer(json_object_size(json)); 101 | size_t integer_length = write_integer(length_node, stream); 102 | total_bytes += integer_length; 103 | json_decref(length_node); 104 | 105 | if (integer_length > 0) { 106 | size_t val_bytes = 1; 107 | void *iter = json_object_iter(json); 108 | 109 | while (iter != NULL && val_bytes > 0) { 110 | val_bytes = 0; 111 | 112 | json_t* key_node = json_string(json_object_iter_key(iter)); 113 | size_t key_bytes = write_string(key_node, stream); 114 | total_bytes += key_bytes; 115 | json_decref(key_node); 116 | 117 | if (key_bytes > 0) { 118 | json_t* value = json_object_iter_value(iter); 119 | val_bytes = write_json(value, stream); 120 | total_bytes += val_bytes; 121 | } 122 | iter = json_object_iter_next(json, iter); 123 | } 124 | if (iter == NULL) { 125 | bytes = total_bytes; 126 | } 127 | } 128 | } 129 | return bytes; 130 | } 131 | 132 | static int 133 | can_be_compact_array(json_t* json) 134 | { 135 | assert(json_is_array(json)); 136 | size_t length = json_array_size(json); 137 | 138 | int is_all_objects = 1; 139 | for (int i = 0; i < length && is_all_objects; ++i) { 140 | json_t* elem = json_array_get(json, i); 141 | is_all_objects &= json_is_object(elem); 142 | } 143 | return length > 1 && is_all_objects; 144 | } 145 | 146 | static int 147 | string_array_contains(json_t* array, const char* string) 148 | { 149 | assert(json_is_array(array)); 150 | int is_present = 0; 151 | for (int i = 0; i < json_array_size(array) && is_present == 0; ++i) { 152 | json_t* str = json_array_get(array, i); 153 | assert(json_is_string(str)); 154 | is_present = !strcmp(json_string_value(str), string); 155 | } 156 | return is_present; 157 | } 158 | 159 | static json_t* 160 | build_compact_array_header(json_t* json) 161 | { 162 | assert(json_is_array(json)); 163 | size_t length = json_array_size(json); 164 | json_t* compact_header = json_array(); 165 | 166 | for (int i = 0; i < length; ++i) { 167 | json_t* obj = json_array_get(json, i); 168 | assert(json_is_object(obj)); 169 | 170 | void* iter = json_object_iter(obj); 171 | while (iter) { 172 | const char* key = json_object_iter_key(iter); 173 | if (!string_array_contains(compact_header, key)) { 174 | json_array_append_new(compact_header, json_string(key)); 175 | } 176 | iter = json_object_iter_next(obj, iter); 177 | } 178 | } 179 | return compact_header; 180 | } 181 | 182 | static size_t 183 | write_array(json_t* json, stream_t* stream); 184 | 185 | static size_t 186 | write_compact_object(json_t* object, json_t* header, stream_t* stream) 187 | { 188 | size_t bytes = 0; 189 | size_t field_bytes = 1; 190 | size_t fields_bytes = 0; 191 | int i; 192 | 193 | assert(json_is_array(header)); 194 | size_t header_length = json_array_size(header); 195 | 196 | for (i = 0; i < header_length && field_bytes > 0; ++i) { 197 | const char* key = json_string_value(json_array_get(header, i)); 198 | json_t* value = json_object_get(object, key); 199 | 200 | if (value == NULL) { 201 | uint8_t no_field_tag = BSER_TAG_NO_FIELD; 202 | field_bytes = stream->write(stream, &no_field_tag, SIZE_U8); 203 | } else { 204 | field_bytes = write_json(value, stream); 205 | } 206 | fields_bytes += field_bytes; 207 | } 208 | if (i == header_length) { 209 | bytes = fields_bytes; 210 | } 211 | 212 | return bytes; 213 | } 214 | 215 | static size_t 216 | write_compact_objects(json_t* objects, json_t* header, stream_t* stream) 217 | { 218 | size_t bytes = 0; 219 | size_t obj_bytes = 1; 220 | size_t objs_bytes = 0; 221 | int i; 222 | 223 | assert(json_is_array(objects)); 224 | size_t array_length = json_array_size(objects); 225 | 226 | for (i = 0; i < array_length && obj_bytes > 0; ++i) { 227 | json_t* obj = json_array_get(objects, i); 228 | 229 | obj_bytes = write_compact_object(obj, header, stream); 230 | objs_bytes += obj_bytes; 231 | } 232 | 233 | if (i == array_length) { 234 | bytes = objs_bytes; 235 | } 236 | return bytes; 237 | } 238 | 239 | static size_t 240 | write_compact_array(json_t* array, stream_t* stream) 241 | { 242 | size_t bytes = 0; 243 | 244 | assert(json_is_array(array)); 245 | json_t* header = build_compact_array_header(array); 246 | 247 | uint8_t tag = BSER_TAG_COMPACT_ARRAY; 248 | if (stream->write(stream, &tag, SIZE_U8) == SIZE_U8) { 249 | size_t header_bytes = write_array(header, stream); 250 | if (header_bytes > 0) { 251 | size_t array_length = json_array_size(array); 252 | 253 | json_t* array_length_node = json_integer(array_length); 254 | size_t integer_size = write_integer(array_length_node, stream); 255 | if (integer_size > 0) { 256 | size_t written = write_compact_objects(array, header, stream); 257 | if (written > 0) { 258 | bytes = SIZE_U8 + header_bytes + integer_size + written; 259 | } 260 | } 261 | json_decref(array_length_node); 262 | } 263 | } 264 | json_decref(header); 265 | return bytes; 266 | } 267 | 268 | static size_t 269 | write_array(json_t* json, stream_t* stream) 270 | { 271 | size_t bytes = 0; 272 | 273 | if (can_be_compact_array(json)) { 274 | bytes = write_compact_array(json, stream); 275 | } else { 276 | uint8_t tag = BSER_TAG_ARRAY; 277 | if (stream->write(stream, &tag, SIZE_U8) == SIZE_U8) { 278 | size_t int_bytes; 279 | 280 | size_t length = json_array_size(json); 281 | json_t* length_node = json_integer(length); 282 | 283 | int_bytes = write_integer(length_node, stream); 284 | json_decref(length_node); 285 | 286 | if (int_bytes > 0) { 287 | int i; 288 | size_t elem_bytes = 1; 289 | size_t elems_bytes = 0; 290 | 291 | for (i = 0; i < length && elem_bytes > 0; ++i) { 292 | elem_bytes = write_json(json_array_get(json, i), stream); 293 | elems_bytes += elem_bytes; 294 | } 295 | 296 | if (i == length) { 297 | bytes = SIZE_U8 + int_bytes + elems_bytes; 298 | } 299 | } 300 | } 301 | } 302 | return bytes; 303 | } 304 | 305 | static size_t 306 | write_real(json_t* json, stream_t* stream) 307 | { 308 | uint8_t op = BSER_TAG_REAL; 309 | assert(json_is_real(json)); 310 | double val = json_real_value(json); 311 | size_t bytes = 0; 312 | 313 | if (stream->write(stream, &op, SIZE_U8) == SIZE_U8) { 314 | if (stream->write(stream, &val, SIZE_DBL) == SIZE_DBL) { 315 | bytes = SIZE_U8 + SIZE_DBL; 316 | } 317 | } 318 | return bytes; 319 | } 320 | 321 | static size_t 322 | write_true(json_t* json, stream_t* stream) 323 | { 324 | uint8_t op = BSER_TAG_TRUE; 325 | return stream->write(stream, &op, SIZE_U8); 326 | } 327 | 328 | static size_t 329 | write_false(json_t* json, stream_t* stream) 330 | { 331 | uint8_t op = BSER_TAG_FALSE; 332 | return stream->write(stream, &op, SIZE_U8); 333 | } 334 | 335 | static size_t 336 | write_null(json_t* json, stream_t* stream) 337 | { 338 | uint8_t op = BSER_TAG_NULL; 339 | return stream->write(stream, &op, SIZE_U8); 340 | } 341 | 342 | static size_t 343 | write_json(json_t* json, stream_t* stream) 344 | { 345 | switch (json_typeof(json)) { 346 | case JSON_OBJECT: return write_object(json, stream); 347 | case JSON_ARRAY: return write_array(json, stream); 348 | case JSON_STRING: return write_string(json, stream); 349 | case JSON_INTEGER: return write_integer(json, stream); 350 | case JSON_REAL: return write_real(json, stream); 351 | case JSON_TRUE: return write_true(json, stream); 352 | case JSON_FALSE: return write_false(json, stream); 353 | case JSON_NULL: return write_null(json, stream); 354 | default: return 0; 355 | } 356 | } 357 | 358 | /* Null stream is used to count the number of bytes that would be written. */ 359 | struct null_stream { 360 | stream_t stream; 361 | }; 362 | 363 | static size_t 364 | null_stream_write(stream_t* s, const void* data, size_t nb) 365 | { 366 | return nb; 367 | } 368 | 369 | /* For writing to a memory buffer stream */ 370 | struct buffer_stream { 371 | stream_t stream; 372 | bser_buffer_t buffer; 373 | }; 374 | 375 | static size_t 376 | buffer_stream_write(stream_t* s, const void* data, size_t nb) 377 | { 378 | size_t result = 0; 379 | 380 | struct buffer_stream* stream = (struct buffer_stream*)s; 381 | 382 | if (stream->buffer.cursor + nb <= stream->buffer.datalen) { 383 | memcpy(stream->buffer.data + stream->buffer.cursor, data, nb); 384 | stream->buffer.cursor += nb; 385 | result = nb; 386 | } 387 | return result; 388 | } 389 | 390 | /* For writing a FILE stream */ 391 | struct file_stream { 392 | stream_t stream; 393 | FILE* file; 394 | size_t position; 395 | }; 396 | 397 | static size_t 398 | file_stream_write(stream_t* s, const void* data, size_t nb) 399 | { 400 | struct file_stream* stream = (struct file_stream*)s; 401 | size_t count = fwrite(data, SIZE_U8, nb, stream->file); 402 | if (count == nb) { 403 | stream->position += nb; 404 | return nb; 405 | } else { 406 | return 0; 407 | } 408 | } 409 | 410 | size_t 411 | bser_encoding_size(json_t* node) 412 | { 413 | struct null_stream stream; 414 | stream.stream.write = null_stream_write; 415 | 416 | return write_json(node, &stream.stream); 417 | } 418 | 419 | size_t 420 | bser_header_size(size_t content_size) { 421 | json_t* node = json_integer(content_size); 422 | size_t sz = SIZE_MAGIC + bser_encoding_size(node); 423 | json_decref(node); 424 | 425 | return sz; 426 | } 427 | 428 | static size_t 429 | write_header(size_t content_size, stream_t* stream) 430 | { 431 | const uint8_t magic[] = { 0x00, 0x01 }; 432 | size_t node_bytes; 433 | 434 | json_t* content_size_node = json_integer(content_size); 435 | 436 | if (stream->write(stream, magic, SIZE_MAGIC) == SIZE_MAGIC && 437 | (node_bytes = write_json(content_size_node, stream)) > 0) { 438 | return SIZE_MAGIC + node_bytes; 439 | } else { 440 | return 0; 441 | } 442 | } 443 | 444 | static size_t 445 | write_pdu(json_t* root, size_t content_size, stream_t* stream) 446 | { 447 | size_t hdr_bytes; 448 | size_t content_bytes; 449 | 450 | if ((hdr_bytes = write_header(content_size, stream)) > 0 && 451 | (content_bytes = write_json(root, stream)) == content_size) { 452 | return hdr_bytes + content_bytes; 453 | } else { 454 | return 0; 455 | } 456 | } 457 | 458 | 459 | size_t 460 | bser_write_to_buffer( 461 | json_t* root, size_t content_size, void* buffer, size_t buflen) 462 | { 463 | struct buffer_stream stream; 464 | 465 | assert(buffer != NULL); 466 | 467 | stream.stream.write = buffer_stream_write; 468 | stream.buffer.data = buffer; 469 | stream.buffer.datalen = buflen; 470 | stream.buffer.cursor = 0; 471 | 472 | return write_pdu(root, content_size, &stream.stream); 473 | } 474 | 475 | size_t 476 | bser_write_to_file(json_t* root, FILE* file) 477 | { 478 | size_t content_size; 479 | size_t bytes; 480 | struct file_stream stream; 481 | 482 | assert(file != NULL); 483 | 484 | stream.stream.write = file_stream_write; 485 | stream.file = file; 486 | stream.position = 0; 487 | 488 | content_size = bser_encoding_size(root); 489 | bytes = write_pdu(root, content_size, &stream.stream); 490 | fflush(file); 491 | return bytes; 492 | } 493 | -------------------------------------------------------------------------------- /bser_write.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBWATCHMAN_BSER_WRITE_H_ 2 | #define LIBWATCHMAN_BSER_WRITE_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | /* Returns the number of bytes needed for encoding 'node'. Does not include 9 | * any header */ 10 | size_t bser_encoding_size(json_t* node); 11 | 12 | /* Size of the header needed for content of size 'content_size' */ 13 | size_t bser_header_size(size_t content_size); 14 | 15 | /* Write JSON data in BSER format to a memory buffer, including a header with 16 | * the magic value and the content size. Returns the number of bytes that were 17 | * written, 0 on error. */ 18 | size_t bser_write_to_buffer( 19 | json_t* root, size_t content_size, void* buffer, size_t buflen); 20 | 21 | /* Write JSON data in BSER format to a FILE, including a header with the magic 22 | * value and the content size. Returns the number of bytes that were written, 23 | * 0 on error. */ 24 | size_t bser_write_to_file(json_t* root, FILE* file); 25 | 26 | #endif /* ndef LIBWATCHMAN_BSER_WRITE_H */ 27 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT([libwatchman], [0.0.4], [], [libwatchman]) 2 | AC_CANONICAL_TARGET 3 | AM_INIT_AUTOMAKE([dist-bzip2 subdir-objects foreign]) 4 | AC_CONFIG_MACRO_DIR([m4]) 5 | 6 | CFLAGS="$CFLAGS -Wall -std=c99 -D_XOPEN_SOURCE -D_BSD_SOURCE" 7 | 8 | AC_PROG_CC 9 | AC_PROG_CPP 10 | AC_PROG_LIBTOOL 11 | AM_PROG_CC_C_O 12 | AM_PROG_AS 13 | 14 | AC_CHECK_HEADER([check.h], [], AC_MSG_ERROR([unable to find check])) 15 | # NOTE: If you get an error about check.h, install "check" and get an 16 | # error about AM_PATH_CHECK() you need to re-run autogen.sh, since the 17 | # AM_PATH_CHECK comes with the "check" package. 18 | AM_PATH_CHECK([0.9.4]) 19 | 20 | LT_INIT() 21 | 22 | AC_SEARCH_LIBS([socket], [socket], [], AC_MSG_ERROR([unable to find socket()])) 23 | AC_SEARCH_LIBS([json_array], [jansson], [], AC_MSG_ERROR([unable to find jansson])) 24 | 25 | AC_CONFIG_HEADER([config.h]) 26 | AC_CONFIG_FILES([Makefile tests/Makefile]) 27 | AC_OUTPUT 28 | 29 | -------------------------------------------------------------------------------- /proto.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBWATCHMAN_PROTO_H_ 2 | #define LIBWATCHMAN_PROTO_H_ 3 | 4 | #include 5 | #include "bser.h" 6 | #include "bser_parse.h" 7 | 8 | /* A wrapper around json (jansson) or bser */ 9 | 10 | enum { PROTO_BSER, PROTO_JSON }; 11 | 12 | typedef struct proto_ptr { 13 | union { 14 | json_t* json; 15 | bser_t* bser; 16 | } u; 17 | int type; 18 | } proto_t; 19 | 20 | proto_t 21 | proto_from_json(json_t* json) 22 | { 23 | proto_t proto; 24 | proto.u.json = json; 25 | proto.type = PROTO_JSON; 26 | return proto; 27 | } 28 | 29 | proto_t 30 | proto_from_bser(bser_t* bser) 31 | { 32 | proto_t proto; 33 | proto.u.bser = bser; 34 | proto.type = PROTO_BSER; 35 | return proto; 36 | } 37 | 38 | proto_t 39 | proto_null() 40 | { 41 | proto_t proto; 42 | proto.u.json = NULL; 43 | return proto; 44 | } 45 | 46 | int 47 | proto_is_null(proto_t p) 48 | { 49 | return p.u.json == NULL; 50 | } 51 | 52 | #define PROTO_DISPATCH(ret, name) \ 53 | ret \ 54 | proto_##name(proto_t p) \ 55 | { \ 56 | return p.type == PROTO_JSON ? \ 57 | json_##name(p.u.json) : \ 58 | bser_##name(p.u.bser); \ 59 | } 60 | 61 | PROTO_DISPATCH(int, is_boolean) 62 | PROTO_DISPATCH(int, is_integer) 63 | PROTO_DISPATCH(int, is_real) 64 | PROTO_DISPATCH(int, is_string) 65 | PROTO_DISPATCH(int, is_array) 66 | PROTO_DISPATCH(int, is_object) 67 | PROTO_DISPATCH(int, is_true) 68 | PROTO_DISPATCH(int64_t, integer_value) 69 | PROTO_DISPATCH(double, real_value) 70 | PROTO_DISPATCH(int, array_size) 71 | 72 | #undef PROTO_DISPATCH 73 | 74 | /* Returns a potentially non-null-terminated read-only string, with a length */ 75 | const char* 76 | proto_string_value(proto_t p, size_t* length) 77 | { 78 | if (p.type == PROTO_JSON) { 79 | const char* v = json_string_value(p.u.json); 80 | *length = strlen(v); 81 | return v; 82 | } else { 83 | return bser_string_value(p.u.bser, length); 84 | } 85 | } 86 | 87 | /* Returns a dynamically-allocated null-terminated c-string (caller-owned) */ 88 | char* 89 | proto_strdup(proto_t p) 90 | { 91 | size_t length; 92 | const char* v = proto_string_value(p, &length); 93 | if (v[length] == '\0') { 94 | return strdup(v); 95 | } else { 96 | char* res = malloc(length + 1); 97 | memcpy(res, v, length); 98 | res[length] = '\0'; 99 | return res; 100 | } 101 | } 102 | 103 | proto_t 104 | proto_array_get(proto_t p, int index) 105 | { 106 | return p.type == PROTO_JSON ? 107 | proto_from_json(json_array_get(p.u.json, index)) : 108 | proto_from_bser(bser_array_get(p.u.bser, index)); 109 | } 110 | 111 | proto_t 112 | proto_object_get(proto_t p, const char* key) 113 | { 114 | return p.type == PROTO_JSON ? 115 | proto_from_json(json_object_get(p.u.json, key)) : 116 | proto_from_bser(bser_object_get(p.u.bser, key)); 117 | } 118 | 119 | char* 120 | proto_dumps(proto_t p, int flags) 121 | { 122 | json_t* json = p.u.json; 123 | char* result; 124 | if (p.type == PROTO_BSER) { 125 | json_error_t err; 126 | json = bser2json(p.u.bser, &err); 127 | if (json == NULL) { 128 | json = json_string(err.text); 129 | flags |= JSON_ENCODE_ANY; 130 | } 131 | } 132 | result = json_dumps(json, flags); 133 | if (p.type == PROTO_BSER) { 134 | json_decref(json); 135 | } 136 | return result; 137 | } 138 | 139 | /* The proto_free command only decrefs the json object in json mode, so 140 | * it's only a true free if the json only has one reference. */ 141 | void 142 | proto_free(proto_t p) 143 | { 144 | if (p.type == PROTO_JSON) { 145 | json_decref(p.u.json); 146 | p.u.json = NULL; 147 | } else { 148 | bser_free(p.u.bser); 149 | p.u.bser = NULL; 150 | } 151 | } 152 | 153 | #endif /* ndef LIBWATCHMAN_PROTO_H_ */ 154 | -------------------------------------------------------------------------------- /tests/Makefile.am: -------------------------------------------------------------------------------- 1 | ## Process this file with automake to produce Makefile.in 2 | 3 | TESTS = check_watchman check_bser 4 | check_PROGRAMS = check_watchman check_bser json2bser bser2json 5 | 6 | check_watchman_SOURCES = check_watchman.c $(top_builddir)/watchman.h 7 | check_watchman_CFLAGS = @CHECK_CFLAGS@ 8 | check_watchman_LDADD = ../libwatchman.la @CHECK_LIBS@ 9 | check_watchman_LDFLAGS = -Wl,-rpath -Wl,$(prefix) 10 | 11 | check_bser_SOURCES = check_bser.c 12 | check_bser_CFLAGS = @CHECK_CFLAGS@ 13 | check_bser_LDADD = ../libwatchman.la @CHECK_LIBS@ 14 | check_bser_LDFLAGS = -Wl,-rpath -Wl,$(prefix) 15 | 16 | 17 | json2bser_SOURCES = json2bser.c $(top_builddir)/bser.h 18 | json2bser_LDADD = ../libwatchman.la 19 | json2bser_LDFLAGS = -Wl,-rpath -Wl,$(prefix) 20 | 21 | bser2json_SOURCES = bser2json.c $(top_builddir)/bser.h $(top_builddir)/bser_write.h 22 | bser2json_LDADD = ../libwatchman.la 23 | bser2json_LDFLAGS = -Wl,-rpath -Wl,$(prefix) 24 | -------------------------------------------------------------------------------- /tests/bser2json.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include "bser_parse.h" 7 | 8 | int main(int argc, char* argv[]) { 9 | #if JANSSON_VERSION_HEX >= 0x020600 10 | json_object_seed(1); /* make test results repeatable */ 11 | #endif 12 | bser_t bser; 13 | json_error_t err; 14 | bser_parse_from_file(stdin, &bser); 15 | json_t* root = bser2json(&bser, &err); 16 | if (root == NULL) { 17 | fprintf(stderr, "Could not convert BSER: %s\n", err.text); 18 | } else { 19 | json_dumpf(root, stdout, JSON_INDENT(4) | JSON_ENCODE_ANY); 20 | fprintf(stdout, "\n"); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/check_bser.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include "bser.h" 7 | #include "bser_parse.h" 8 | #include "bser_write.h" 9 | 10 | void 11 | setup(void) 12 | { 13 | } 14 | 15 | void 16 | teardown(void) 17 | { 18 | } 19 | 20 | START_TEST(test_bser_parse_simple) 21 | { 22 | uint8_t buffer[] = { 0x03, 0x42 }; 23 | bser_t* bser = bser_parse_content(buffer, sizeof(buffer), NULL); 24 | ck_assert_msg(bser != NULL && !bser_is_error(bser), "Parse error"); 25 | ck_assert_msg(bser_is_integer(bser), "Did not parse integer"); 26 | ck_assert_msg(bser_integer_value(bser) == 0x42, "Parsed wrong value"); 27 | bser_free(bser); 28 | } 29 | END_TEST 30 | 31 | START_TEST(test_bser_in_order_parse) 32 | { 33 | json_error_t err; 34 | json_t* root = json_loads( 35 | "[ " 36 | "{ \"val\": 42, \"foo\": \"foo_value\", \"bar\": \"bar_value\" }, " 37 | "4, " 38 | "{ \"one\": 1, \"two\": 2, \"three\": 3, \"four\": 4 }, " 39 | "{ \"five\": 5, \"field\": \"field_value\" }, " 40 | "277 " 41 | "]", JSON_DISABLE_EOF_CHECK, &err); 42 | 43 | ck_assert_msg(root != NULL, "Parse error on input json"); 44 | 45 | size_t content_size = bser_encoding_size(root); 46 | 47 | ck_assert_msg(content_size > 0, "Bad content byte count"); 48 | 49 | size_t hdr_size = bser_header_size(content_size); 50 | size_t buf_size = hdr_size + content_size; 51 | uint8_t* buffer = malloc(buf_size); 52 | 53 | size_t wrote = bser_write_to_buffer(root, content_size, buffer, buf_size); 54 | json_decref(root); 55 | 56 | ck_assert_msg(wrote > 0, "Overflowed buffer"); 57 | 58 | bser_t* bser = bser_parse_buffer(buffer, buf_size, NULL); 59 | ck_assert_msg(bser != NULL && !bser_is_error(bser), "Parse error"); 60 | ck_assert_msg(bser_is_array(bser), "Did not parse root array"); 61 | 62 | bser_t* item = bser_array_get(bser, 3); 63 | ck_assert_msg(item != NULL, "NULL item at array[3]"); 64 | ck_assert_msg(bser_is_object(item), "Wrong type in array[3]"); 65 | 66 | bser_t* five = bser_object_get(item, "five"); 67 | ck_assert_msg(five != NULL, "NULL field 'five'"); 68 | ck_assert_msg(bser_is_integer(five), "Wrong type in object"); 69 | ck_assert_msg(bser_integer_value(five) == 5, "Wrong value in field in object 3"); 70 | 71 | item = bser_array_get(bser, 0); 72 | ck_assert_msg(item != NULL, "NULL item at array[0]"); 73 | ck_assert_msg(bser_is_object(item), "Wrong type in array[0]"); 74 | bser_t* val = bser_object_get(item, "val"); 75 | ck_assert_msg(val != NULL, "NULL field 'val'"); 76 | ck_assert_msg(bser_integer_value(val) == 42, "Wrong value in object 0"); 77 | 78 | item = bser_array_get(bser, 1); 79 | ck_assert_msg(bser_is_integer(item), "Wrong type in array[1]"); 80 | ck_assert_msg(bser_integer_value(item) == 4, "Wrong value in array[1]"); 81 | 82 | item = bser_array_get(bser, 4); 83 | ck_assert_msg(bser_is_integer(item), "Wrong type in array[4]"); 84 | ck_assert_msg(bser_integer_value(item) == 277, "Wrong value in array[4]"); 85 | 86 | bser_free(bser); 87 | } 88 | END_TEST 89 | 90 | START_TEST(test_bser_in_order_parse_compact) 91 | { 92 | json_error_t err; 93 | json_t* root = json_loads( 94 | "[ " 95 | "{ \"val\": 42, \"foo\": \"foo_value\", \"bar\": \"bar_value\" }, " 96 | "{ \"one\": 1, \"two\": 2, \"three\": 3, \"four\": 4 }, " 97 | "{ \"five\": 5, \"field\": \"field_value\" } " 98 | "]", JSON_DISABLE_EOF_CHECK, &err); 99 | 100 | ck_assert_msg(root != NULL, "Parse error on input json"); 101 | 102 | size_t content_size = bser_encoding_size(root); 103 | 104 | ck_assert_msg(content_size > 0, "Bad content byte count"); 105 | 106 | size_t hdr_size = bser_header_size(content_size); 107 | size_t buf_size = hdr_size + content_size; 108 | uint8_t* buffer = malloc(buf_size); 109 | size_t wrote = bser_write_to_buffer(root, content_size, buffer, buf_size); 110 | json_decref(root); 111 | 112 | ck_assert_msg(wrote > 0, "Overflowed buffer"); 113 | 114 | bser_t* bser = bser_parse_buffer(buffer, buf_size, NULL); 115 | ck_assert_msg(bser != NULL && !bser_is_error(bser), "Parse error"); 116 | ck_assert_msg(bser_is_array(bser), "Did not parse root array"); 117 | 118 | bser_t* item = bser_array_get(bser, 2); 119 | ck_assert_msg(item != NULL, "NULL item at array[2]"); 120 | ck_assert_msg(bser_is_object(item), "Wrong type in array[2]"); 121 | 122 | bser_t* five = bser_object_get(item, "five"); 123 | ck_assert_msg(five != NULL, "NULL field 'five'"); 124 | ck_assert_msg(bser_is_integer(five), "Wrong type in object"); 125 | ck_assert_msg(bser_integer_value(five) == 5, "Wrong value in object 2"); 126 | 127 | item = bser_array_get(bser, 0); 128 | ck_assert_msg(item != NULL, "NULL item at array[0]"); 129 | ck_assert_msg(bser_is_object(item), "Wrong type in array[0]"); 130 | bser_t* val = bser_object_get(item, "val"); 131 | ck_assert_msg(val != NULL, "NULL field 'val'"); 132 | ck_assert_msg(bser_integer_value(val) == 42, "Wrong value in object 0"); 133 | 134 | bser_free(bser); 135 | } 136 | END_TEST 137 | 138 | 139 | 140 | Suite * 141 | bser_suite(void) 142 | { 143 | Suite *s = suite_create("Tests"); 144 | 145 | /* Core test case */ 146 | TCase *tc_core = tcase_create("Core"); 147 | tcase_add_checked_fixture(tc_core, setup, teardown); 148 | tcase_add_test(tc_core, test_bser_parse_simple); 149 | tcase_add_test(tc_core, test_bser_in_order_parse); 150 | tcase_add_test(tc_core, test_bser_in_order_parse_compact); 151 | suite_add_tcase(s, tc_core); 152 | 153 | return s; 154 | } 155 | 156 | int 157 | main(void) 158 | { 159 | int number_failed; 160 | Suite *s = bser_suite(); 161 | SRunner *sr = srunner_create(s); 162 | srunner_run_all(sr, CK_NORMAL); 163 | number_failed = srunner_ntests_failed(sr); 164 | srunner_free(sr); 165 | return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; 166 | } 167 | -------------------------------------------------------------------------------- /tests/check_watchman.c: -------------------------------------------------------------------------------- 1 | #include "../watchman.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | char test_dir[L_tmpnam]; 13 | 14 | void 15 | setup(void) 16 | { 17 | assert(tmpnam(test_dir)); 18 | mode_t mode = 0700; 19 | if (mkdir(test_dir, mode)) { 20 | perror("Couldn't make test directory"); 21 | exit(1); 22 | } 23 | } 24 | 25 | static int 26 | is_dir(char *path) 27 | { 28 | struct stat statbuf; 29 | stat(path, &statbuf); 30 | return S_ISDIR(statbuf.st_mode); 31 | } 32 | 33 | static void 34 | rmdir_recursive(char *dir) 35 | { 36 | DIR *dh = opendir(dir); 37 | struct dirent *cur; 38 | int dir_len = strlen(dir); 39 | char path[256 + dir_len + 2]; 40 | strcpy(path, dir); 41 | path[dir_len] = '/'; 42 | while ((cur = readdir(dh))) { 43 | if (strcmp(cur->d_name, ".") && strcmp(cur->d_name, "..")) { 44 | strcpy(path + dir_len + 1, cur->d_name); 45 | if (is_dir(path)) { 46 | rmdir_recursive(path); 47 | rmdir(path); 48 | } else { 49 | unlink(path); 50 | } 51 | } 52 | } 53 | closedir(dh); 54 | if (rmdir(dir)) { 55 | perror("Couldn't remove test directory"); 56 | exit(1); 57 | } 58 | } 59 | 60 | void 61 | teardown(void) 62 | { 63 | rmdir_recursive(test_dir); 64 | } 65 | 66 | START_TEST(test_watchman_connect_timeout_fails) 67 | { 68 | struct watchman_error error; 69 | struct timeval tv = {0}; 70 | tv.tv_usec = 1; 71 | struct watchman_connection *conn = watchman_connect(tv, &error); 72 | ck_assert_msg(conn == NULL, "Should have failed to connect"); 73 | } 74 | END_TEST 75 | 76 | START_TEST(test_watchman_connect_timeout_succeeds) 77 | { 78 | struct watchman_error error; 79 | struct timeval tv = {0}; 80 | tv.tv_sec = 10; 81 | struct watchman_connection *conn = watchman_connect(tv, &error); 82 | ck_assert_msg(conn != NULL, error.message); 83 | watchman_connection_close(conn); 84 | } 85 | END_TEST 86 | 87 | 88 | START_TEST(test_watchman_connect) 89 | { 90 | struct watchman_error error; 91 | struct timeval tv_zero = {0}; 92 | struct watchman_connection *conn = watchman_connect(tv_zero, &error); 93 | ck_assert_msg(conn != NULL, error.message); 94 | ck_assert_msg(!watchman_watch(conn, test_dir, &error), error.message); 95 | 96 | struct watchman_watch_list *watched; 97 | watched = watchman_watch_list(conn, &error); 98 | ck_assert_msg(watched != NULL, error.message); 99 | 100 | struct stat buf; 101 | int status = lstat(test_dir, &buf); 102 | ck_assert_msg(status == 0, error.message); 103 | ino_t inode = buf.st_ino; 104 | 105 | int found = 0; 106 | int i; 107 | for (i = 0; i < watched->nr; ++i) { 108 | if (!strcmp(watched->roots[i], test_dir)) { 109 | found = 1; 110 | break; 111 | } else { 112 | /* Different paths may refer to the same directory -- check the inode */ 113 | if ((lstat(watched->roots[i], &buf) == 0) && buf.st_ino == inode) { 114 | found = 1; 115 | break; 116 | } 117 | } 118 | } 119 | ck_assert_msg(found, "Dir we just started watching is not in watch-list"); 120 | watchman_free_watch_list(watched); 121 | 122 | ck_assert_msg(!watchman_watch_del(conn, test_dir, &error), error.message); 123 | 124 | watched = watchman_watch_list(conn, &error); 125 | ck_assert_msg(watched != NULL, error.message); 126 | 127 | for (i = 0; i < watched->nr; ++i) { 128 | if (!strcmp(watched->roots[i], test_dir)) { 129 | ck_abort_msg("Dir we just stopped watching is in watch-list"); 130 | } 131 | } 132 | 133 | watchman_free_watch_list(watched); 134 | 135 | watchman_connection_close(conn); 136 | } 137 | END_TEST 138 | 139 | static void 140 | create_file(char *filename, char *body) 141 | { 142 | int test_dir_len = strlen(test_dir); 143 | char *path = malloc(test_dir_len + strlen(filename) + 2); 144 | strcpy(path, test_dir); 145 | path[test_dir_len] = '/'; 146 | strcpy(path + test_dir_len + 1, filename); 147 | FILE *fp = fopen(path, "a"); 148 | fwrite(body, strlen(body), 1, fp); 149 | fclose(fp); 150 | free(path); 151 | 152 | } 153 | 154 | static void 155 | create_dir(char *dirname) 156 | { 157 | int test_dir_len = strlen(test_dir); 158 | char *path = malloc(test_dir_len + strlen(dirname) + 2); 159 | strcpy(path, test_dir); 160 | path[test_dir_len] = '/'; 161 | strcpy(path + test_dir_len + 1, dirname); 162 | ck_assert(mkdir(path, 0700) == 0); 163 | free(path); 164 | } 165 | 166 | START_TEST(test_watchman_watch) 167 | { 168 | struct watchman_error error; 169 | struct timeval tv_zero = {0}; 170 | struct watchman_connection *conn = watchman_connect(tv_zero, &error); 171 | ck_assert_msg(conn != NULL, error.message); 172 | ck_assert_msg(!watchman_watch(conn, test_dir, &error), error.message); 173 | 174 | struct watchman_expression *since; 175 | since = watchman_since_expression_time_t(0, 0); 176 | /* we expect to get nothing back from this one */ 177 | struct watchman_query_result *result = 178 | watchman_do_query(conn, test_dir, NULL, since, &error); 179 | ck_assert_msg(result != NULL, error.message); 180 | ck_assert_int_eq(0, result->nr); 181 | char *clock = strdup(result->clock); 182 | watchman_free_query_result(result); 183 | watchman_free_expression(since); 184 | 185 | create_file("morx.jar", "abcde"); 186 | 187 | /* now that we have created a file, check again */ 188 | since = watchman_since_expression(clock, 0); 189 | free(clock); 190 | int fields = WATCHMAN_FIELD_NAME | WATCHMAN_FIELD_CTIME_F; 191 | struct watchman_query *query = watchman_query(); 192 | watchman_query_add_suffix(query, "jar"); 193 | watchman_query_set_fields(query, fields); 194 | result = watchman_do_query(conn, test_dir, query, since, &error); 195 | ck_assert_msg(result != NULL, error.message); 196 | ck_assert_int_eq(1, result->nr); 197 | ck_assert_str_eq("morx.jar", result->stats[0].name); 198 | ck_assert(result->stats[0].ctime_f > 1390436718.0); 199 | watchman_free_query_result(result); 200 | watchman_free_query(query); 201 | 202 | /* try with a file inside a directory, to check paths */ 203 | 204 | create_dir("fleem"); 205 | create_file("fleem/fleem.jar", "body"); 206 | 207 | query = watchman_query(); 208 | watchman_query_add_path(query, "fleem", 0); 209 | result = watchman_do_query(conn, test_dir, query, since, &error); 210 | ck_assert_msg(result != NULL, error.message); 211 | ck_assert_int_eq(1, result->nr); 212 | 213 | watchman_free_query(query); 214 | watchman_free_query_result(result); 215 | watchman_free_expression(since); 216 | 217 | ck_assert_msg(!watchman_watch_del(conn, test_dir, &error), error.message); 218 | watchman_connection_close(conn); 219 | } 220 | END_TEST 221 | 222 | START_TEST(test_watchman_misc) 223 | { 224 | struct watchman_error error; 225 | struct timeval tv_zero = {0}; 226 | struct watchman_connection *conn = watchman_connect(tv_zero, &error); 227 | ck_assert_msg(!watchman_watch(conn, test_dir, &error), error.message); 228 | 229 | struct watchman_expression *expressions[9]; 230 | expressions[0] = watchman_since_expression_time_t(0, 0); 231 | expressions[1] = watchman_since_expression_time_t(1, 0); 232 | expressions[2] = watchman_since_expression("c:123:45", 0); 233 | expressions[3] = watchman_exists_expression(); 234 | expressions[4] = 235 | watchman_not_expression(watchman_suffix_expression(".jsp")); 236 | expressions[5] = watchman_imatch_expression(".jsp", 0); 237 | 238 | const char *names[] = { "morx", "fleem" }; 239 | expressions[6] = watchman_names_expression(2, names, 0); 240 | expressions[7] = watchman_type_expression('D'); 241 | expressions[8] = watchman_true_expression(); 242 | 243 | struct watchman_expression *all; 244 | all = watchman_allof_expression(8, expressions); 245 | struct watchman_query_result *result = 246 | watchman_do_query(conn, test_dir, NULL, all, &error); 247 | ck_assert_msg(result != NULL, error.message); 248 | watchman_free_query_result(result); 249 | watchman_free_expression(all); 250 | 251 | ck_assert_msg(!watchman_watch_del(conn, test_dir, &error), error.message); 252 | watchman_connection_close(conn); 253 | } 254 | END_TEST 255 | 256 | Suite * 257 | watchman_suite(void) 258 | { 259 | Suite *s = suite_create("Tests"); 260 | 261 | /* Core test case */ 262 | TCase *tc_core = tcase_create("Core"); 263 | tcase_add_checked_fixture(tc_core, setup, teardown); 264 | tcase_add_test(tc_core, test_watchman_connect_timeout_fails); 265 | tcase_add_test(tc_core, test_watchman_connect_timeout_succeeds); 266 | tcase_add_test(tc_core, test_watchman_connect); 267 | tcase_add_test(tc_core, test_watchman_watch); 268 | tcase_add_test(tc_core, test_watchman_misc); 269 | suite_add_tcase(s, tc_core); 270 | 271 | return s; 272 | } 273 | 274 | int 275 | main(void) 276 | { 277 | int number_failed; 278 | Suite *s = watchman_suite(); 279 | SRunner *sr = srunner_create(s); 280 | srunner_run_all(sr, CK_NORMAL); 281 | number_failed = srunner_ntests_failed(sr); 282 | srunner_free(sr); 283 | return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; 284 | } 285 | -------------------------------------------------------------------------------- /tests/json2bser.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include "bser_write.h" 6 | 7 | int main(int argc, char* argv[]) { 8 | json_error_t error; 9 | #if JANSSON_VERSION_HEX >= 0x020600 10 | json_object_seed(1); /* make test results repeatable */ 11 | #endif 12 | json_t* root = json_loadf(stdin, 0, &error); 13 | if (root == NULL) { 14 | fprintf(stderr, "Parse error: %s\n at %s line %d col %d\n", 15 | error.text, error.source, error.line, error.column); 16 | } else { 17 | int ret = bser_write_to_file(root, stdout); 18 | fprintf(stderr, "Wrote %d bytes\n", ret); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tests/run_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FAIL=false 4 | 5 | make check || FAIL=true 6 | prove test*.sh || FAIL=true 7 | 8 | if [ "${FAIL}" = "true" ]; then 9 | echo "Some tests FAILED" 10 | exit 2 11 | fi 12 | 13 | exit 0 14 | -------------------------------------------------------------------------------- /tests/t/array_array.bser: -------------------------------------------------------------------------------- 1 | 0001030d00030200030203030304000300 2 | -------------------------------------------------------------------------------- /tests/t/array_array.json: -------------------------------------------------------------------------------- 1 | [ [ 3, 4 ], [ ] ] 2 | -------------------------------------------------------------------------------- /tests/t/array_deep.bser: -------------------------------------------------------------------------------- 1 | 000103140003010003010003010003010003010003010305 2 | -------------------------------------------------------------------------------- /tests/t/array_deep.json: -------------------------------------------------------------------------------- 1 | [ [ [ [ [ [ 5 ] ] ] ] ] ] 2 | -------------------------------------------------------------------------------- /tests/t/array_empty.bser: -------------------------------------------------------------------------------- 1 | 00010303000300 2 | -------------------------------------------------------------------------------- /tests/t/array_empty.json: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /tests/t/array_hemogenous.bser: -------------------------------------------------------------------------------- 1 | 000103110003040304020303666f6f010300000300 2 | -------------------------------------------------------------------------------- /tests/t/array_hemogenous.json: -------------------------------------------------------------------------------- 1 | [ 4, "foo", { }, [ ] ] 2 | -------------------------------------------------------------------------------- /tests/t/array_int.bser: -------------------------------------------------------------------------------- 1 | 0001030b0003040300030103020303 2 | -------------------------------------------------------------------------------- /tests/t/array_int.json: -------------------------------------------------------------------------------- 1 | [ 0, 1, 2, 3 ] 2 | -------------------------------------------------------------------------------- /tests/t/array_string.bser: -------------------------------------------------------------------------------- 1 | 00010315000303020303666f6f02030362617202030362617a 2 | -------------------------------------------------------------------------------- /tests/t/array_string.json: -------------------------------------------------------------------------------- 1 | [ "foo", "bar", "baz" ] 2 | -------------------------------------------------------------------------------- /tests/t/compact_array.bser: -------------------------------------------------------------------------------- 1 | 000104d4000b0003050203046e616d6502030a666f7263655f7573657202 2 | 030361676502030a6f636375706174696f6e020307776561706f6e730303 3 | 02030e4c756b6520536b7977616c6b65720803130203046a656469000303 4 | 02030a6c696768747361626572020307626c6173746572020305666f7263 5 | 6502030848616e20536f6c6f09031b02030b6e6572662068657264657200 6 | 0302020307626c6173746572020305736d69726b02030b4c656961204f72 7 | 67616e610903120203087072696e63657373000302020307626c61737465 8 | 72020303776974 9 | -------------------------------------------------------------------------------- /tests/t/compact_array.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Luke Skywalker", 4 | "age": 19, 5 | "force_user": true, 6 | "weapons": [ 7 | "lightsaber", 8 | "blaster", 9 | "force" 10 | ], 11 | "occupation": "jedi" 12 | }, 13 | { 14 | "name": "Han Solo", 15 | "age": 27, 16 | "force_user": false, 17 | "weapons": [ 18 | "blaster", 19 | "smirk" 20 | ], 21 | "occupation": "nerf herder" 22 | }, 23 | { 24 | "name": "Leia Organa", 25 | "age": 18, 26 | "force_user": false, 27 | "weapons": [ 28 | "blaster", 29 | "wit" 30 | ], 31 | "occupation": "princess" 32 | } 33 | ] 34 | -------------------------------------------------------------------------------- /tests/t/compact_array_nuls.bser: -------------------------------------------------------------------------------- 1 | 000104c3000b0003060203046e616d6502030a666f7263655f7573657202 2 | 030361676502030a6f636375706174696f6e020307776561706f6e730203 3 | 0667656e646572030302030e4c756b6520536b7977616c6b657208031302 4 | 03046a65646900030302030a6c696768747361626572020307626c617374 5 | 6572020305666f7263650c02030848616e20536f6c6f09031b0c00030202 6 | 0307626c6173746572020305736d69726b0c02030b4c656961204f726761 7 | 6e610c0c0203087072696e636573730c02030166 8 | -------------------------------------------------------------------------------- /tests/t/compact_array_nuls.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name" : "Luke Skywalker", 4 | "age" : 19, 5 | "force_user" : true, 6 | "weapons" : 7 | [ "lightsaber", "blaster", "force" ], 8 | "occupation" : "jedi" 9 | }, 10 | { 11 | "name" : "Han Solo", 12 | "age" : 27, 13 | "force_user" : false, 14 | "weapons" : [ "blaster", "smirk" ] 15 | }, 16 | { 17 | "name" : "Leia Organa", 18 | "gender" : "f", 19 | "occupation" : "princess" 20 | } 21 | ] 22 | -------------------------------------------------------------------------------- /tests/t/false.bser: -------------------------------------------------------------------------------- 1 | 0001030400030109 2 | -------------------------------------------------------------------------------- /tests/t/false.json: -------------------------------------------------------------------------------- 1 | [ false ] 2 | -------------------------------------------------------------------------------- /tests/t/int_huge.bser: -------------------------------------------------------------------------------- 1 | 0001030c000301060605040301000000 2 | -------------------------------------------------------------------------------- /tests/t/int_huge.json: -------------------------------------------------------------------------------- 1 | [ 4345562374 ] 2 | -------------------------------------------------------------------------------- /tests/t/int_large.bser: -------------------------------------------------------------------------------- 1 | 000103080003010506050403 2 | -------------------------------------------------------------------------------- /tests/t/int_large.json: -------------------------------------------------------------------------------- 1 | [ 50595078 ] 2 | -------------------------------------------------------------------------------- /tests/t/int_neg.bser: -------------------------------------------------------------------------------- 1 | 0001030500030103fd 2 | -------------------------------------------------------------------------------- /tests/t/int_neg.json: -------------------------------------------------------------------------------- 1 | [ -3 ] 2 | -------------------------------------------------------------------------------- /tests/t/int_short.bser: -------------------------------------------------------------------------------- 1 | 00010306000301040201 2 | -------------------------------------------------------------------------------- /tests/t/int_short.json: -------------------------------------------------------------------------------- 1 | [ 258 ] 2 | -------------------------------------------------------------------------------- /tests/t/int_short_neg.bser: -------------------------------------------------------------------------------- 1 | 000103060003010438fe 2 | -------------------------------------------------------------------------------- /tests/t/int_short_neg.json: -------------------------------------------------------------------------------- 1 | [ -456 ] 2 | -------------------------------------------------------------------------------- /tests/t/int_small.bser: -------------------------------------------------------------------------------- 1 | 00010305000301030c 2 | -------------------------------------------------------------------------------- /tests/t/int_small.json: -------------------------------------------------------------------------------- 1 | [ 12 ] 2 | -------------------------------------------------------------------------------- /tests/t/int_small_signbit.bser: -------------------------------------------------------------------------------- 1 | 0001030600030104fd00 2 | -------------------------------------------------------------------------------- /tests/t/int_small_signbit.json: -------------------------------------------------------------------------------- 1 | [ 253 ] 2 | -------------------------------------------------------------------------------- /tests/t/null.bser: -------------------------------------------------------------------------------- 1 | 000103040003010a 2 | -------------------------------------------------------------------------------- /tests/t/null.json: -------------------------------------------------------------------------------- 1 | [ null ] 2 | -------------------------------------------------------------------------------- /tests/t/object.bser: -------------------------------------------------------------------------------- 1 | 00010317010302020303666f6f020303667070020303667171030a 2 | -------------------------------------------------------------------------------- /tests/t/object.json: -------------------------------------------------------------------------------- 1 | { "foo" : "fpp", "fqq" : 10 } 2 | -------------------------------------------------------------------------------- /tests/t/object_big.bser: -------------------------------------------------------------------------------- 1 | 00010350010306020303666f6f0003030301042001030402030362617201 2 | 030102030362617a02030462726f73020304626f7a6f080203037a6f6f02 3 | 03046b65657002030562726f7a6f0a020304626f6e6f032a 4 | -------------------------------------------------------------------------------- /tests/t/object_big.json: -------------------------------------------------------------------------------- 1 | { 2 | "foo" : [ 1, 288, 4 ], 3 | "bar" : { "baz" : "bros" }, 4 | "zoo" : "keep", 5 | "bozo" : true, 6 | "brozo" : null, 7 | "bono" : 42 8 | } 9 | -------------------------------------------------------------------------------- /tests/t/object_empty.bser: -------------------------------------------------------------------------------- 1 | 00010303010300 2 | -------------------------------------------------------------------------------- /tests/t/object_empty.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /tests/t/real.bser: -------------------------------------------------------------------------------- 1 | 0001030c00030107182d4454fb210940 2 | -------------------------------------------------------------------------------- /tests/t/real.json: -------------------------------------------------------------------------------- 1 | [ 3.1415926535897931 ] 2 | -------------------------------------------------------------------------------- /tests/t/string_medium.bser: -------------------------------------------------------------------------------- 1 | 000104970100030102049001303132333435363738393031323334353637 2 | 383930313233343536373839303132333435363738393031323334353637 3 | 383930313233343536373839303132333435363738393031323334353637 4 | 383930313233343536373839303132333435363738393031323334353637 5 | 383930313233343536373839303132333435363738393031323334353637 6 | 383930313233343536373839303132333435363738393031323334353637 7 | 383930313233343536373839303132333435363738393031323334353637 8 | 383930313233343536373839303132333435363738393031323334353637 9 | 383930313233343536373839303132333435363738393031323334353637 10 | 383930313233343536373839303132333435363738393031323334353637 11 | 383930313233343536373839303132333435363738393031323334353637 12 | 383930313233343536373839303132333435363738393031323334353637 13 | 383930313233343536373839303132333435363738393031323334353637 14 | 38393031323334353637383930313233343536373839 15 | -------------------------------------------------------------------------------- /tests/t/string_medium.json: -------------------------------------------------------------------------------- 1 | [ "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" ] 2 | -------------------------------------------------------------------------------- /tests/t/string_small.bser: -------------------------------------------------------------------------------- 1 | 00010309000301020303666f6f 2 | -------------------------------------------------------------------------------- /tests/t/string_small.json: -------------------------------------------------------------------------------- 1 | [ "foo" ] 2 | -------------------------------------------------------------------------------- /tests/t/true.bser: -------------------------------------------------------------------------------- 1 | 0001030400030108 2 | -------------------------------------------------------------------------------- /tests/t/true.json: -------------------------------------------------------------------------------- 1 | [ true ] 2 | -------------------------------------------------------------------------------- /tests/testBserEncoding.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o nounset 4 | set -o errexit 5 | 6 | TEST_DIR=t 7 | 8 | TESTS=$(cd ${TEST_DIR}; ls -1 *.json) 9 | NUM_TESTS=$(echo ${TESTS} | wc -w | awk '{print $1}') 10 | 11 | echo "1..${NUM_TESTS}" 12 | 13 | for json in ${TESTS}; do 14 | OUT=${json%.*}.bser.out 15 | GOLD=${TEST_DIR}/${json%.*}.bser 16 | if [ ! -f ${GOLD} ]; then 17 | echo "not ok -- Missing gold result file: ${GOLD}" 18 | else 19 | ./json2bser < ${TEST_DIR}/${json} 2>/dev/null | xxd -ps > ${OUT} 20 | RESULT=$(diff ${OUT} ${GOLD} 2>&1 >/dev/null || echo "fail") 21 | if [ "$RESULT" = "fail" ]; then 22 | echo "not ok -- result did not match gold file: ${GOLD}" 23 | else 24 | echo "ok" 25 | rm ${OUT} 26 | fi 27 | fi 28 | done 29 | -------------------------------------------------------------------------------- /tests/testBserParsing.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o nounset 4 | set -o errexit 5 | 6 | mktight() { 7 | local readonly in=$1 8 | local readonly out=$2 9 | 10 | cat ${in} | tr '\n\t' ' ' | sed -e 's/ //g' > ${out} 11 | } 12 | 13 | TEST_DIR=t 14 | 15 | TESTS=$(cd ${TEST_DIR}; ls -1 *.bser) 16 | NUM_TESTS=$(echo ${TESTS} | wc -w | awk '{print $1}') 17 | 18 | echo "1..${NUM_TESTS}" 19 | 20 | for bser in ${TESTS}; do 21 | OUT=${bser%.*}.json.out 22 | GOLD=${TEST_DIR}/${bser%.*}.json 23 | if [ ! -f ${GOLD} ]; then 24 | echo "not ok -- Missing gold result file: ${GOLD}" 25 | else 26 | xxd -r -p ${TEST_DIR}/${bser} |./bser2json 2>/dev/null > ${OUT} 27 | mktight ${OUT} ${OUT}.tight 28 | mktight ${GOLD} ${GOLD}.tight 29 | RESULT=$(diff -w ${OUT}.tight ${GOLD}.tight 2>&1 >/dev/null || echo "fail") 30 | rm -f ${OUT}.tight ${GOLD}.tight 31 | if [ "$RESULT" = "fail" ]; then 32 | echo "not ok -- result did not match gold file: ${GOLD}" 33 | else 34 | echo "ok" 35 | rm ${OUT} 36 | fi 37 | fi 38 | done 39 | -------------------------------------------------------------------------------- /watchman.c: -------------------------------------------------------------------------------- 1 | #include "watchman.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | #include "bser_write.h" 20 | #include "proto.h" 21 | 22 | static int use_bser_encoding = 0; 23 | static FILE* error_handle = NULL; 24 | 25 | /* It's safe to have a small buffer here because watchman's socket name 26 | * is guaranteed to be under 108 bytes (see sockaddr_un). The JSON only has 27 | * sockname and version fields. 28 | */ 29 | #define WATCHMAN_GET_SOCKNAME_MAX 1024 30 | 31 | void 32 | watchman_set_error_handle(FILE* fp) { 33 | error_handle = fp; 34 | } 35 | 36 | static void do_output(const char* category, const char *msg, va_list params) { 37 | if (error_handle != NULL) { 38 | fprintf(error_handle, "%s: ", category); 39 | vfprintf(error_handle, msg, params); 40 | fprintf(error_handle, "\n"); 41 | } 42 | } 43 | 44 | static void trace(const char *msg, ...) { 45 | if (getenv("LIBWATCHMAN_TRACE_WATCHMAN") != NULL) { 46 | va_list params; 47 | va_start(params, msg); 48 | do_output("trace", msg, params); 49 | va_end(params); 50 | } 51 | } 52 | 53 | static void warning(const char* msg, ...) { 54 | va_list params; 55 | va_start(params, msg); 56 | do_output("warning", msg, params); 57 | va_end(params); 58 | } 59 | 60 | static void error(const char* msg, ...) { 61 | va_list params; 62 | va_start(params, msg); 63 | do_output("error", msg, params); 64 | va_end(params); 65 | } 66 | 67 | 68 | static void watchman_err(struct watchman_error *error, 69 | enum watchman_error_code code, 70 | const char *message, ...) 71 | __attribute__ ((format(printf, 3, 4))); 72 | 73 | static void 74 | watchman_err(struct watchman_error *error, enum watchman_error_code code, 75 | const char *message, ...) 76 | { 77 | if (!error) 78 | return; 79 | va_list argptr; 80 | va_start(argptr, message); 81 | char c; 82 | int len = vsnprintf(&c, 1, message, argptr); 83 | warning(message, argptr); 84 | va_end(argptr); 85 | 86 | error->message = malloc(len + 1); 87 | error->code = code; 88 | error->err_no = errno; 89 | va_start(argptr, message); 90 | vsnprintf(error->message, len + 1, message, argptr); 91 | va_end(argptr); 92 | } 93 | 94 | static int unix_stream_socket(void) 95 | { 96 | int fd = socket(AF_UNIX, SOCK_STREAM, 0); 97 | return fd; 98 | } 99 | 100 | static int chdir_len(const char *orig, int len) 101 | { 102 | char *path = malloc(len + 1); 103 | memcpy(path, orig, len); 104 | path[len] = 0; 105 | int r = chdir(path); 106 | free(path); 107 | return r; 108 | } 109 | 110 | struct unix_sockaddr_context { 111 | char *orig_dir; 112 | }; 113 | 114 | static char *getcwd_alloc(void) 115 | { 116 | int buflen = PATH_MAX; 117 | char *buf = NULL; 118 | 119 | while (1) { 120 | buf = realloc(buf, buflen); 121 | if (getcwd(buf, buflen)) { 122 | break; 123 | } 124 | 125 | if (errno != ERANGE) { 126 | return NULL; 127 | } 128 | buflen *= 2; 129 | } 130 | 131 | return buf; 132 | } 133 | 134 | static int unix_sockaddr_init(struct sockaddr_un *sa, const char *path, 135 | struct unix_sockaddr_context *ctx) 136 | { 137 | size_t size = strlen(path) + 1; 138 | 139 | ctx->orig_dir = NULL; 140 | if (size > sizeof(sa->sun_path)) { 141 | const char *slash = strrchr(path, '/'); 142 | const char *dir; 143 | 144 | if (!slash) { 145 | errno = ENAMETOOLONG; 146 | return -1; 147 | } 148 | 149 | dir = path; 150 | path = slash + 1; 151 | size = strlen(path) + 1; 152 | if (size > sizeof(sa->sun_path)) { 153 | errno = ENAMETOOLONG; 154 | return -1; 155 | } 156 | char *cwd = getcwd_alloc(); 157 | if (!cwd) { 158 | return -1; 159 | } 160 | ctx->orig_dir = cwd; 161 | if (chdir_len(dir, slash - dir) < 0) { 162 | free(cwd); 163 | return -1; 164 | } 165 | } 166 | 167 | memset(sa, 0, sizeof(*sa)); 168 | sa->sun_family = AF_UNIX; 169 | memcpy(sa->sun_path, path, size); 170 | return 0; 171 | } 172 | 173 | static int unix_sockaddr_cleanup(struct unix_sockaddr_context *ctx, struct watchman_error *error) 174 | { 175 | int ret = 0; 176 | if (!ctx->orig_dir) { 177 | return 0; 178 | } 179 | /* 180 | * If we fail, we have moved the cwd of the whole process, which 181 | * could confuse calling code. But we don't want to just die, because 182 | * libraries shouldn't do that. So we'll return an error but be 183 | * sad about it. 184 | */ 185 | if (chdir(ctx->orig_dir) < 0) { 186 | watchman_err(error, WATCHMAN_ERR_CWD, 187 | "unable to restore original working directory"); 188 | ret = -1; 189 | } 190 | free(ctx->orig_dir); 191 | return ret; 192 | } 193 | 194 | static int unix_stream_connect(const char *path, struct watchman_error *error) 195 | { 196 | struct sockaddr_un sa; 197 | struct unix_sockaddr_context ctx; 198 | 199 | if (unix_sockaddr_init(&sa, path, &ctx) < 0) { 200 | return -1; 201 | } 202 | int fd = unix_stream_socket(); 203 | if (fd < 0) { 204 | return -1; 205 | } 206 | if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) { 207 | watchman_err(error, WATCHMAN_ERR_CONNECT, "Connect error %s", 208 | strerror(errno)); 209 | 210 | if (unix_sockaddr_cleanup(&ctx, error)) { 211 | error->code |= WATCHMAN_ERR_CWD; 212 | } 213 | close(fd); 214 | return -1; 215 | } 216 | if (unix_sockaddr_cleanup(&ctx, error)) { 217 | close(fd); 218 | return -1; 219 | } 220 | return fd; 221 | } 222 | 223 | static struct watchman_connection * 224 | watchman_sock_connect(const char *sockname, struct timeval timeout, struct watchman_error *error) 225 | { 226 | use_bser_encoding = getenv("LIBWATCHMAN_USE_JSON_PROTOCOL") == NULL; 227 | trace("Using bser encoding: %s", use_bser_encoding ? "yes" : "no"); 228 | 229 | int fd; 230 | 231 | error->message = NULL; 232 | fd = unix_stream_connect(sockname, error); 233 | if (fd < 0 && !error->message) { 234 | watchman_err(error, WATCHMAN_ERR_CONNECT, "Connect error %s", 235 | strerror(errno)); 236 | return NULL; 237 | } 238 | 239 | if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout))) { 240 | watchman_err(error, WATCHMAN_ERR_CONNECT, "Failed to set timeout %s", 241 | strerror(errno)); 242 | return NULL; 243 | } 244 | 245 | if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout))) { 246 | watchman_err(error, WATCHMAN_ERR_CONNECT, "Failed to set timeout %s", 247 | strerror(errno)); 248 | return NULL; 249 | } 250 | 251 | FILE *sockfp = fdopen(fd, "r+"); 252 | if (!sockfp) { 253 | close(fd); 254 | watchman_err(error, WATCHMAN_ERR_OTHER, 255 | "Failed to connect to watchman socket %s: %s.", 256 | sockname, strerror(errno)); 257 | return NULL; 258 | } 259 | setlinebuf(sockfp); 260 | 261 | struct watchman_connection *conn = malloc(sizeof(*conn)); 262 | conn->fp = sockfp; 263 | return conn; 264 | } 265 | 266 | struct watchman_popen { 267 | int fd; 268 | int pid; 269 | }; 270 | 271 | #define WATCHMAN_EXEC_FAILED 241 272 | #define WATCHMAN_EXEC_INTERNAL_ERROR 242 273 | 274 | static const char* get_sockname_msg = "Could not run watchman get-sockname: %s"; 275 | /* Runs watchman get-sockname and returns a FILE from which the output 276 | can be read. */ 277 | static struct watchman_popen *watchman_popen_getsockname(struct watchman_error *err) 278 | { 279 | int pipefd[2]; 280 | static struct watchman_popen ret = {0, 0}; 281 | 282 | if (pipe(pipefd) < 0) { 283 | goto fail; 284 | } 285 | 286 | pid_t pid = fork(); 287 | if (pid < 0) { 288 | goto fail; 289 | } else if (pid == 0) { 290 | if (dup2(pipefd[1], 1) < 0) { 291 | error("Could not dup pipe"); 292 | exit(WATCHMAN_EXEC_INTERNAL_ERROR); 293 | } 294 | 295 | int devnull_fh = open("/dev/null", O_RDWR); 296 | if (devnull_fh < 0) { 297 | error("Could not open /dev/null"); 298 | exit(WATCHMAN_EXEC_INTERNAL_ERROR); 299 | } 300 | 301 | if (dup2(devnull_fh, 2) < 0) { 302 | error("Could not dup2 /dev/null"); 303 | exit(WATCHMAN_EXEC_INTERNAL_ERROR); 304 | } 305 | 306 | execlp("watchman", "watchman", "get-sockname", (char *) NULL); 307 | error("execlp failed"); 308 | exit(WATCHMAN_EXEC_FAILED); 309 | } else { 310 | close(pipefd[1]); 311 | ret.fd = pipefd[0]; 312 | ret.pid = pid; 313 | return &ret; 314 | } 315 | 316 | fail: 317 | watchman_err(err, WATCHMAN_ERR_OTHER, get_sockname_msg, strerror(errno)); 318 | return NULL; 319 | } 320 | 321 | int watchman_pclose(struct watchman_error *error, struct watchman_popen *popen) 322 | { 323 | close(popen->fd); 324 | 325 | int status; 326 | int pid = waitpid(popen->pid, &status, 0); 327 | if (pid < 0) { 328 | watchman_err(error, WATCHMAN_ERR_RUN_WATCHMAN, get_sockname_msg, 329 | strerror(errno)); 330 | return -1; 331 | } 332 | 333 | switch(WEXITSTATUS(status)) { 334 | case 0: 335 | return 0; 336 | case WATCHMAN_EXEC_FAILED: 337 | watchman_err(error, WATCHMAN_ERR_RUN_WATCHMAN, get_sockname_msg, 338 | strerror(errno)); 339 | return -1; 340 | case WATCHMAN_EXEC_INTERNAL_ERROR: 341 | watchman_err(error, WATCHMAN_ERR_OTHER, get_sockname_msg, 342 | strerror(errno)); 343 | return -1; 344 | default: 345 | watchman_err(error, WATCHMAN_ERR_WATCHMAN_BROKEN, get_sockname_msg, 346 | strerror(errno)); 347 | return -1; 348 | } 349 | } 350 | 351 | /** 352 | * Calls select() on the given fd until it's ready to read from or 353 | * until the timeout expires. Updates timeout to indicate the 354 | * remaining time. Returns 1 if the socket is readable, 0 if the 355 | * socket is not readable, and -1 on error. 356 | */ 357 | static int block_on_read(int fd, struct timeval *timeout) 358 | { 359 | struct timeval now, start, elapsed, orig_timeout; 360 | fd_set fds; 361 | int ret = 0; 362 | 363 | FD_ZERO(&fds); 364 | FD_SET(fd, &fds); 365 | 366 | if (timeout != NULL) { 367 | gettimeofday(&start, NULL); 368 | memcpy(&orig_timeout, timeout, sizeof(orig_timeout)); 369 | } 370 | 371 | ret = select(fd + 1, &fds, NULL, NULL, timeout); 372 | 373 | if (timeout != NULL) { 374 | gettimeofday(&now, NULL); 375 | timersub(&now, &start, &elapsed); 376 | timersub(&orig_timeout, &elapsed, timeout); 377 | } 378 | 379 | return ret; 380 | } 381 | 382 | /* 383 | * Read from fd into buf until either `bytes` bytes have been read, or 384 | * EOF, or the data that has been read can be parsed as JSON. In the 385 | * event of a timeout or read error, returns NULL. 386 | */ 387 | static proto_t read_with_timeout(int fd, char* buf, size_t bytes, struct timeval timeout) 388 | { 389 | size_t read_so_far = 0; 390 | ssize_t bytes_read = 0; 391 | 392 | while (read_so_far < bytes) { 393 | if (timeout.tv_sec || timeout.tv_usec) { 394 | if (timeout.tv_sec < 0 || timeout.tv_usec < 0) { 395 | /* Timeout */ 396 | return proto_null(); 397 | } 398 | 399 | if (1 == block_on_read(fd, &timeout)) { 400 | bytes_read = read(fd, buf, bytes - read_so_far); 401 | } else { 402 | return proto_null(); /* timeout or error */ 403 | } 404 | } else { 405 | bytes_read = read(fd, buf, bytes - read_so_far); 406 | } 407 | if (bytes_read < 0) { 408 | return proto_null(); 409 | } 410 | if (bytes_read == 0) { 411 | /* EOF, but we couldn't parse the JSON we have so far */ 412 | return proto_null(); 413 | } 414 | read_so_far += bytes_read; 415 | 416 | /* try to parse this */ 417 | buf[read_so_far] = 0; 418 | proto_t proto; 419 | if (buf[0] <= 0x0b) { 420 | bser_t* bser = bser_parse_buffer((uint8_t*)buf, read_so_far, NULL); 421 | proto = proto_from_bser(bser); 422 | } else { 423 | json_error_t jerror; 424 | json_t* json = json_loads(buf, JSON_DISABLE_EOF_CHECK, &jerror); 425 | proto = proto_from_json(json); 426 | } 427 | return proto; 428 | } 429 | return proto_null(); 430 | } 431 | 432 | /* 433 | * Connect to watchman's socket. Sets a socket send and receive 434 | * timeout of `timeout`. Pass a {0} for no-timeout. On error, 435 | * returns NULL and, if `error` is non-NULL, fills it in. 436 | */ 437 | struct watchman_connection * 438 | watchman_connect(struct timeval timeout, struct watchman_error *error) 439 | { 440 | struct watchman_connection *conn = NULL; 441 | /* If an environment variable WATCHMAN_SOCK is set, establish a connection 442 | to that address. Otherwise, run `watchman get-sockname` to start the 443 | daemon and retrieve its address. */ 444 | const char *sockname_env = getenv("WATCHMAN_SOCK"); 445 | if (sockname_env) { 446 | conn = watchman_sock_connect(sockname_env, timeout, error); 447 | goto done; 448 | } 449 | struct watchman_popen *p = watchman_popen_getsockname(error); 450 | if (p == NULL) { 451 | return NULL; 452 | } 453 | 454 | char buf[WATCHMAN_GET_SOCKNAME_MAX + 1]; 455 | 456 | proto_t proto = read_with_timeout(p->fd, buf, WATCHMAN_GET_SOCKNAME_MAX, timeout); 457 | 458 | if (watchman_pclose(error, p)) { 459 | goto done; 460 | } 461 | if (proto_is_null(proto)) { 462 | watchman_err(error, WATCHMAN_ERR_WATCHMAN_BROKEN, 463 | "Got bad or no JSON/BSER from watchman get-sockname"); 464 | goto done; 465 | } 466 | if (!proto_is_object(proto)) { 467 | watchman_err(error, WATCHMAN_ERR_WATCHMAN_BROKEN, 468 | "Got bad JSON/BSER from watchman get-sockname: object expected"); 469 | goto bad_proto; 470 | } 471 | proto_t sockname_obj = proto_object_get(proto, "sockname"); 472 | if (proto_is_null(sockname_obj)) { 473 | watchman_err(error, WATCHMAN_ERR_WATCHMAN_BROKEN, 474 | "Got bad JSON/BSER from watchman get-sockname: " 475 | "sockname element expected"); 476 | goto bad_proto; 477 | } 478 | if (!proto_is_string(sockname_obj)) { 479 | watchman_err(error, WATCHMAN_ERR_WATCHMAN_BROKEN, 480 | "Got bad JSON/BSER from watchman get-sockname:" 481 | " sockname is not string"); 482 | goto bad_proto; 483 | } 484 | const char *sockname = proto_strdup(sockname_obj); 485 | conn = watchman_sock_connect(sockname, timeout, error); 486 | bad_proto: 487 | proto_free(proto); 488 | done: 489 | return conn; 490 | } 491 | 492 | static int 493 | watchman_send_simple_command(struct watchman_connection *conn, 494 | struct watchman_error *error, ...) 495 | { 496 | int result = 0; 497 | json_t *cmd_array = json_array(); 498 | va_list argptr; 499 | va_start(argptr, error); 500 | char *arg; 501 | while ((arg = va_arg(argptr, char *))) { 502 | json_array_append_new(cmd_array, json_string(arg)); 503 | } 504 | if (use_bser_encoding) { 505 | result = bser_write_to_file(cmd_array, conn->fp) == 0; 506 | } else { 507 | result = json_dumpf(cmd_array, conn->fp, JSON_COMPACT); 508 | fputc('\n', conn->fp); 509 | } 510 | if (result) { 511 | if (errno == EAGAIN || errno == EWOULDBLOCK) { 512 | watchman_err(error, WATCHMAN_ERR_TIMEOUT, 513 | "Timeout sending simple watchman command"); 514 | } else { 515 | watchman_err(error, WATCHMAN_ERR_WATCHMAN_BROKEN, 516 | "Failed to send simple watchman command"); 517 | } 518 | result = 1; 519 | } 520 | 521 | json_decref(cmd_array); 522 | return result; 523 | } 524 | 525 | static proto_t 526 | watchman_read_with_timeout(struct watchman_connection *conn, struct timeval *timeout, struct watchman_error *error) 527 | { 528 | proto_t result; 529 | json_error_t jerror; 530 | 531 | int ret = 1; 532 | 533 | if (!timeout || timeout->tv_sec || timeout->tv_usec) 534 | ret = block_on_read(fileno(conn->fp), timeout); 535 | if (ret == -1) { 536 | watchman_err(error, WATCHMAN_ERR_WATCHMAN_BROKEN, 537 | "Error encountered blocking on watchman"); 538 | return proto_null(); 539 | } 540 | 541 | if (ret != 1) { 542 | watchman_err(error, WATCHMAN_ERR_TIMEOUT, 543 | "timed out waiting for watchman"); 544 | return proto_null(); 545 | } 546 | 547 | if (use_bser_encoding) { 548 | bser_t* bser = bser_parse_from_file(conn->fp, NULL); 549 | result = proto_from_bser(bser); 550 | } else { 551 | json_t* json = json_loadf(conn->fp, JSON_DISABLE_EOF_CHECK, &jerror); 552 | result = proto_from_json(json); 553 | if (fgetc(conn->fp) != '\n') { 554 | if (errno == EAGAIN || errno == EWOULDBLOCK) { 555 | watchman_err(error, WATCHMAN_ERR_TIMEOUT, 556 | "Timeout reading EOL from watchman"); 557 | } else { 558 | watchman_err(error, WATCHMAN_ERR_WATCHMAN_BROKEN, 559 | "No newline at end of reply"); 560 | } 561 | json_decref(json); 562 | return proto_null(); 563 | } 564 | } 565 | if (proto_is_null(result)) { 566 | if (errno == EAGAIN) { 567 | watchman_err(error, WATCHMAN_ERR_TIMEOUT, 568 | "Timeout:EAGAIN reading from watchman."); 569 | } 570 | else if (errno == EWOULDBLOCK) { 571 | watchman_err(error, WATCHMAN_ERR_TIMEOUT, 572 | "Timeout:EWOULDBLOCK reading from watchman"); 573 | } else { 574 | watchman_err(error, WATCHMAN_ERR_WATCHMAN_BROKEN, 575 | "Can't parse result from watchman: %s", 576 | jerror.text); 577 | } 578 | return proto_null(); 579 | } 580 | return result; 581 | } 582 | 583 | static proto_t 584 | watchman_read(struct watchman_connection *conn, struct watchman_error *error) 585 | { 586 | return watchman_read_with_timeout(conn, NULL, error); 587 | } 588 | 589 | static int 590 | watchman_read_and_handle_errors(struct watchman_connection *conn, 591 | struct watchman_error *error) 592 | { 593 | proto_t obj = watchman_read(conn, error); 594 | if (proto_is_null(obj)) { 595 | return 1; 596 | } 597 | if (!proto_is_object(obj)) { 598 | char *bogus_text = proto_dumps(obj, 0); 599 | watchman_err(error, WATCHMAN_ERR_WATCHMAN_BROKEN, 600 | "Got non-object result from watchman : %s", 601 | bogus_text); 602 | free(bogus_text); 603 | proto_free(obj); 604 | return 1; 605 | } 606 | proto_t error_node = proto_object_get(obj, "error"); 607 | if (!proto_is_null(error_node)) { 608 | watchman_err(error, WATCHMAN_ERR_OTHER, 609 | "Got error result from watchman : %s", 610 | proto_strdup(error_node)); 611 | proto_free(obj); 612 | return 1; 613 | } 614 | 615 | proto_free(obj); 616 | return 0; 617 | } 618 | 619 | int 620 | watchman_watch(struct watchman_connection *conn, 621 | const char *path, struct watchman_error *error) 622 | { 623 | if (watchman_send_simple_command(conn, error, "watch", path, NULL)) { 624 | return 1; 625 | } 626 | if (watchman_read_and_handle_errors(conn, error)) { 627 | return 1; 628 | } 629 | return 0; 630 | } 631 | 632 | int 633 | watchman_recrawl(struct watchman_connection *conn, 634 | const char *path, struct watchman_error *error) 635 | { 636 | if (watchman_send_simple_command(conn, error, "debug-recrawl", path, NULL)) { 637 | return 1; 638 | } 639 | if (watchman_read_and_handle_errors(conn, error)) { 640 | return 1; 641 | } 642 | return 0; 643 | } 644 | 645 | int 646 | watchman_watch_del(struct watchman_connection *conn, 647 | const char *path, struct watchman_error *error) 648 | { 649 | if (watchman_send_simple_command(conn, error, "watch-del", path, NULL)) { 650 | return 1; 651 | } 652 | if (watchman_read_and_handle_errors(conn, error)) { 653 | return 1; 654 | } 655 | return 0; 656 | } 657 | 658 | static struct watchman_expression * 659 | alloc_expr(enum watchman_expression_type ty) 660 | { 661 | struct watchman_expression *expr; 662 | expr = calloc(1, sizeof(*expr)); 663 | expr->ty = ty; 664 | return expr; 665 | } 666 | 667 | struct watchman_expression * 668 | watchman_since_expression(const char *since, enum watchman_clockspec spec) 669 | { 670 | assert(since); 671 | struct watchman_expression *expr = alloc_expr(WATCHMAN_EXPR_TY_SINCE); 672 | expr->e.since_expr.is_str = 1; 673 | expr->e.since_expr.t.since = strdup(since); 674 | expr->e.since_expr.clockspec = spec; 675 | return expr; 676 | } 677 | 678 | struct watchman_expression * 679 | watchman_since_expression_time_t(time_t time, enum watchman_clockspec 680 | spec) 681 | { 682 | struct watchman_expression *expr = alloc_expr(WATCHMAN_EXPR_TY_SINCE); 683 | expr->e.since_expr.is_str = 0; 684 | expr->e.since_expr.t.time = time; 685 | expr->e.since_expr.clockspec = spec; 686 | return expr; 687 | } 688 | 689 | /* corresponds to enum watchman_expression_type */ 690 | static char *ty_str[] = { 691 | "allof", 692 | "anyof", 693 | "not", 694 | "true", 695 | "false", 696 | "since", 697 | "suffix", 698 | "match", 699 | "imatch", 700 | "pcre", 701 | "ipcre", 702 | "name", 703 | "iname", 704 | "type", 705 | "empty", 706 | "exists" 707 | }; 708 | 709 | /* corresponds to enum watchman_clockspec */ 710 | static char *clockspec_str[] = { 711 | NULL, 712 | "oclock", 713 | "cclock", 714 | "mtime", 715 | "ctime" 716 | }; 717 | 718 | /* corresponds to enum watchman_basename */ 719 | static char *basename_str[] = { 720 | NULL, 721 | "basename", 722 | "wholename" 723 | }; 724 | 725 | static json_t * 726 | json_string_from_char(char c) 727 | { 728 | char str[2] = { c, 0 }; 729 | return json_string(str); 730 | } 731 | 732 | static json_t * 733 | json_string_or_array(int nr, char **items) 734 | { 735 | if (nr == 1) { 736 | return json_string(items[0]); 737 | } 738 | json_t *result = json_array(); 739 | int i; 740 | for (i = 0; i < nr; ++i) { 741 | json_array_append_new(result, json_string(items[i])); 742 | } 743 | return result; 744 | } 745 | 746 | static void 747 | since_to_json(json_t *result, const struct watchman_expression *expr) 748 | { 749 | if (expr->e.since_expr.is_str) { 750 | json_array_append_new(result, json_string(expr->e.since_expr.t.since)); 751 | } else { 752 | json_array_append_new(result, json_integer(expr->e.since_expr.t.time)); 753 | } 754 | if (expr->e.since_expr.clockspec) { 755 | char *clockspec = clockspec_str[expr->e.since_expr.clockspec]; 756 | json_array_append_new(result, json_string(clockspec)); 757 | } 758 | } 759 | 760 | static json_t * 761 | to_json(const struct watchman_expression *expr) 762 | { 763 | json_t *result = json_array(); 764 | json_t *arg; 765 | json_array_append_new(result, json_string(ty_str[expr->ty])); 766 | 767 | int i; 768 | switch (expr->ty) { 769 | case WATCHMAN_EXPR_TY_ALLOF: 770 | /*-fallthrough*/ 771 | case WATCHMAN_EXPR_TY_ANYOF: 772 | for (i = 0; i < expr->e.union_expr.nr; ++i) { 773 | json_array_append_new(result, 774 | to_json(expr->e.union_expr.clauses[i])); 775 | } 776 | break; 777 | case WATCHMAN_EXPR_TY_NOT: 778 | json_array_append_new(result, to_json(expr->e.not_expr.clause)); 779 | break; 780 | case WATCHMAN_EXPR_TY_TRUE: 781 | /*-fallthrough*/ 782 | case WATCHMAN_EXPR_TY_FALSE: 783 | /*-fallthrough*/ 784 | case WATCHMAN_EXPR_TY_EMPTY: 785 | /*-fallthrough*/ 786 | case WATCHMAN_EXPR_TY_EXISTS: 787 | /* Nothing to do */ 788 | break; 789 | 790 | case WATCHMAN_EXPR_TY_SINCE: 791 | since_to_json(result, expr); 792 | break; 793 | case WATCHMAN_EXPR_TY_SUFFIX: 794 | json_array_append_new(result, 795 | json_string(expr->e.suffix_expr.suffix)); 796 | break; 797 | case WATCHMAN_EXPR_TY_MATCH: 798 | /*-fallthrough*/ 799 | case WATCHMAN_EXPR_TY_IMATCH: 800 | /*-fallthrough*/ 801 | case WATCHMAN_EXPR_TY_PCRE: 802 | /*-fallthrough*/ 803 | case WATCHMAN_EXPR_TY_IPCRE: 804 | json_array_append_new(result, 805 | json_string(expr->e.match_expr.match)); 806 | if (expr->e.match_expr.basename) { 807 | char *base = basename_str[expr->e.match_expr.basename]; 808 | json_array_append_new(result, json_string(base)); 809 | } 810 | break; 811 | case WATCHMAN_EXPR_TY_NAME: 812 | /*-fallthrough*/ 813 | case WATCHMAN_EXPR_TY_INAME: 814 | arg = 815 | json_string_or_array(expr->e.name_expr.nr, 816 | expr->e.name_expr.names); 817 | json_array_append_new(result, arg); 818 | if (expr->e.name_expr.basename) { 819 | char *base = basename_str[expr->e.name_expr.basename]; 820 | json_array_append_new(result, json_string(base)); 821 | } 822 | break; 823 | case WATCHMAN_EXPR_TY_TYPE: 824 | json_array_append_new(result, 825 | json_string_from_char(expr->e. 826 | type_expr.type)); 827 | } 828 | return result; 829 | } 830 | 831 | /* corresponds to enum watchman_fields */ 832 | static char *fields_str[] = { 833 | "name", 834 | "exists", 835 | "cclock", 836 | "oclock", 837 | "ctime", 838 | "ctime_ms", 839 | "ctime_us", 840 | "ctime_ns", 841 | "ctime_f", 842 | "mtime", 843 | "mtime_ms", 844 | "mtime_us", 845 | "mtime_ns", 846 | "mtime_f", 847 | "size", 848 | "uid", 849 | "gid", 850 | "ino", 851 | "dev", 852 | "nlink", 853 | "new", 854 | "mode" 855 | }; 856 | 857 | json_t * 858 | fields_to_json(int fields) 859 | { 860 | json_t *result = json_array(); 861 | int i = 0; 862 | int mask; 863 | for (mask = 1; mask < WATCHMAN_FIELD_END; mask *= 2) { 864 | if (fields & mask) { 865 | json_array_append_new(result, json_string(fields_str[i])); 866 | } 867 | ++i; 868 | } 869 | return result; 870 | } 871 | 872 | #define PROTO_ASSERT(cond, condarg, msg) \ 873 | if (!cond(condarg)) { \ 874 | char *dump = proto_dumps(condarg, 0); \ 875 | watchman_err(error, WATCHMAN_ERR_WATCHMAN_BROKEN, msg, dump); \ 876 | free(dump); \ 877 | goto done; \ 878 | } 879 | 880 | struct watchman_watch_list * 881 | watchman_watch_list(struct watchman_connection *conn, 882 | struct watchman_error *error) 883 | { 884 | struct watchman_watch_list *res = NULL; 885 | struct watchman_watch_list *result = NULL; 886 | if (watchman_send_simple_command(conn, error, "watch-list", NULL)) { 887 | return NULL; 888 | } 889 | 890 | proto_t obj = watchman_read(conn, error); 891 | if (proto_is_null(obj)) { 892 | return NULL; 893 | } 894 | PROTO_ASSERT(proto_is_object, obj, "Got bogus value from watch-list %s"); 895 | proto_t roots = proto_object_get(obj, "roots"); 896 | PROTO_ASSERT(proto_is_array, roots, "Got bogus value from watch-list %s"); 897 | 898 | res = malloc(sizeof(*res)); 899 | int nr = proto_array_size(roots); 900 | res->nr = 0; 901 | res->roots = calloc(nr, sizeof(*res->roots)); 902 | int i; 903 | for (i = 0; i < nr; ++i) { 904 | proto_t root = proto_array_get(roots, i); 905 | PROTO_ASSERT(proto_is_string, root, 906 | "Got non-string root from watch-list %s"); 907 | res->nr++; 908 | res->roots[i] = proto_strdup(root); 909 | } 910 | result = res; 911 | res = NULL; 912 | done: 913 | if (res) { 914 | watchman_free_watch_list(res); 915 | } 916 | proto_free(obj); 917 | return result; 918 | } 919 | 920 | #define WRITE_BOOL_STAT(stat, statobj, attr) \ 921 | proto_t attr = proto_object_get(statobj, #attr); \ 922 | if (!proto_is_null(attr)) { \ 923 | PROTO_ASSERT(proto_is_boolean, attr, #attr " is not boolean: %s"); \ 924 | stat->attr = proto_is_true(attr); \ 925 | } 926 | 927 | #define WRITE_INT_STAT(stat, statobj, attr) \ 928 | proto_t attr = proto_object_get(statobj, #attr); \ 929 | if (!proto_is_null(attr)) { \ 930 | PROTO_ASSERT(proto_is_integer, attr, #attr " is not an int: %s"); \ 931 | stat->attr = proto_integer_value(attr); \ 932 | } 933 | 934 | #define WRITE_STR_STAT(stat, statobj, attr) \ 935 | proto_t attr = proto_object_get(statobj, #attr); \ 936 | if (!proto_is_null(attr)) { \ 937 | PROTO_ASSERT(proto_is_string, attr, #attr " is not a string: %s"); \ 938 | stat->attr = proto_strdup(attr); \ 939 | } 940 | 941 | #define WRITE_FLOAT_STAT(stat, statobj, attr) \ 942 | proto_t attr = proto_object_get(statobj, #attr); \ 943 | if (!proto_is_null(attr)) { \ 944 | PROTO_ASSERT(proto_is_real, attr, #attr " is not a float: %s"); \ 945 | stat->attr = proto_real_value(attr); \ 946 | } 947 | 948 | static int 949 | watchman_send(struct watchman_connection *conn, 950 | json_t *query, struct watchman_error *error) 951 | { 952 | int result; 953 | if (use_bser_encoding) { 954 | result = bser_write_to_file(query, conn->fp) == 0; 955 | } else { 956 | result = json_dumpf(query, conn->fp, JSON_COMPACT); 957 | fputc('\n', conn->fp); 958 | } 959 | if (result) { 960 | if (errno == EAGAIN || errno == EWOULDBLOCK) { 961 | watchman_err(error, WATCHMAN_ERR_TIMEOUT, 962 | "Timeout sending to watchman"); 963 | } else { 964 | char *dump = json_dumps(query, 0); 965 | watchman_err(error, WATCHMAN_ERR_OTHER, 966 | "Failed to send watchman query %s", dump); 967 | free(dump); 968 | } 969 | return 1; 970 | } 971 | return 0; 972 | } 973 | 974 | char * 975 | watchman_clock(struct watchman_connection *conn, 976 | const char *path, 977 | unsigned int sync_timeout, 978 | struct watchman_error *error) 979 | { 980 | char *result = NULL; 981 | json_t *query = json_array(); 982 | json_array_append_new(query, json_string("clock")); 983 | json_array_append_new(query, json_string(path)); 984 | if (sync_timeout) { 985 | json_t *options = json_object(); 986 | json_object_set_new(options, "sync_timeout", json_integer(sync_timeout)); 987 | json_array_append_new(query, options); 988 | } 989 | 990 | int ret = watchman_send(conn, query, error); 991 | json_decref(query); 992 | if (ret) { 993 | return NULL; 994 | } 995 | 996 | proto_t obj = watchman_read(conn, error); 997 | if (proto_is_null(obj)) { 998 | return NULL; 999 | } 1000 | PROTO_ASSERT(proto_is_object, obj, "Got bogus value from clock %s"); 1001 | proto_t clock = proto_object_get(obj, "clock"); 1002 | PROTO_ASSERT(proto_is_string, clock, "Bad clock %s"); 1003 | result = proto_strdup(clock); 1004 | 1005 | done: 1006 | proto_free(obj); 1007 | return result; 1008 | } 1009 | 1010 | static struct watchman_query_result * 1011 | watchman_query_json(struct watchman_connection *conn, 1012 | json_t *query, 1013 | struct timeval *timeout, 1014 | struct watchman_error *error) 1015 | { 1016 | struct watchman_query_result *result = NULL; 1017 | struct watchman_query_result *res = NULL; 1018 | 1019 | if (watchman_send(conn, query, error)) { 1020 | return NULL; 1021 | } 1022 | /* parse the result */ 1023 | proto_t obj = watchman_read_with_timeout(conn, timeout, error); 1024 | if (proto_is_null(obj)) { 1025 | return NULL; 1026 | } 1027 | PROTO_ASSERT(proto_is_object, obj, "Failed to send watchman query %s"); 1028 | 1029 | proto_t jerror = proto_object_get(obj, "error"); 1030 | if (!proto_is_null(jerror)) { 1031 | watchman_err(error, WATCHMAN_ERR_WATCHMAN_REPORTED, 1032 | "Error result from watchman: %s", 1033 | proto_strdup(jerror)); 1034 | goto done; 1035 | } 1036 | 1037 | res = calloc(1, sizeof(*res)); 1038 | 1039 | proto_t files = proto_object_get(obj, "files"); 1040 | PROTO_ASSERT(proto_is_array, files, "Bad files %s"); 1041 | 1042 | int nr = proto_array_size(files); 1043 | res->stats = calloc(nr, sizeof(*res->stats)); 1044 | 1045 | int i; 1046 | for (i = 0; i < nr; ++i) { 1047 | struct watchman_stat *stat = res->stats + i; 1048 | proto_t statobj = proto_array_get(files, i); 1049 | if (proto_is_string(statobj)) { 1050 | /* then hopefully we only requested names */ 1051 | stat->name = proto_strdup(statobj); 1052 | res->nr++; 1053 | continue; 1054 | } 1055 | 1056 | PROTO_ASSERT(proto_is_object, statobj, "must be object: %s"); 1057 | 1058 | proto_t name = proto_object_get(statobj, "name"); 1059 | PROTO_ASSERT(proto_is_string, name, "name must be string: %s"); 1060 | stat->name = proto_strdup(name); 1061 | 1062 | WRITE_BOOL_STAT(stat, statobj, exists); 1063 | WRITE_INT_STAT(stat, statobj, ctime); 1064 | WRITE_INT_STAT(stat, statobj, ctime_ms); 1065 | WRITE_INT_STAT(stat, statobj, ctime_us); 1066 | WRITE_INT_STAT(stat, statobj, ctime_ns); 1067 | WRITE_INT_STAT(stat, statobj, dev); 1068 | WRITE_INT_STAT(stat, statobj, gid); 1069 | WRITE_INT_STAT(stat, statobj, ino); 1070 | WRITE_INT_STAT(stat, statobj, mode); 1071 | WRITE_INT_STAT(stat, statobj, mtime); 1072 | WRITE_INT_STAT(stat, statobj, mtime_ms); 1073 | WRITE_INT_STAT(stat, statobj, mtime_us); 1074 | WRITE_INT_STAT(stat, statobj, mtime_ns); 1075 | WRITE_INT_STAT(stat, statobj, nlink); 1076 | WRITE_INT_STAT(stat, statobj, size); 1077 | WRITE_INT_STAT(stat, statobj, uid); 1078 | 1079 | WRITE_STR_STAT(stat, statobj, cclock); 1080 | WRITE_STR_STAT(stat, statobj, oclock); 1081 | 1082 | WRITE_FLOAT_STAT(stat, statobj, ctime_f); 1083 | WRITE_FLOAT_STAT(stat, statobj, mtime_f); 1084 | 1085 | /* the one we have to do manually because we don't 1086 | * want to use the name "new" */ 1087 | proto_t newer = proto_object_get(statobj, "new"); 1088 | if (!proto_is_null(newer)) { 1089 | stat->newer = proto_is_true(newer); 1090 | } 1091 | res->nr++; 1092 | } 1093 | 1094 | proto_t version = proto_object_get(obj, "version"); 1095 | PROTO_ASSERT(proto_is_string, version, "Bad version %s"); 1096 | res->version = proto_strdup(version); 1097 | 1098 | proto_t clock = proto_object_get(obj, "clock"); 1099 | PROTO_ASSERT(proto_is_string, clock, "Bad clock %s"); 1100 | res->clock = proto_strdup(clock); 1101 | 1102 | proto_t fresh = proto_object_get(obj, "is_fresh_instance"); 1103 | PROTO_ASSERT(proto_is_boolean, fresh, "Bad is_fresh_instance %s"); 1104 | res->is_fresh_instance = proto_is_true(fresh); 1105 | 1106 | result = res; 1107 | res = NULL; 1108 | done: 1109 | if (res) { 1110 | watchman_free_query_result(res); 1111 | } 1112 | proto_free(obj); 1113 | return result; 1114 | } 1115 | 1116 | struct watchman_query * 1117 | watchman_query(void) 1118 | { 1119 | struct watchman_query *result = calloc(1, sizeof(*result)); 1120 | result->sync_timeout = -1; 1121 | return result; 1122 | } 1123 | 1124 | void 1125 | watchman_free_query(struct watchman_query *query) 1126 | { 1127 | if (query->since_is_str) { 1128 | free(query->s.str); 1129 | query->s.str = NULL; 1130 | } 1131 | if (query->nr_suffixes) { 1132 | int i; 1133 | for (i = 0; i < query->nr_suffixes; ++i) { 1134 | free(query->suffixes[i]); 1135 | query->suffixes[i] = NULL; 1136 | } 1137 | free(query->suffixes); 1138 | query->suffixes = NULL; 1139 | } 1140 | if (query->nr_paths) { 1141 | int i; 1142 | for (i = 0; i < query->nr_paths; ++i) { 1143 | free(query->paths[i].path); 1144 | query->paths[i].path = NULL; 1145 | } 1146 | free(query->paths); 1147 | query->paths = NULL; 1148 | } 1149 | free(query); 1150 | } 1151 | 1152 | void 1153 | watchman_query_add_suffix(struct watchman_query *query, const char *suffix) 1154 | { 1155 | assert(suffix); 1156 | if (query->cap_suffixes == query->nr_suffixes) { 1157 | if (query->nr_suffixes == 0) { 1158 | query->cap_suffixes = 10; 1159 | } else { 1160 | query->cap_suffixes *= 2; 1161 | } 1162 | int new_size = sizeof(*query->suffixes) * query->cap_suffixes; 1163 | query->suffixes = realloc(query->suffixes, new_size); 1164 | } 1165 | query->suffixes[query->nr_suffixes] = strdup(suffix); 1166 | query->nr_suffixes++; 1167 | } 1168 | 1169 | void 1170 | watchman_query_add_path(struct watchman_query *query, const char *path, int depth) 1171 | { 1172 | if (query->cap_paths == query->nr_paths) { 1173 | if (query->nr_paths == 0) { 1174 | query->cap_paths = 10; 1175 | } else { 1176 | query->cap_paths *= 2; 1177 | } 1178 | int new_size = sizeof(*query->paths) * query->cap_paths; 1179 | query->paths = realloc(query->paths, new_size); 1180 | } 1181 | query->paths[query->nr_paths].path = strdup(path); 1182 | query->paths[query->nr_paths].depth = depth; 1183 | query->nr_paths++; 1184 | } 1185 | 1186 | void 1187 | watchman_query_set_since_oclock(struct watchman_query *query, const char *since) 1188 | { 1189 | if (query->since_is_str) { 1190 | free(query->s.str); 1191 | } 1192 | query->since_is_str = 1; 1193 | query->s.str = strdup(since); 1194 | } 1195 | 1196 | void 1197 | watchman_query_set_since_time_t(struct watchman_query *query, time_t since) 1198 | { 1199 | if (query->since_is_str) { 1200 | free(query->s.str); 1201 | } 1202 | query->since_is_str = 1; 1203 | query->s.time = since; 1204 | } 1205 | 1206 | void 1207 | watchman_query_set_fields(struct watchman_query *query, int fields) 1208 | { 1209 | query->fields = fields; 1210 | } 1211 | 1212 | void 1213 | watchman_query_set_empty_on_fresh(struct watchman_query *query, 1214 | bool empty_on_fresh) 1215 | { 1216 | query->empty_on_fresh = empty_on_fresh; 1217 | } 1218 | 1219 | static json_t * 1220 | json_path(struct watchman_pathspec *spec) 1221 | { 1222 | if (spec->depth == -1) { 1223 | return json_string(spec->path); 1224 | } 1225 | 1226 | json_t *obj = json_object(); 1227 | json_object_set_new(obj, "depth", json_integer(spec->depth)); 1228 | json_object_set_new(obj, "path", json_string(spec->path)); 1229 | return obj; 1230 | } 1231 | 1232 | struct watchman_query_result * 1233 | watchman_do_query_timeout(struct watchman_connection *conn, 1234 | const char *fs_path, 1235 | const struct watchman_query *query, 1236 | const struct watchman_expression *expr, 1237 | struct timeval *timeout, 1238 | struct watchman_error *error) 1239 | { 1240 | /* construct the json */ 1241 | json_t *json = json_array(); 1242 | json_array_append_new(json, json_string("query")); 1243 | json_array_append_new(json, json_string(fs_path)); 1244 | json_t *obj = json_object(); 1245 | json_object_set_new(obj, "expression", to_json(expr)); 1246 | if (query) { 1247 | if (query->fields) { 1248 | json_object_set_new(obj, "fields", fields_to_json(query->fields)); 1249 | } 1250 | 1251 | if (query->empty_on_fresh) { 1252 | json_object_set_new(obj, "empty_on_fresh_instance", 1253 | json_true()); 1254 | } 1255 | 1256 | if (query->s.time) { 1257 | if (query->since_is_str) { 1258 | json_object_set_new(obj, "since", json_string(query->s.str)); 1259 | } else { 1260 | json_t *since = json_integer(query->s.time); 1261 | json_object_set_new(obj, "since", since); 1262 | } 1263 | } 1264 | 1265 | if (query->nr_suffixes) { 1266 | /* Note that even if you have only one suffix, 1267 | * watchman requires this to be an array. */ 1268 | int i; 1269 | json_t *suffixes = json_array(); 1270 | for (i = 0; i < query->nr_suffixes; ++i) { 1271 | json_array_append_new(suffixes, 1272 | json_string(query->suffixes[i])); 1273 | } 1274 | json_object_set_new(obj, "suffix", suffixes); 1275 | } 1276 | if (query->nr_paths) { 1277 | int i; 1278 | json_t *paths = json_array(); 1279 | for (i = 0; i < query->nr_paths; ++i) { 1280 | json_array_append_new(paths, json_path(&query->paths[i])); 1281 | } 1282 | json_object_set_new(obj, "path", paths); 1283 | } 1284 | 1285 | if (query->all) { 1286 | json_object_set_new(obj, "all", json_string("all")); 1287 | } 1288 | 1289 | if (query->sync_timeout >= 0) { 1290 | json_object_set_new(obj, "sync_timeout", 1291 | json_integer(query->sync_timeout)); 1292 | } 1293 | } 1294 | json_array_append_new(json, obj); 1295 | 1296 | /* do the query */ 1297 | struct watchman_query_result *r = watchman_query_json(conn, json, timeout, error); 1298 | json_decref(json); 1299 | return r; 1300 | } 1301 | 1302 | struct watchman_query_result * 1303 | watchman_do_query(struct watchman_connection *conn, 1304 | const char *fs_path, 1305 | const struct watchman_query *query, 1306 | const struct watchman_expression *expr, 1307 | struct watchman_error *error) 1308 | { 1309 | struct timeval unused = {0}; 1310 | return watchman_do_query_timeout(conn, fs_path, query, expr, &unused, error); 1311 | } 1312 | 1313 | void 1314 | watchman_free_expression(struct watchman_expression *expr) 1315 | { 1316 | int i; 1317 | switch (expr->ty) { 1318 | case WATCHMAN_EXPR_TY_ALLOF: 1319 | /*-fallthrough*/ 1320 | case WATCHMAN_EXPR_TY_ANYOF: 1321 | for (i = 0; i < expr->e.union_expr.nr; ++i) { 1322 | watchman_free_expression(expr->e.union_expr.clauses[i]); 1323 | } 1324 | free(expr->e.union_expr.clauses); 1325 | free(expr); 1326 | break; 1327 | case WATCHMAN_EXPR_TY_NOT: 1328 | watchman_free_expression(expr->e.not_expr.clause); 1329 | free(expr); 1330 | break; 1331 | case WATCHMAN_EXPR_TY_TRUE: 1332 | /*-fallthrough*/ 1333 | case WATCHMAN_EXPR_TY_FALSE: 1334 | /*-fallthrough*/ 1335 | /* These are singletons; don't delete them */ 1336 | break; 1337 | case WATCHMAN_EXPR_TY_SINCE: 1338 | if (expr->e.since_expr.is_str) { 1339 | free(expr->e.since_expr.t.since); 1340 | } 1341 | free(expr); 1342 | break; 1343 | case WATCHMAN_EXPR_TY_SUFFIX: 1344 | free(expr->e.suffix_expr.suffix); 1345 | free(expr); 1346 | break; 1347 | case WATCHMAN_EXPR_TY_MATCH: 1348 | /*-fallthrough*/ 1349 | case WATCHMAN_EXPR_TY_IMATCH: 1350 | /*-fallthrough*/ 1351 | case WATCHMAN_EXPR_TY_PCRE: 1352 | /*-fallthrough*/ 1353 | case WATCHMAN_EXPR_TY_IPCRE: 1354 | /*-fallthrough*/ 1355 | free(expr->e.match_expr.match); 1356 | free(expr); 1357 | break; 1358 | case WATCHMAN_EXPR_TY_NAME: 1359 | /*-fallthrough*/ 1360 | case WATCHMAN_EXPR_TY_INAME: 1361 | for (i = 0; i < expr->e.name_expr.nr; ++i) { 1362 | free(expr->e.name_expr.names[i]); 1363 | } 1364 | free(expr->e.name_expr.names); 1365 | free(expr); 1366 | break; 1367 | case WATCHMAN_EXPR_TY_TYPE: 1368 | free(expr); 1369 | break; 1370 | case WATCHMAN_EXPR_TY_EMPTY: 1371 | /*-fallthrough*/ 1372 | case WATCHMAN_EXPR_TY_EXISTS: 1373 | /* These are singletons; don't delete them */ 1374 | break; 1375 | } 1376 | } 1377 | 1378 | void 1379 | watchman_connection_close(struct watchman_connection *conn) 1380 | { 1381 | if (!conn->fp) { 1382 | return; 1383 | } 1384 | fclose(conn->fp); 1385 | conn->fp = NULL; 1386 | free(conn); 1387 | } 1388 | 1389 | void 1390 | watchman_release_error(struct watchman_error *error) 1391 | { 1392 | if (error->message) { 1393 | free(error->message); 1394 | } 1395 | } 1396 | 1397 | void 1398 | watchman_free_watch_list(struct watchman_watch_list *list) 1399 | { 1400 | int i; 1401 | for (i = 0; i < list->nr; ++i) { 1402 | free(list->roots[i]); 1403 | list->roots[i] = NULL; 1404 | } 1405 | free(list->roots); 1406 | list->roots = NULL; 1407 | free(list); 1408 | } 1409 | 1410 | /* Not a _free_ function, since stats are allocated as a block. */ 1411 | static void 1412 | watchman_release_stat(struct watchman_stat *stat) 1413 | { 1414 | if (stat->name) { 1415 | free(stat->name); 1416 | stat->name = NULL; 1417 | } 1418 | } 1419 | 1420 | void 1421 | watchman_free_query_result(struct watchman_query_result *result) 1422 | { 1423 | if (result->version) { 1424 | free(result->version); 1425 | result->version = NULL; 1426 | } 1427 | if (result->clock) { 1428 | free(result->clock); 1429 | result->clock = NULL; 1430 | } 1431 | if (result->stats) { 1432 | int i; 1433 | for (i = 0; i < result->nr; ++i) { 1434 | watchman_release_stat(&(result->stats[i])); 1435 | } 1436 | free(result->stats); 1437 | result->stats = NULL; 1438 | } 1439 | free(result); 1440 | } 1441 | 1442 | struct watchman_expression * 1443 | watchman_not_expression(struct watchman_expression *expression) 1444 | { 1445 | struct watchman_expression *not_expr = alloc_expr(WATCHMAN_EXPR_TY_NOT); 1446 | not_expr->e.not_expr.clause = expression; 1447 | return not_expr; 1448 | } 1449 | 1450 | static struct watchman_expression * 1451 | watchman_union_expression(enum watchman_expression_type 1452 | ty, int nr, struct watchman_expression **expressions) 1453 | { 1454 | assert(nr); 1455 | assert(expressions); 1456 | size_t sz = sizeof(*expressions); 1457 | struct watchman_expression *result = malloc(sizeof(*result)); 1458 | result->ty = ty; 1459 | result->e.union_expr.nr = nr; 1460 | result->e.union_expr.clauses = malloc(nr * sz); 1461 | memcpy(result->e.union_expr.clauses, expressions, nr * sz); 1462 | return result; 1463 | } 1464 | 1465 | struct watchman_expression * 1466 | watchman_allof_expression(int nr, struct watchman_expression **expressions) 1467 | { 1468 | return watchman_union_expression(WATCHMAN_EXPR_TY_ALLOF, nr, expressions); 1469 | } 1470 | 1471 | struct watchman_expression * 1472 | watchman_anyof_expression(int nr, struct watchman_expression **expressions) 1473 | { 1474 | return watchman_union_expression(WATCHMAN_EXPR_TY_ANYOF, nr, expressions); 1475 | } 1476 | 1477 | #define STATIC_EXPR(ty, tylower) \ 1478 | static struct watchman_expression ty##_EXPRESSION = \ 1479 | { WATCHMAN_EXPR_TY_##ty }; \ 1480 | struct watchman_expression * \ 1481 | watchman_##tylower##_expression(void) \ 1482 | { \ 1483 | return &ty##_EXPRESSION; \ 1484 | } 1485 | 1486 | STATIC_EXPR(EMPTY, empty) 1487 | STATIC_EXPR(TRUE, true) 1488 | STATIC_EXPR(FALSE, false) 1489 | STATIC_EXPR(EXISTS, exists) 1490 | #undef STATIC_EXPR 1491 | 1492 | struct watchman_expression * 1493 | watchman_suffix_expression(const char *suffix) 1494 | { 1495 | assert(suffix); 1496 | struct watchman_expression *expr = alloc_expr(WATCHMAN_EXPR_TY_SUFFIX); 1497 | expr->e.suffix_expr.suffix = strdup(suffix); 1498 | return expr; 1499 | } 1500 | 1501 | #define MATCH_EXPR(tyupper, tylower) \ 1502 | struct watchman_expression * \ 1503 | watchman_##tylower##_expression(const char *match, \ 1504 | enum watchman_basename basename) \ 1505 | { \ 1506 | assert(match); \ 1507 | struct watchman_expression *expr = \ 1508 | alloc_expr(WATCHMAN_EXPR_TY_##tyupper); \ 1509 | expr->e.match_expr.match = strdup(match); \ 1510 | expr->e.match_expr.basename = basename; \ 1511 | return expr; \ 1512 | } 1513 | 1514 | MATCH_EXPR(MATCH, match) 1515 | MATCH_EXPR(IMATCH, imatch) 1516 | MATCH_EXPR(PCRE, pcre) 1517 | MATCH_EXPR(IPCRE, ipcre) 1518 | #undef MATCH_EXPR 1519 | 1520 | #define NAME_EXPR(tyupper, tylower) \ 1521 | struct watchman_expression * \ 1522 | watchman_##tylower##_expression(const char *name, \ 1523 | enum watchman_basename basename) \ 1524 | { \ 1525 | assert(name); \ 1526 | return watchman_##tylower##s_expression(1, &name, basename); \ 1527 | } 1528 | 1529 | NAME_EXPR(NAME, name) 1530 | NAME_EXPR(INAME, iname) 1531 | #undef NAME_EXPR 1532 | 1533 | #define NAMES_EXPR(tyupper, tylower) \ 1534 | struct watchman_expression * \ 1535 | watchman_##tylower##s_expression(int nr, const char **names, \ 1536 | enum watchman_basename basename) \ 1537 | { \ 1538 | assert(nr); \ 1539 | assert(names); \ 1540 | struct watchman_expression *result = \ 1541 | alloc_expr(WATCHMAN_EXPR_TY_##tyupper); \ 1542 | result->e.name_expr.nr = nr; \ 1543 | result->e.name_expr.names = malloc(nr * sizeof(*names)); \ 1544 | int i; \ 1545 | for (i = 0; i < nr; ++i) { \ 1546 | result->e.name_expr.names[i] = strdup(names[i]); \ 1547 | } \ 1548 | return result; \ 1549 | } 1550 | 1551 | NAMES_EXPR(NAME, name) 1552 | NAMES_EXPR(INAME, iname) 1553 | #undef NAMES_EXPR 1554 | 1555 | struct watchman_expression * 1556 | watchman_type_expression(char c) 1557 | { 1558 | struct watchman_expression *result = alloc_expr(WATCHMAN_EXPR_TY_TYPE); 1559 | result->e.type_expr.type = c; 1560 | return result; 1561 | } 1562 | 1563 | int 1564 | watchman_version(struct watchman_connection *conn, 1565 | struct watchman_error *error, 1566 | struct watchman_version* version) 1567 | { 1568 | const char *result = NULL; 1569 | json_t *cmd = json_array(); 1570 | json_array_append_new(cmd, json_string("version")); 1571 | 1572 | int ret = watchman_send(conn, cmd, error); 1573 | json_decref(cmd); 1574 | if (ret) { 1575 | return -1; 1576 | } 1577 | 1578 | proto_t obj = watchman_read(conn, error); 1579 | if (proto_is_null(obj)) { 1580 | return -1; 1581 | } 1582 | PROTO_ASSERT(proto_is_object, obj, "Got bogus value from version %s"); 1583 | proto_t version_field = proto_object_get(obj, "version"); 1584 | PROTO_ASSERT(proto_is_string, version_field, "Bad version %s"); 1585 | result = proto_strdup(version_field); 1586 | 1587 | int count = sscanf(result, "%d.%d.%d", &version->major, 1588 | &version->minor, &version->micro); 1589 | proto_free(obj); 1590 | return count == 3 ? 0 : -1; 1591 | 1592 | done: 1593 | proto_free(obj); 1594 | return -1; 1595 | } 1596 | 1597 | int 1598 | watchman_shutdown_server(struct watchman_connection *conn, 1599 | struct watchman_error *error) 1600 | { 1601 | json_t *cmd = json_array(); 1602 | json_array_append_new(cmd, json_string("shutdown-server")); 1603 | 1604 | int ret = watchman_send(conn, cmd, error); 1605 | json_decref(cmd); 1606 | 1607 | if (ret) { 1608 | return -1; 1609 | } 1610 | 1611 | proto_t obj = watchman_read(conn, error); 1612 | if (proto_is_null(obj)) { 1613 | return -1; 1614 | } 1615 | 1616 | proto_free(obj); 1617 | return 0; 1618 | } 1619 | -------------------------------------------------------------------------------- /watchman.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBWATCHMAN_WATCHMAN_H_ 2 | #define LIBWATCHMAN_WATCHMAN_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | enum watchman_fields { 12 | WATCHMAN_FIELD_NAME = 0x00000001, 13 | WATCHMAN_FIELD_EXISTS = 0x00000002, 14 | WATCHMAN_FIELD_CCLOCK = 0x00000004, 15 | WATCHMAN_FIELD_OCLOCK = 0x00000008, 16 | WATCHMAN_FIELD_CTIME = 0x00000010, 17 | WATCHMAN_FIELD_CTIME_MS = 0x00000020, 18 | WATCHMAN_FIELD_CTIME_US = 0x00000040, 19 | WATCHMAN_FIELD_CTIME_NS = 0x00000080, 20 | WATCHMAN_FIELD_CTIME_F = 0x00000100, 21 | WATCHMAN_FIELD_MTIME = 0x00000200, 22 | WATCHMAN_FIELD_MTIME_MS = 0x00000400, 23 | WATCHMAN_FIELD_MTIME_US = 0x00000800, 24 | WATCHMAN_FIELD_MTIME_NS = 0x00001000, 25 | WATCHMAN_FIELD_MTIME_F = 0x00002000, 26 | WATCHMAN_FIELD_SIZE = 0x00004000, 27 | WATCHMAN_FIELD_UID = 0x00008000, 28 | WATCHMAN_FIELD_GID = 0x00010000, 29 | WATCHMAN_FIELD_INO = 0x00020000, 30 | WATCHMAN_FIELD_DEV = 0x00040000, 31 | WATCHMAN_FIELD_NLINK = 0x00080000, 32 | WATCHMAN_FIELD_NEWER = 0x00100000, /* corresponds to "new" */ 33 | WATCHMAN_FIELD_MODE = 0x00200000, 34 | WATCHMAN_FIELD_END = 0x00400000 35 | }; 36 | 37 | struct watchman_connection { 38 | FILE *fp; 39 | }; 40 | 41 | enum watchman_expression_type { 42 | WATCHMAN_EXPR_TY_ALLOF, 43 | WATCHMAN_EXPR_TY_ANYOF, 44 | WATCHMAN_EXPR_TY_NOT, 45 | WATCHMAN_EXPR_TY_TRUE, 46 | WATCHMAN_EXPR_TY_FALSE, 47 | WATCHMAN_EXPR_TY_SINCE, 48 | WATCHMAN_EXPR_TY_SUFFIX, 49 | WATCHMAN_EXPR_TY_MATCH, 50 | WATCHMAN_EXPR_TY_IMATCH, 51 | WATCHMAN_EXPR_TY_PCRE, 52 | WATCHMAN_EXPR_TY_IPCRE, 53 | WATCHMAN_EXPR_TY_NAME, 54 | WATCHMAN_EXPR_TY_INAME, 55 | WATCHMAN_EXPR_TY_TYPE, 56 | WATCHMAN_EXPR_TY_EMPTY, 57 | WATCHMAN_EXPR_TY_EXISTS 58 | }; 59 | 60 | enum watchman_error_code { 61 | WATCHMAN_ERR_CONNECT = 1024, 62 | WATCHMAN_ERR_TIMEOUT, 63 | WATCHMAN_ERR_RUN_WATCHMAN, 64 | WATCHMAN_ERR_WATCHMAN_BROKEN, 65 | WATCHMAN_ERR_WATCHMAN_REPORTED, 66 | /* We only want to have codes for the errors that 67 | * callers might find interesting*/ 68 | WATCHMAN_ERR_OTHER, 69 | /* Possibly in addition to another error, we couldn't get back to 70 | * our initial working directory */ 71 | WATCHMAN_ERR_CWD = 2048 72 | }; 73 | 74 | struct watchman_error { 75 | char *message; 76 | enum watchman_error_code code; 77 | int err_no; 78 | }; 79 | 80 | enum watchman_clockspec { 81 | WATCHMAN_CLOCKSPEC_DEFAULT = 0, 82 | WATCHMAN_CLOCKSPEC_OCLOCK, 83 | WATCHMAN_CLOCKSPEC_CCLOCK, 84 | WATCHMAN_CLOCKSPEC_MTIME, 85 | WATCHMAN_CLOCKSPEC_CTIME 86 | }; 87 | 88 | enum watchman_basename { 89 | WATCHMAN_BASENAME_DEFAULT, 90 | WATCHMAN_BASENAME_BASENAME, 91 | WATCHMAN_BASENAME_WHOLENAME 92 | }; 93 | 94 | struct watchman_expression; 95 | 96 | struct watchman_since_expr { 97 | unsigned is_str:1; 98 | union { 99 | char *since; 100 | time_t time; 101 | } t; 102 | enum watchman_clockspec clockspec; 103 | }; 104 | 105 | struct watchman_suffix_expr { 106 | char *suffix; 107 | }; 108 | 109 | struct watchman_match_expr { 110 | char *match; 111 | enum watchman_basename basename; 112 | }; 113 | 114 | struct watchman_name_expr { 115 | int nr; 116 | char **names; 117 | enum watchman_basename basename; 118 | }; 119 | 120 | struct watchman_type_expr { 121 | char type; 122 | }; 123 | 124 | struct watchman_not_expr { 125 | struct watchman_expression *clause; 126 | }; 127 | 128 | struct watchman_union_expr { 129 | int nr; 130 | struct watchman_expression **clauses; 131 | }; 132 | 133 | /* These are the possible fields that can be returned by watchman 134 | query. Only fields that you request will be set (if you don't 135 | request any, then watchman's default will be used). */ 136 | struct watchman_stat { 137 | time_t ctime; 138 | int64_t ctime_ms; 139 | int64_t ctime_us; 140 | int64_t ctime_ns; 141 | double ctime_f; 142 | dev_t dev; 143 | gid_t gid; 144 | int ino; 145 | int mode; 146 | time_t mtime; 147 | int64_t mtime_ms; 148 | int64_t mtime_us; 149 | int64_t mtime_ns; 150 | double mtime_f; 151 | unsigned newer:1; 152 | unsigned exists:1; 153 | int nlink; 154 | uid_t uid; 155 | char *name; 156 | char *oclock; 157 | char *cclock; 158 | off_t size; 159 | }; 160 | 161 | struct watchman_query_result { 162 | char *version; 163 | char *clock; 164 | unsigned is_fresh_instance:1; 165 | 166 | int nr; 167 | struct watchman_stat *stats; 168 | }; 169 | 170 | struct watchman_watch_list { 171 | int nr; 172 | char **roots; 173 | }; 174 | 175 | struct watchman_pathspec { 176 | int depth; 177 | char *path; 178 | }; 179 | 180 | struct watchman_query { 181 | unsigned since_is_str:1; 182 | unsigned all:1; 183 | unsigned empty_on_fresh:1; 184 | union { 185 | char *str; 186 | time_t time; 187 | } s; 188 | int nr_suffixes; 189 | int cap_suffixes; 190 | char **suffixes; 191 | int nr_paths; 192 | int cap_paths; 193 | struct watchman_pathspec *paths; 194 | int fields; 195 | 196 | /* negative for unset */ 197 | int64_t sync_timeout; 198 | }; 199 | 200 | struct watchman_expression { 201 | enum watchman_expression_type ty; 202 | union { 203 | struct watchman_union_expr union_expr; 204 | struct watchman_not_expr not_expr; 205 | struct watchman_since_expr since_expr; 206 | struct watchman_suffix_expr suffix_expr; 207 | struct watchman_match_expr match_expr; 208 | struct watchman_name_expr name_expr; 209 | struct watchman_type_expr type_expr; 210 | /* true, false, empty, and exists don't need any extra data */ 211 | } e; 212 | }; 213 | 214 | struct watchman_version { 215 | int major; 216 | int minor; 217 | int micro; 218 | }; 219 | 220 | /** 221 | * If set, errors and warnings are sent to this location 222 | */ 223 | void watchman_set_error_handle(FILE* fp); 224 | 225 | struct watchman_connection * 226 | watchman_connect(struct timeval timeout, struct watchman_error *error); 227 | int 228 | watchman_watch(struct watchman_connection *connection, const char *path, 229 | struct watchman_error *error); 230 | int 231 | watchman_watch_del(struct watchman_connection *connection, const char *path, 232 | struct watchman_error *error); 233 | struct watchman_watch_list * 234 | watchman_watch_list(struct watchman_connection *connection, 235 | struct watchman_error *error); 236 | struct watchman_expression * 237 | watchman_since_expression(const char *since, enum watchman_clockspec spec); 238 | struct watchman_expression * 239 | watchman_since_expression_time_t(time_t time, enum watchman_clockspec spec); 240 | struct watchman_expression * 241 | watchman_not_expression(struct watchman_expression *expression); 242 | struct watchman_expression * 243 | watchman_allof_expression(int nr, struct watchman_expression **expressions); 244 | struct watchman_expression * 245 | watchman_anyof_expression(int nr, struct watchman_expression **expressions); 246 | struct watchman_expression * 247 | watchman_empty_expression(void); 248 | struct watchman_expression * 249 | watchman_true_expression(void); 250 | struct watchman_expression * 251 | watchman_false_expression(void); 252 | struct watchman_expression * 253 | watchman_exists_expression(void); 254 | struct watchman_expression * 255 | watchman_suffix_expression(const char *suffix); 256 | struct watchman_expression * 257 | watchman_match_expression(const char *match, enum watchman_basename basename); 258 | struct watchman_expression * 259 | watchman_imatch_expression(const char *match, enum watchman_basename basename); 260 | struct watchman_expression * 261 | watchman_pcre_expression(const char *match, enum watchman_basename basename); 262 | struct watchman_expression * 263 | watchman_ipcre_expression(const char *match, enum watchman_basename basename); 264 | struct watchman_expression * 265 | watchman_name_expression(const char *match, enum watchman_basename basename); 266 | struct watchman_expression * 267 | watchman_iname_expression(const char *match, enum watchman_basename basename); 268 | struct watchman_expression * 269 | watchman_names_expression(int nr, char const **match, 270 | enum watchman_basename basename); 271 | struct watchman_expression * 272 | watchman_inames_expression(int nr, char const **match, 273 | enum watchman_basename basename); 274 | struct watchman_expression * 275 | watchman_type_expression(char c); 276 | struct watchman_query_result * 277 | watchman_do_query(struct watchman_connection *connection, const char *fs_path, 278 | const struct watchman_query *query, 279 | const struct watchman_expression *expr, 280 | struct watchman_error *error); 281 | struct watchman_query_result * 282 | watchman_do_query_timeout(struct watchman_connection *conn, 283 | const char *fs_path, 284 | const struct watchman_query *query, 285 | const struct watchman_expression *expr, 286 | struct timeval *timeout, 287 | struct watchman_error *error); 288 | struct watchman_query * 289 | watchman_query(void); 290 | void 291 | watchman_query_add_suffix(struct watchman_query *query, const char *suffix); 292 | void 293 | watchman_query_add_path(struct watchman_query *query, const char *path, int depth); 294 | void 295 | watchman_query_set_since_oclock(struct watchman_query *query, const char *since); 296 | void 297 | watchman_query_set_since_time_t(struct watchman_query *query, time_t since); 298 | void 299 | watchman_query_set_fields(struct watchman_query *query, int fields); 300 | void 301 | watchman_query_set_empty_on_fresh(struct watchman_query *query, 302 | bool empty_on_fresh); 303 | void 304 | watchman_free_expression(struct watchman_expression *expr); 305 | void 306 | watchman_free_query_result(struct watchman_query_result *res); 307 | void 308 | watchman_free_query(struct watchman_query *query); 309 | void 310 | watchman_free_watch_list(struct watchman_watch_list *list); 311 | void 312 | watchman_release_error(struct watchman_error *error); 313 | void 314 | watchman_connection_close(struct watchman_connection *connection); 315 | int 316 | watchman_recrawl(struct watchman_connection *connection, const char *path, 317 | struct watchman_error *error); 318 | char * 319 | watchman_clock(struct watchman_connection *conn, 320 | const char *path, 321 | unsigned int sync_timeout, 322 | struct watchman_error *error); 323 | int 324 | watchman_version(struct watchman_connection *conn, 325 | struct watchman_error *error, 326 | struct watchman_version* version); 327 | int 328 | watchman_shutdown_server(struct watchman_connection *conn, 329 | struct watchman_error *error); 330 | 331 | int 332 | is_watchman_error(struct watchman_error *error); 333 | #endif /* LIBWATCHMAN_WATCHMAN_H */ 334 | --------------------------------------------------------------------------------