pax_global_header00006660000000000000000000000064143307723170014521gustar00rootroot0000000000000052 comment=2cc476178198774ad6bea89be0b9e4ed2d4bf4c5 jo-1.9/000077500000000000000000000000001433077231700120025ustar00rootroot00000000000000jo-1.9/.gitignore000066400000000000000000000006701433077231700137750ustar00rootroot00000000000000*.dSYM .DS_Store *.o *.so *.la *.lai *.so.* # Autotools junk .libs .deps .dirstamp libtool *.log stamp-h1 config.log config.status autom4te.cache INSTALL Makefile jo-*.tar.gz configure aclocal.m4 Makefile.in version.h config.cache m4/libtool.m4 m4/ltoptions.m4 m4/ltsugar.m4 m4/ltversion.m4 m4/lt~obsolete.m4 *.trs install-sh depcomp missing build-aux/compile build-aux/depcomp build-aux/install-sh build-aux/missing tests/jo.07.sh jo g jo-1.9/.travis.yml000066400000000000000000000003721433077231700141150ustar00rootroot00000000000000os: - linux - osx arch: - amd64 - ppc64le sudo: false language: c compiler: - gcc - clang before_install: - echo $TRAVIS_OS_NAME - uname -a - echo $LANG - echo $LC_ALL before_script: - autoreconf -i - ./configure script: - make check jo-1.9/AUTHORS000066400000000000000000000001031433077231700130440ustar00rootroot00000000000000Jan-Piet Mens Adrian Ho jo-1.9/COPYING000066400000000000000000000037401433077231700130410ustar00rootroot00000000000000/* * Copyright (C) 2016 Jan-Piet Mens * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #-------------------------------------------------------------------- json.[ch] /* Copyright (C) 2011 Joseph A. Adams (joeyadams3.14159@gmail.com) All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ jo-1.9/ChangeLog000066400000000000000000000060111433077231700135520ustar00rootroot00000000000000 2022-11-04 1.9 - FIX: fix reading of long lines from stdin (mk 2) (#195) - FIX: add missing test files (#196) 2022-11-04 1.8 - FIX: fix reading of long lines from stdin (mk 2) (#195) 2022-10-29 1.7 - FIX: fix warnings on Windows build and reimplement err{x,} functions (#193) - FIX: fix reading of long lines from stdin and refactor slurp_file() in the process. (#192) - NEW: Add option -o outfile for when not run from a shell and redirect '>' is not an option. (#189) - FIX: fix tables in jo.md (#167) 2022-01-05 1.6 - FIX: repair tests broken by AUTHORS change (#164) - FIX: repair make distcheck by removing copied _jo zsh functions 2022-01-04 1.5 - NEW: replace asserts with human errors (#162) - NEW: zsh completion (#158) - FIX: stdin filter on Windows (# - FIX: several cleanups - NEW: Meson build - UPD: snap to newer base (#149) - NEW: option to deduplicate keys (#143, #145) - NEW: Filter functionality (#141) - FIX: file embedding - FIX: add missing tests to Makefile.am 2020-07-18 1.4 - FIX: Coercion flag logic now permits getopt(3) double-dash - FIX: Documentation clarifies special characters - FIX: Jo builds on snap builds (#110) - FIX: Jo builds on systems with slightly older pkg-config (#107) 2019-11-04 1.3 - FIX: Escaped @ ("\@") is treated as "@" (#42, #103) - NEW: Support reading JSON array elements (#91) - UPD: Add home and removable-media interfaces to snap (#94) - FIX: fix unlikely crash after malloc fail when base64 encoding. - NEW: Support reading nested data from pipes (#82) 2018-12-10 1.2 - NEW: Dockerfile (#76) - UPD: add examples of empty arrays/objects to manual (#74) - NEW: support -e to ignore empty stdin; contributed by Robi Karp - NEW: object-path support (#57) 2017-05-18 1.1 - NEW: type coercion (#55) - FIX: quotes in quotes and double quotes at begin of string (#47) - FIX: catch null value in assignmen (#46) - NEW: support for key:=file.json for reading object values from a file (#43) - NEW: PPA contributed by Ross Duggan in #32 - FIX: "null" is now handled like we handle "true" and "false"; disable with -B - NEW: more tests in the test suite 2016-03-11 1.0 - NEW: read JSON element values from files (#22) - FIX: usage diagnostic - NEW: add support for OpenBSD pledge(2) (#21) 2016-03-10 0.9 - UPD: revert support for $JO_PRETTY et. al; it was a bad idea 2016-03-10 0.8 - UPD: new test suite - NEW: support for nested elements (#19) - NEW: if $JO_PRETTY is set, jo will always pretty-print - NEW: Define $JO_SPACER to any desired number of spaces or tabs for pretty-printing (#18) 2016-03-09 0.7 - NEW: strings "true"/"false" now default to booleans; avoid with -B (#17) - FIX: test.sh get quotes to prevent failures with pdksh (#16) - FIX: pretty-print Version if requested (#15) - FIX: Add cast to suppress warning when compiling with GCC 4.8.4 (#14) 2016-03-08 0.6 - FIX: make build work on CentOS 6 (#13) - NEW: JSONy version with -V 2016-03-08 0.5 - FIX: fileno error (#12) 2016-03-08 0.4 - NEW: Win32 support for CJK contributed by @mattn 2016-03-08 0.3 - NEW: autotools support - NEW: option -v jo-1.9/Dockerfile000066400000000000000000000003721433077231700137760ustar00rootroot00000000000000FROM alpine AS builder RUN apk -U add automake autoconf build-base make pkgconf COPY . /src WORKDIR /src RUN autoreconf -i && ./configure && make check && make install FROM alpine COPY --from=builder /usr/local/bin/jo /bin/jo ENTRYPOINT ["/bin/jo"] jo-1.9/Makefile.am000066400000000000000000000044401433077231700140400ustar00rootroot00000000000000 AM_CFLAGS = -Wall -O2 bin_PROGRAMS = jo jo_SOURCES = jo.c json.c json.h base64.c base64.h jo_EXTRA = jo.pandoc dist_man_MANS = jo.1 jo_LDADD = -lm bashcompdir = @bashcompdir@ dist_bashcomp_DATA = jo.bash zshcompdir = $(datadir)/zsh/site-functions dist_zshcomp_DATA = jo.zsh install-data-hook: mv -f $(DESTDIR)$(zshcompdir)/jo.zsh $(DESTDIR)$(zshcompdir)/_jo uninstall-local: rm -f $(DESTDIR)$(zshcompdir)/_jo if USE_PANDOC # Add targets to rebuild pages jo.1: jo.pandoc @test -n "$(PANDOC)" || \ { echo 'pandoc' not found during configure.; exit 1; } $(PANDOC) -s -w man -f markdown -o $@ $< jo.md: jo.pandoc @test -n "$(PANDOC)" || \ { echo 'pandoc' not found during configure.; exit 1; } $(PANDOC) -s -w gfm -f markdown-smart -o $@ $< endif # docdir = $(datadir)/doc/@PACKAGE@ # doc_DATA = README.md # If on OS/X, fail if $COPYFILE_DISABLE is not in the environment # so that tar doesn't bundle the AppleDouble attributes dist-hook: if test $$(uname -s) = "Darwin" -a "x$$COPYFILE_DISABLE" = "x"; then echo "Set COPYFILE_DISABLE before making dist" >&2; exit 2; fi TEST_LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) \ $(top_srcdir)/build-aux/tap-driver.sh TESTS = tests/jo.test EXTRA_DIST = $(jo_EXTRA) \ $(TESTS) \ tests/jo.01.sh tests/jo.01.exp \ tests/jo.02.sh tests/jo.02.exp \ tests/jo.03.sh tests/jo.03.exp \ tests/jo.04.sh tests/jo.04.exp \ tests/jo.05.sh tests/jo.05.exp \ tests/jo.06.sh tests/jo.06.exp \ tests/jo.07.sh.in \ tests/jo.08.sh tests/jo.08.exp \ tests/jo.09.sh tests/jo.09.exp \ tests/jo.10.sh tests/jo.10.exp \ tests/jo.11.sh tests/jo.11.exp \ tests/jo.12.sh tests/jo.12.exp \ tests/jo.13.sh tests/jo.13.exp tests/jo-logo.png \ tests/jo.14.sh tests/jo.14.exp \ tests/jo.15.sh tests/jo.15.exp \ tests/jo.16.sh tests/jo.16.exp \ tests/jo.17.sh tests/jo.17.exp tests/jo-creator.txt \ tests/jo.18.sh tests/jo.18.exp \ tests/jo.19.sh tests/jo.19.exp \ tests/jo.20.sh tests/jo.20.exp tests/jo-large1.json tests/jo-large2.json \ tests/jo.21.sh tests/jo.21.exp \ tests/jo.22.sh tests/jo.22.exp \ tests/jo.23.sh tests/jo.23.exp \ tests/jo.24.sh tests/jo.24.exp \ tests/jo.25.sh tests/jo.25.exp \ tests/jo.26.sh tests/jo.26.exp \ tests/jo.27.sh tests/jo.27.exp jo-1.9/NEWS000066400000000000000000000000001433077231700124670ustar00rootroot00000000000000jo-1.9/README000077700000000000000000000000001433077231700141342README.mdustar00rootroot00000000000000jo-1.9/README.md000066400000000000000000000053771433077231700132750ustar00rootroot00000000000000# jo ![jo logo](tests/jo-logo.png) This is `jo`, a small utility to create JSON objects ```bash $ jo -p name=jo n=17 parser=false { "name": "jo", "n": 17, "parser": false } ``` or arrays ```bash $ seq 1 10 | jo -a [1,2,3,4,5,6,7,8,9,10] ``` It has a [manual](jo.md), and you can read [why I wrote jo](http://jpmens.net/2016/03/05/a-shell-command-to-create-json-jo/). ## Build from Release tarball To build from [a release](https://github.com/jpmens/jo/releases) you will need a C compiler to install from a source tarball which you download from the [Releases page](https://github.com/jpmens/jo/releases). ```bash tar xvzf jo-1.3.tar.gz cd jo-1.3 autoreconf -i ./configure make check make install ``` ## Build from Github [![Build Status](https://api.travis-ci.com/jpmens/jo.svg?branch=master)](https://travis-ci.com/github/jpmens/jo) To install from the repository, you will need a C compiler as well as a relatively recent version of _automake_ and _autoconf_. ```bash git clone https://github.com/jpmens/jo.git cd jo autoreconf -i ./configure make check make install ``` ## Install ### Homebrew ```bash brew install jo ``` ### MacPorts ```bash sudo port install jo ``` ### Ubuntu ``` apt-get install jo ``` ### Gentoo ``` emerge jo ``` ### Fedora ``` dnf install jo ``` ### Snap Thanks to [Roger Light](https://twitter.com/ralight/status/1166023769623867398), _jo_ is available as a [snap package](https://snapcraft.io/jo). Use `snap install jo` from a Linux distro that supports snaps. ### Windows ```cmd scoop install jo ``` ### Windows WSL2 As shown in [#175](https://github.com/jpmens/jo/issues/175) when using _git_ on Windows WSL2 it should be necessary to disable automatic CRLF conversion in _git_ or the tests will fail: ```cmd git config --local core.autocrlf false ``` ### AIX _jo_ builds and passes all tests on AIX 7.1 using the _autoconf_, _automake_, _gcc_, and _pkg-config_ RPMs from IBM's [AIX Toolbox for Open Source Software](https://www.ibm.com/support/pages/node/883796). The _xlclang_ compiler from IBM's xlC/C++ suite for AIX will also build _jo_. ## Others * [voidlinux](https://github.com/voidlinux/void-packages/tree/master/srcpkgs/jo) * [ArchLinux](https://archlinux.org/packages/community/x86_64/jo/) * [OpenBSD](http://openports.se/textproc/jo) * [FreeBSD](https://www.freshports.org/textproc/jo) * [Guix](https://guix.gnu.org/packages/jo-1.4/) * [pkgsrc](http://pkgsrc.se/textproc/jo) * [repology.org](https://repology.org/metapackage/jo/versions) * [Docker](https://hub.docker.com/repository/docker/jpmens/jo) ## See also * [gjo](https://github.com/skanehira/gjo) * [rjo](https://github.com/dskkato/rjo) * [jjo](https://github.com/memoryhole/jjo) ## Credits * `json.[ch]` by 2011 Joseph A. Adams (joeyadams3.14159[at]gmail.com). jo-1.9/base64.c000066400000000000000000000045451433077231700132420ustar00rootroot00000000000000/* This code is public domain software. */ #include "base64.h" #include #include // base64 encoding // // buf: binary input data // size: size of input (bytes) // return: base64-encoded string (null-terminated) // memory for output will be allocated here, free it later // char* base64_encode(const void* buf, size_t size) { static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; char* str = (char*) malloc((size+3)*4/3 + 1); char* p = str; const unsigned char* q = (const unsigned char*) buf; size_t i = 0; if (str == NULL) { return NULL; } while (i < size) { int c = q[i++]; c *= 256; if (i < size) c += q[i]; i++; c *= 256; if (i < size) c += q[i]; i++; *p++ = base64[(c & 0x00fc0000) >> 18]; *p++ = base64[(c & 0x0003f000) >> 12]; if (i > size + 1) *p++ = '='; else *p++ = base64[(c & 0x00000fc0) >> 6]; if (i > size) *p++ = '='; else *p++ = base64[c & 0x0000003f]; } *p = 0; return str; } // single base64 character conversion // static int POS(char c) { if (c>='A' && c<='Z') return c - 'A'; if (c>='a' && c<='z') return c - 'a' + 26; if (c>='0' && c<='9') return c - '0' + 52; if (c == '+') return 62; if (c == '/') return 63; if (c == '=') return -1; return -2; } // base64 decoding // // s: base64 string, must be null-terminated // data: output buffer for decoded data // data_len size of decoded data // return: allocated data buffer // void* base64_decode(const char* s, size_t *data_len) { const char *p; unsigned char *q, *data; int n[4] = { 0, 0, 0, 0 }; size_t len = strlen(s); if (len % 4) return NULL; data = (unsigned char*) malloc(len/4*3); q = (unsigned char*) data; for (p = s; *p; ) { n[0] = POS(*p++); n[1] = POS(*p++); n[2] = POS(*p++); n[3] = POS(*p++); if (n[0] == -2 || n[1] == -2 || n[2] == -2 || n[3] == -2) return NULL; if (n[0] == -1 || n[1] == -1) return NULL; if (n[2] == -1 && n[3] != -1) return NULL; q[0] = (n[0] << 2) + (n[1] >> 4); if (n[2] != -1) q[1] = ((n[1] & 15) << 4) + (n[2] >> 2); if (n[3] != -1) q[2] = ((n[2] & 3) << 6) + n[3]; q += 3; } *data_len = q-data - (n[2]==-1) - (n[3]==-1); return data; } jo-1.9/base64.h000066400000000000000000000002651433077231700132420ustar00rootroot00000000000000#ifndef BASE64_H_GUARD #define BASE64_H_GUARD #include char* base64_encode(const void* buf, size_t size); void* base64_decode(const char* s, size_t *data_len); #endif jo-1.9/build-aux/000077500000000000000000000000001433077231700136745ustar00rootroot00000000000000jo-1.9/build-aux/tap-driver.sh000077500000000000000000000460721433077231700163210ustar00rootroot00000000000000#! /bin/sh # Copyright (C) 2011-2013 Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . scriptversion=2011-12-27.17; # UTC # Make unconditional expansion of undefined variables an error. This # helps a lot in preventing typo-related bugs. set -u me=tap-driver.sh fatal () { echo "$me: fatal: $*" >&2 exit 1 } usage_error () { echo "$me: $*" >&2 print_usage >&2 exit 2 } print_usage () { cat < # trap : 1 3 2 13 15 if test $merge -gt 0; then exec 2>&1 else exec 2>&3 fi "$@" echo $? ) | LC_ALL=C ${AM_TAP_AWK-awk} \ -v me="$me" \ -v test_script_name="$test_name" \ -v log_file="$log_file" \ -v trs_file="$trs_file" \ -v expect_failure="$expect_failure" \ -v merge="$merge" \ -v ignore_exit="$ignore_exit" \ -v comments="$comments" \ -v diag_string="$diag_string" \ ' # FIXME: the usages of "cat >&3" below could be optimized when using # FIXME: GNU awk, and/on on systems that supports /dev/fd/. # Implementation note: in what follows, `result_obj` will be an # associative array that (partly) simulates a TAP result object # from the `TAP::Parser` perl module. ## ----------- ## ## FUNCTIONS ## ## ----------- ## function fatal(msg) { print me ": " msg | "cat >&2" exit 1 } function abort(where) { fatal("internal error " where) } # Convert a boolean to a "yes"/"no" string. function yn(bool) { return bool ? "yes" : "no"; } function add_test_result(result) { if (!test_results_index) test_results_index = 0 test_results_list[test_results_index] = result test_results_index += 1 test_results_seen[result] = 1; } # Whether the test script should be re-run by "make recheck". function must_recheck() { for (k in test_results_seen) if (k != "XFAIL" && k != "PASS" && k != "SKIP") return 1 return 0 } # Whether the content of the log file associated to this test should # be copied into the "global" test-suite.log. function copy_in_global_log() { for (k in test_results_seen) if (k != "PASS") return 1 return 0 } # FIXME: this can certainly be improved ... function get_global_test_result() { if ("ERROR" in test_results_seen) return "ERROR" if ("FAIL" in test_results_seen || "XPASS" in test_results_seen) return "FAIL" all_skipped = 1 for (k in test_results_seen) if (k != "SKIP") all_skipped = 0 if (all_skipped) return "SKIP" return "PASS"; } function stringify_result_obj(result_obj) { if (result_obj["is_unplanned"] || result_obj["number"] != testno) return "ERROR" if (plan_seen == LATE_PLAN) return "ERROR" if (result_obj["directive"] == "TODO") return result_obj["is_ok"] ? "XPASS" : "XFAIL" if (result_obj["directive"] == "SKIP") return result_obj["is_ok"] ? "SKIP" : COOKED_FAIL; if (length(result_obj["directive"])) abort("in function stringify_result_obj()") return result_obj["is_ok"] ? COOKED_PASS : COOKED_FAIL } function decorate_result(result) { color_name = color_for_result[result] if (color_name) return color_map[color_name] "" result "" color_map["std"] # If we are not using colorized output, or if we do not know how # to colorize the given result, we should return it unchanged. return result } function report(result, details) { if (result ~ /^(X?(PASS|FAIL)|SKIP|ERROR)/) { msg = ": " test_script_name add_test_result(result) } else if (result == "#") { msg = " " test_script_name ":" } else { abort("in function report()") } if (length(details)) msg = msg " " details # Output on console might be colorized. print decorate_result(result) msg # Log the result in the log file too, to help debugging (this is # especially true when said result is a TAP error or "Bail out!"). print result msg | "cat >&3"; } function testsuite_error(error_message) { report("ERROR", "- " error_message) } function handle_tap_result() { details = result_obj["number"]; if (length(result_obj["description"])) details = details " " result_obj["description"] if (plan_seen == LATE_PLAN) { details = details " # AFTER LATE PLAN"; } else if (result_obj["is_unplanned"]) { details = details " # UNPLANNED"; } else if (result_obj["number"] != testno) { details = sprintf("%s # OUT-OF-ORDER (expecting %d)", details, testno); } else if (result_obj["directive"]) { details = details " # " result_obj["directive"]; if (length(result_obj["explanation"])) details = details " " result_obj["explanation"] } report(stringify_result_obj(result_obj), details) } # `skip_reason` should be empty whenever planned > 0. function handle_tap_plan(planned, skip_reason) { planned += 0 # Avoid getting confused if, say, `planned` is "00" if (length(skip_reason) && planned > 0) abort("in function handle_tap_plan()") if (plan_seen) { # Error, only one plan per stream is acceptable. testsuite_error("multiple test plans") return; } planned_tests = planned # The TAP plan can come before or after *all* the TAP results; we speak # respectively of an "early" or a "late" plan. If we see the plan line # after at least one TAP result has been seen, assume we have a late # plan; in this case, any further test result seen after the plan will # be flagged as an error. plan_seen = (testno >= 1 ? LATE_PLAN : EARLY_PLAN) # If testno > 0, we have an error ("too many tests run") that will be # automatically dealt with later, so do not worry about it here. If # $plan_seen is true, we have an error due to a repeated plan, and that # has already been dealt with above. Otherwise, we have a valid "plan # with SKIP" specification, and should report it as a particular kind # of SKIP result. if (planned == 0 && testno == 0) { if (length(skip_reason)) skip_reason = "- " skip_reason; report("SKIP", skip_reason); } } function extract_tap_comment(line) { if (index(line, diag_string) == 1) { # Strip leading `diag_string` from `line`. line = substr(line, length(diag_string) + 1) # And strip any leading and trailing whitespace left. sub("^[ \t]*", "", line) sub("[ \t]*$", "", line) # Return what is left (if any). return line; } return ""; } # When this function is called, we know that line is a TAP result line, # so that it matches the (perl) RE "^(not )?ok\b". function setup_result_obj(line) { # Get the result, and remove it from the line. result_obj["is_ok"] = (substr(line, 1, 2) == "ok" ? 1 : 0) sub("^(not )?ok[ \t]*", "", line) # If the result has an explicit number, get it and strip it; otherwise, # automatically assing the next progresive number to it. if (line ~ /^[0-9]+$/ || line ~ /^[0-9]+[^a-zA-Z0-9_]/) { match(line, "^[0-9]+") # The final `+ 0` is to normalize numbers with leading zeros. result_obj["number"] = substr(line, 1, RLENGTH) + 0 line = substr(line, RLENGTH + 1) } else { result_obj["number"] = testno } if (plan_seen == LATE_PLAN) # No further test results are acceptable after a "late" TAP plan # has been seen. result_obj["is_unplanned"] = 1 else if (plan_seen && testno > planned_tests) result_obj["is_unplanned"] = 1 else result_obj["is_unplanned"] = 0 # Strip trailing and leading whitespace. sub("^[ \t]*", "", line) sub("[ \t]*$", "", line) # This will have to be corrected if we have a "TODO"/"SKIP" directive. result_obj["description"] = line result_obj["directive"] = "" result_obj["explanation"] = "" if (index(line, "#") == 0) return # No possible directive, nothing more to do. # Directives are case-insensitive. rx = "[ \t]*#[ \t]*([tT][oO][dD][oO]|[sS][kK][iI][pP])[ \t]*" # See whether we have the directive, and if yes, where. pos = match(line, rx "$") if (!pos) pos = match(line, rx "[^a-zA-Z0-9_]") # If there was no TAP directive, we have nothing more to do. if (!pos) return # Let`s now see if the TAP directive has been escaped. For example: # escaped: ok \# SKIP # not escaped: ok \\# SKIP # escaped: ok \\\\\# SKIP # not escaped: ok \ # SKIP if (substr(line, pos, 1) == "#") { bslash_count = 0 for (i = pos; i > 1 && substr(line, i - 1, 1) == "\\"; i--) bslash_count += 1 if (bslash_count % 2) return # Directive was escaped. } # Strip the directive and its explanation (if any) from the test # description. result_obj["description"] = substr(line, 1, pos - 1) # Now remove the test description from the line, that has been dealt # with already. line = substr(line, pos) # Strip the directive, and save its value (normalized to upper case). sub("^[ \t]*#[ \t]*", "", line) result_obj["directive"] = toupper(substr(line, 1, 4)) line = substr(line, 5) # Now get the explanation for the directive (if any), with leading # and trailing whitespace removed. sub("^[ \t]*", "", line) sub("[ \t]*$", "", line) result_obj["explanation"] = line } function get_test_exit_message(status) { if (status == 0) return "" if (status !~ /^[1-9][0-9]*$/) abort("getting exit status") if (status < 127) exit_details = "" else if (status == 127) exit_details = " (command not found?)" else if (status >= 128 && status <= 255) exit_details = sprintf(" (terminated by signal %d?)", status - 128) else if (status > 256 && status <= 384) # We used to report an "abnormal termination" here, but some Korn # shells, when a child process die due to signal number n, can leave # in $? an exit status of 256+n instead of the more standard 128+n. # Apparently, both behaviours are allowed by POSIX (2008), so be # prepared to handle them both. See also Austing Group report ID # 0000051 exit_details = sprintf(" (terminated by signal %d?)", status - 256) else # Never seen in practice. exit_details = " (abnormal termination)" return sprintf("exited with status %d%s", status, exit_details) } function write_test_results() { print ":global-test-result: " get_global_test_result() > trs_file print ":recheck: " yn(must_recheck()) > trs_file print ":copy-in-global-log: " yn(copy_in_global_log()) > trs_file for (i = 0; i < test_results_index; i += 1) print ":test-result: " test_results_list[i] > trs_file close(trs_file); } BEGIN { ## ------- ## ## SETUP ## ## ------- ## '"$init_colors"' # Properly initialized once the TAP plan is seen. planned_tests = 0 COOKED_PASS = expect_failure ? "XPASS": "PASS"; COOKED_FAIL = expect_failure ? "XFAIL": "FAIL"; # Enumeration-like constants to remember which kind of plan (if any) # has been seen. It is important that NO_PLAN evaluates "false" as # a boolean. NO_PLAN = 0 EARLY_PLAN = 1 LATE_PLAN = 2 testno = 0 # Number of test results seen so far. bailed_out = 0 # Whether a "Bail out!" directive has been seen. # Whether the TAP plan has been seen or not, and if yes, which kind # it is ("early" is seen before any test result, "late" otherwise). plan_seen = NO_PLAN ## --------- ## ## PARSING ## ## --------- ## is_first_read = 1 while (1) { # Involutions required so that we are able to read the exit status # from the last input line. st = getline if (st < 0) # I/O error. fatal("I/O error while reading from input stream") else if (st == 0) # End-of-input { if (is_first_read) abort("in input loop: only one input line") break } if (is_first_read) { is_first_read = 0 nextline = $0 continue } else { curline = nextline nextline = $0 $0 = curline } # Copy any input line verbatim into the log file. print | "cat >&3" # Parsing of TAP input should stop after a "Bail out!" directive. if (bailed_out) continue # TAP test result. if ($0 ~ /^(not )?ok$/ || $0 ~ /^(not )?ok[^a-zA-Z0-9_]/) { testno += 1 setup_result_obj($0) handle_tap_result() } # TAP plan (normal or "SKIP" without explanation). else if ($0 ~ /^1\.\.[0-9]+[ \t]*$/) { # The next two lines will put the number of planned tests in $0. sub("^1\\.\\.", "") sub("[^0-9]*$", "") handle_tap_plan($0, "") continue } # TAP "SKIP" plan, with an explanation. else if ($0 ~ /^1\.\.0+[ \t]*#/) { # The next lines will put the skip explanation in $0, stripping # any leading and trailing whitespace. This is a little more # tricky in truth, since we want to also strip a potential leading # "SKIP" string from the message. sub("^[^#]*#[ \t]*(SKIP[: \t][ \t]*)?", "") sub("[ \t]*$", ""); handle_tap_plan(0, $0) } # "Bail out!" magic. # Older versions of prove and TAP::Harness (e.g., 3.17) did not # recognize a "Bail out!" directive when preceded by leading # whitespace, but more modern versions (e.g., 3.23) do. So we # emulate the latter, "more modern" behaviour. else if ($0 ~ /^[ \t]*Bail out!/) { bailed_out = 1 # Get the bailout message (if any), with leading and trailing # whitespace stripped. The message remains stored in `$0`. sub("^[ \t]*Bail out![ \t]*", ""); sub("[ \t]*$", ""); # Format the error message for the bailout_message = "Bail out!" if (length($0)) bailout_message = bailout_message " " $0 testsuite_error(bailout_message) } # Maybe we have too look for dianogtic comments too. else if (comments != 0) { comment = extract_tap_comment($0); if (length(comment)) report("#", comment); } } ## -------- ## ## FINISH ## ## -------- ## # A "Bail out!" directive should cause us to ignore any following TAP # error, as well as a non-zero exit status from the TAP producer. if (!bailed_out) { if (!plan_seen) { testsuite_error("missing test plan") } else if (planned_tests != testno) { bad_amount = testno > planned_tests ? "many" : "few" testsuite_error(sprintf("too %s tests run (expected %d, got %d)", bad_amount, planned_tests, testno)) } if (!ignore_exit) { # Fetch exit status from the last line. exit_message = get_test_exit_message(nextline) if (exit_message) testsuite_error(exit_message) } } write_test_results() exit 0 } # End of "BEGIN" block. ' # TODO: document that we consume the file descriptor 3 :-( } 3>"$log_file" test $? -eq 0 || fatal "I/O or internal error" # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: jo-1.9/configure.ac000066400000000000000000000032301433077231700142660ustar00rootroot00000000000000AC_PREREQ([2.63]) AC_INIT([jo], [1.9], [jp@mens.de]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_SRCDIR([jo.c]) # Checks for programs. AC_PROG_CC # Checks for header files. AC_USE_SYSTEM_EXTENSIONS AC_CHECK_HEADERS([stddef.h stdint.h stdlib.h string.h unistd.h stdbool.h]) # Checks for library functions. # AC_FUNC_MALLOC # AC_FUNC_REALLOC AC_FUNC_STRTOD AC_CHECK_FUNCS([strchr strrchr strlcpy strlcat snprintf pledge err errx]) # backport PKG_CHECK_VAR from pkgconfig 0.29 m4_ifndef([PKG_CHECK_VAR], [AC_DEFUN([PKG_CHECK_VAR], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl _PKG_CONFIG([$1], [variable="][$3]["], [$2]) AS_VAR_COPY([$1], [pkg_cv_][$1]) AS_VAR_IF([$1], [""], [$5], [$4])dnl ])dnl PKG_CHECK_VAR ]) AM_INIT_AUTOMAKE([foreign -Wall]) AM_SILENT_RULES([yes]) AC_REQUIRE_AUX_FILE([tap-driver.sh]) AC_ARG_VAR(PANDOC, [pandoc path]) AC_PATH_PROG(PANDOC, [pandoc], []) if test -z "$PANDOC" then AC_MSG_WARN([pandoc not found, man pages rebuild will not be possible]) fi AM_CONDITIONAL([USE_PANDOC], [test -n "$PANDOC"]) PKG_CHECK_VAR(bashcompdir, [bash-completion], [completionsdir], , bashcompdir="${sysconfdir}/bash_completion.d") AC_SUBST(bashcompdir) AC_CONFIG_FILES([Makefile tests/jo.07.sh]) AC_OUTPUT echo " Jo.............: version $PACKAGE_VERSION Prefix.........: $prefix C compiler.....: $CC $CFLAGS $CPPFLAGS Pandoc.........: ${PANDOC:-NONE} Bash completion: $bashcompdir/jo.bash Now type 'make @<:@@:>@' where the optional is: all - build all binaries check - run the tests install - install everything " jo-1.9/debian/000077500000000000000000000000001433077231700132245ustar00rootroot00000000000000jo-1.9/debian/changelog000066400000000000000000000001761433077231700151020ustar00rootroot00000000000000jo (1.0) trusty; urgency=low * Initial packaging for jo -- Ross Duggan Sat, 12 Mar 2016 18:30:00 +0100 jo-1.9/debian/compat000066400000000000000000000000021433077231700144220ustar00rootroot000000000000008 jo-1.9/debian/control000066400000000000000000000004241433077231700146270ustar00rootroot00000000000000Source: jo Section: devel Priority: optional Maintainer: Jan-Piet Mens Build-Depends: debhelper (>= 8.0.0), dh-autoreconf, libtool, autoconf (>= 2.69), make Standards-Version: 3.9.3 Package: jo Architecture: any Description: A shell command to create JSON jo-1.9/debian/copyright000066400000000000000000000015351433077231700151630ustar00rootroot00000000000000Format: http://dep.debian.net/deps/dep5 Upstream-Name: jo Source: https://github.com/jpmens/jo Files: * Copyright: Jan-Piet Mens License: GPLv2 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. . This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. . You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. jo-1.9/debian/dirs000066400000000000000000000000331433077231700141040ustar00rootroot00000000000000usr/bin usr/share/man/man1 jo-1.9/debian/docs000066400000000000000000000000001433077231700140650ustar00rootroot00000000000000jo-1.9/debian/jo.manpages000066400000000000000000000000051433077231700153440ustar00rootroot00000000000000jo.1 jo-1.9/debian/rules000077500000000000000000000007141433077231700143060ustar00rootroot00000000000000#!/usr/bin/make -f # -*- makefile -*- # Sample debian/rules that uses debhelper. # This file was originally written by Joey Hess and Craig Small. # As a special exception, when this file is copied by dh-make into a # dh-make output file, you may use that output file without restriction. # This special exception was added by Craig Small in version 0.37 of dh-make. # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 %: dh $@ --with autoreconf jo-1.9/debian/source/000077500000000000000000000000001433077231700145245ustar00rootroot00000000000000jo-1.9/debian/source/format000066400000000000000000000000141433077231700157320ustar00rootroot000000000000003.0 (quilt) jo-1.9/jo.1000066400000000000000000000272421433077231700125030ustar00rootroot00000000000000'\" t .\" Automatically generated by Pandoc 2.12 .\" .TH "JO" "1" "" "User Manuals" "" .hy .SH NAME .PP jo - JSON output from a shell .SH SYNOPSIS .PP jo [-p] [-a] [-B] [-D] [-e] [-n] [-v] [-V] [-d keydelim] [-f file] [\[en]] [ [-s|-n|-b] word \&...] .SH DESCRIPTION .PP \f[I]jo\f[R] creates a JSON string on \f[I]stdout\f[R] from \f[I]word\f[R]s given it as arguments or read from \f[I]stdin\f[R]. If \f[C]-f\f[R] is specified, \f[I]jo\f[R] first loads the contents of \f[I]file\f[R] as a JSON object or array, then modifies it with subsequent \f[I]word\f[R]s before printing the final JSON string to \f[I]stdout\f[R]. \f[I]file\f[R] may be specified as \f[C]-\f[R] to read from \f[I]jo\f[R]\[cq]s standard input; this takes precedence over reading \f[I]word\f[R]s from \f[I]stdin\f[R]. .PP Without option \f[C]-a\f[R] it generates an object whereby each \f[I]word\f[R] is a \f[C]key=value\f[R] (or \f[C]key\[at]value\f[R]) pair with \f[I]key\f[R] being the JSON object element and \f[I]value\f[R] its value. \f[I]jo\f[R] attempts to guess the type of \f[I]value\f[R] in order to create number (using \f[I]strtod(3)\f[R]), string, or null values in JSON. .PP A missing or empty \f[I]value\f[R] normally results in an element whose value is \f[C]null\f[R]. If \f[C]-n\f[R] is specified, this element is not created. .PP \f[I]jo\f[R] normally treats \f[I]key\f[R] as a literal string value. If the \f[C]-d\f[R] option is specified, \f[I]key\f[R] will be interpreted as an \f[I]object path\f[R], whose individual components are separated by the first character of \f[I]keydelim\f[R]. .PP \f[I]jo\f[R] normally treats \f[I]value\f[R] as a literal string value, unless it begins with one of the following characters: .PP .TS tab(@); l l. T{ value T}@T{ action T} _ T{ \[at]file T}@T{ substitute the contents of \f[I]file\f[R] as-is T} T{ %file T}@T{ substitute the contents of \f[I]file\f[R] in base64-encoded form T} T{ :file T}@T{ interpret the contents of \f[I]file\f[R] as JSON, and substitute the result T} .TE .PP Escape the special character with a backslash to prevent this interpretation. .PP \f[I]jo\f[R] treats \f[C]key\[at]value\f[R] specifically as boolean JSON elements: if the value begins with \f[C]T\f[R], \f[C]t\f[R], or the numeric value is greater than zero, the result is \f[C]true\f[R], else \f[C]false\f[R]. .PP \f[I]jo\f[R] creates an array instead of an object when \f[C]-a\f[R] is specified. .PP When the \f[C]:=\f[R] operator is used in a \f[I]word\f[R], the name to the right of \f[C]:=\f[R] is a file containing JSON which is parsed and assigned to the key left of the operator. The file may be specified as \f[C]-\f[R] to read from \f[I]jo\f[R]\[cq]s standard input. .SH TYPE COERCION .PP \f[I]jo\f[R]\[cq]s type guesses can be overridden on a per-word basis by prefixing \f[I]word\f[R] with \f[C]-s\f[R] for \f[I]string\f[R], \f[C]-n\f[R] for \f[I]number\f[R], or \f[C]-b\f[R] for \f[I]boolean\f[R]. The list of \f[I]word\f[R]s \f[I]must\f[R] be prefixed with \f[C]--\f[R], to indicate to \f[I]jo\f[R] that there are no more global options. .PP Type coercion works as follows: .PP .TS tab(@); l l l l l. T{ word T}@T{ -s T}@T{ -n T}@T{ -b T}@T{ default T} _ T{ a= T}@T{ \[lq]a\[rq]:\[dq]\[dq] T}@T{ \[lq]a\[rq]:0 T}@T{ \[lq]a\[rq]:false T}@T{ \[lq]a\[rq]:null T} T{ a=string T}@T{ \[lq]a\[rq]:\[lq]string\[rq] T}@T{ \[lq]a\[rq]:6 T}@T{ \[lq]a\[rq]:true T}@T{ \[lq]a\[rq]:\[lq]string\[rq] T} T{ a=\[dq]quoted\[dq] T}@T{ \[lq]a\[rq]:\[lq]\[dq]quoted\[dq]\[rq] T}@T{ \[lq]a\[rq]:8 T}@T{ \[lq]a\[rq]:true T}@T{ \[lq]a\[rq]:\[lq]\[dq]quoted\[dq]\[rq] T} T{ a=12345 T}@T{ \[lq]a\[rq]:\[lq]12345\[rq] T}@T{ \[lq]a\[rq]:12345 T}@T{ \[lq]a\[rq]:true T}@T{ \[lq]a\[rq]:12345 T} T{ a=true T}@T{ \[lq]a\[rq]:\[lq]true\[rq] T}@T{ \[lq]a\[rq]:1 T}@T{ \[lq]a\[rq]:true T}@T{ \[lq]a\[rq]:true T} T{ a=false T}@T{ \[lq]a\[rq]:\[lq]false\[rq] T}@T{ \[lq]a\[rq]:0 T}@T{ \[lq]a\[rq]:false T}@T{ \[lq]a\[rq]:false T} T{ a=null T}@T{ \[lq]a\[rq]:\[dq]\[dq] T}@T{ \[lq]a\[rq]:0 T}@T{ \[lq]a\[rq]:false T}@T{ \[lq]a\[rq]:null T} .TE .PP Coercing a non-number string to number outputs the \f[I]length\f[R] of the string. .PP Coercing a non-boolean string to boolean outputs \f[C]false\f[R] if the string is empty, \f[C]true\f[R] otherwise. .PP Type coercion only applies to \f[C]key=value\f[R] words, and individual words in a \f[C]-a\f[R] array. Coercing other words has no effect. .SH EXAMPLES .PP Create an object. Note how the incorrectly-formatted float value becomes a string: .IP .nf \f[C] $ jo tst=1457081292 lat=12.3456 cc=FR badfloat=3.14159.26 name=\[dq]JP Mens\[dq] nada= coffee\[at]T {\[dq]tst\[dq]:1457081292,\[dq]lat\[dq]:12.3456,\[dq]cc\[dq]:\[dq]FR\[dq],\[dq]badfloat\[dq]:\[dq]3.14159.26\[dq],\[dq]name\[dq]:\[dq]JP Mens\[dq],\[dq]nada\[dq]:null,\[dq]coffee\[dq]:true} \f[R] .fi .PP Pretty-print an array with a list of files in the current directory: .IP .nf \f[C] $ jo -p -a * [ \[dq]Makefile\[dq], \[dq]README.md\[dq], \[dq]jo.1\[dq], \[dq]jo.c\[dq], \[dq]jo.pandoc\[dq], \[dq]json.c\[dq], \[dq]json.h\[dq] ] \f[R] .fi .PP Create objects within objects; this works because if the first character of value is an open brace or a bracket we attempt to decode the remainder as JSON. Beware spaces in strings \&... .IP .nf \f[C] $ jo -p name=JP object=$(jo fruit=Orange hungry\[at]0 point=$(jo x=10 y=20 list=$(jo -a 1 2 3 4 5)) number=17) sunday\[at]0 { \[dq]name\[dq]: \[dq]JP\[dq], \[dq]object\[dq]: { \[dq]fruit\[dq]: \[dq]Orange\[dq], \[dq]hungry\[dq]: false, \[dq]point\[dq]: { \[dq]x\[dq]: 10, \[dq]y\[dq]: 20, \[dq]list\[dq]: [ 1, 2, 3, 4, 5 ] }, \[dq]number\[dq]: 17 }, \[dq]sunday\[dq]: false } \f[R] .fi .PP Booleans as strings or as boolean (pay particular attention to \f[I]switch\f[R]; the \f[C]-B\f[R] option disables the default detection of the \[lq]\f[C]true\f[R]\[rq], \[lq]\f[C]false\f[R]\[rq], and \[lq]\f[C]null\f[R]\[rq] strings): .IP .nf \f[C] $ jo switch=true morning\[at]0 {\[dq]switch\[dq]:true,\[dq]morning\[dq]:false} $ jo -B switch=true morning\[at]0 {\[dq]switch\[dq]:\[dq]true\[dq],\[dq]morning\[dq]:false} \f[R] .fi .PP Elements (objects and arrays) can be nested. The following example nests an array called \f[I]point\f[R] and an object named \f[I]geo\f[R]: .IP .nf \f[C] $ jo -p name=Jane point[]=1 point[]=2 geo[lat]=10 geo[lon]=20 { \[dq]name\[dq]: \[dq]Jane\[dq], \[dq]point\[dq]: [ 1, 2 ], \[dq]geo\[dq]: { \[dq]lat\[dq]: 10, \[dq]lon\[dq]: 20 } } \f[R] .fi .PP The same example, using object paths: .IP .nf \f[C] $ jo -p -d. name=Jane point[]=1 point[]=2 geo.lat=10 geo.lon=20 { \[dq]name\[dq]: \[dq]Jane\[dq], \[dq]point\[dq]: [ 1, 2 ], \[dq]geo\[dq]: { \[dq]lat\[dq]: 10, \[dq]lon\[dq]: 20 } } \f[R] .fi .PP Without \f[C]-d\f[R], a different object is generated: .IP .nf \f[C] $ jo -p name=Jane point[]=1 point[]=2 geo.lat=10 geo.lon=20 { \[dq]name\[dq]: \[dq]Jane\[dq], \[dq]point\[dq]: [ 1, 2 ], \[dq]geo.lat\[dq]: 10, \[dq]geo.lon\[dq]: 20 } \f[R] .fi .PP Create empty objects or arrays, intentionally or potentially: .IP .nf \f[C] $ jo < /dev/null {} $ MY_ARRAY=(a=1 b=2) $ jo -a \[dq]${MY_ARRAY[\[at]]}\[dq] < /dev/null [\[dq]a=1\[dq],\[dq]b=2\[dq]] \f[R] .fi .PP Type coercion: .IP .nf \f[C] $ jo -p -- -s a=true b=true -s c=123 d=123 -b e=\[dq]1\[dq] -b f=\[dq]true\[dq] -n g=\[dq]This is a test\[dq] -b h=\[dq]This is a test\[dq] { \[dq]a\[dq]: \[dq]true\[dq], \[dq]b\[dq]: true, \[dq]c\[dq]: \[dq]123\[dq], \[dq]d\[dq]: 123, \[dq]e\[dq]: true, \[dq]f\[dq]: true, \[dq]g\[dq]: 14, \[dq]h\[dq]: true } $ jo -a -- -s 123 -n \[dq]This is a test\[dq] -b C_Rocks 456 [\[dq]123\[dq],14,true,456] \f[R] .fi .PP Read element values from files: a value which starts with \f[C]\[at]\f[R] is read in plain whereas if it begins with a \f[C]%\f[R] it will be base64-encoded and if it starts with \f[C]:\f[R] the contents are interpreted as JSON: .IP .nf \f[C] $ jo program=jo authors=\[at]AUTHORS {\[dq]program\[dq]:\[dq]jo\[dq],\[dq]authors\[dq]:\[dq]Jan-Piet Mens \[dq]} $ jo filename=AUTHORS content=%AUTHORS {\[dq]filename\[dq]:\[dq]AUTHORS\[dq],\[dq]content\[dq]:\[dq]SmFuLVBpZXQgTWVucyA8anBtZW5zQGdtYWlsLmNvbT4K\[dq]} $ jo nested=:nested.json {\[dq]nested\[dq]:{\[dq]field1\[dq]:123,\[dq]field2\[dq]:\[dq]abc\[dq]}} \f[R] .fi .PP These characters can be escaped to avoid interpretation: .IP .nf \f[C] $ jo name=\[dq]JP Mens\[dq] twitter=\[aq]\[rs]\[at]jpmens\[aq] {\[dq]name\[dq]:\[dq]JP Mens\[dq],\[dq]twitter\[dq]:\[dq]\[at]jpmens\[dq]} $ jo char=\[dq] \[dq] URIescape=\[rs]\[rs]%20 {\[dq]char\[dq]:\[dq] \[dq],\[dq]URIescape\[dq]:\[dq]%20\[dq]} $ jo action=\[dq]split window\[dq] vimcmd=\[dq]\[rs]:split\[dq] {\[dq]action\[dq]:\[dq]split window\[dq],\[dq]vimcmd\[dq]:\[dq]:split\[dq]} \f[R] .fi .PP Read element values from a file in order to overcome ARG_MAX limits during object assignment: .IP .nf \f[C] $ ls | jo -a > child.json $ jo files:=child.json {\[dq]files\[dq]:[\[dq]AUTHORS\[dq],\[dq]COPYING\[dq],\[dq]ChangeLog\[dq] .... $ ls *.c | jo -a > source.json; ls *.h | jo -a > headers.json $ jo -a :source.json :headers.json [[\[dq]base64.c\[dq],\[dq]jo.c\[dq],\[dq]json.c\[dq]],[\[dq]base64.h\[dq],\[dq]json.h\[dq]]] \f[R] .fi .PP Add elements to existing JSON: .IP .nf \f[C] $ jo -f source.json 1 | jo -f - 2 3 [\[dq]base64.c\[dq],\[dq]jo.c\[dq],\[dq]json.c\[dq],1,2,3] $ curl -s \[aq]https://noembed.com/embed?url=https://www.youtube.com/watch?v=dQw4w9WgXcQ\[aq] | jo -f - status=Rickrolled { ...., \[dq]type\[dq]:\[dq]video\[dq],\[dq]author_url\[dq]:\[dq]https://www.youtube.com/user/RickAstleyVEVO\[dq],\[dq]status\[dq]:\[dq]Rickrolled\[dq]} \f[R] .fi .PP Deduplicate object keys (\f[I]jo\f[R] appends duplicate object keys by default): .IP .nf \f[C] $ jo a=1 b=2 a=3 {\[dq]a\[dq]:1,\[dq]b\[dq]:2,\[dq]a\[dq]:3} $ jo -D a=1 b=2 a=3 {\[dq]a\[dq]:3,\[dq]b\[dq]:2} \f[R] .fi .SH OPTIONS .PP \f[I]jo\f[R] understands the following global options. .TP -a Interpret the list of \f[I]words\f[R] as array values and produce an array instead of an object. .TP -B By default, \f[I]jo\f[R] interprets the strings \[lq]\f[C]true\f[R]\[rq] and \[lq]\f[C]false\f[R]\[rq] as boolean elements \f[C]true\f[R] and \f[C]false\f[R] respectively, and \[lq]\f[C]null\f[R]\[rq] as \f[C]null\f[R]. Disable with this option. .TP -D Deduplicate object keys. .TP -e Ignore empty stdin (i.e.\ don\[cq]t produce a diagnostic error when \f[I]stdin\f[R] is empty) .TP -n Do not add keys with empty values. .TP -p Pretty-print the JSON string on output instead of the terse one-line output it prints by default. .TP -v Show version and exit. .TP -V Show version as a JSON object and exit. .SH BUGS .PP Probably. .PP If a value given to \f[I]jo\f[R] expands to empty in the shell, then \f[I]jo\f[R] produces a \f[C]null\f[R] in object mode, and might appear to hang in array mode; it is not hanging, rather it\[cq]s reading \f[I]stdin\f[R]. This is not a bug. .PP Numeric values are converted to numbers which can produce undesired results. If you quote a numeric value, \f[I]jo\f[R] will make it a string. Compare the following: .IP .nf \f[C] $ jo a=1.0 {\[dq]a\[dq]:1} $ jo a=\[rs]\[dq]1.0\[rs]\[dq] {\[dq]a\[dq]:\[dq]1.0\[dq]} \f[R] .fi .PP Omitting a closing bracket on a nested element causes a diagnostic message to print, but the output contains garbage anyway. This was designed thusly. .SH RETURN CODES .PP \f[I]jo\f[R] exits with a code 0 on success and non-zero on failure after indicating what caused the failure. .SH AVAILABILITY .PP .SH CREDITS .IP \[bu] 2 This program uses \f[C]json.[ch]\f[R], by Joseph A. Adams. .SH SEE ALSO .IP \[bu] 2 .IP \[bu] 2 .IP \[bu] 2 .IP \[bu] 2 strtod(3) .SH AUTHOR .PP Jan-Piet Mens jo-1.9/jo.bash000066400000000000000000000017661433077231700132630ustar00rootroot00000000000000# bash completion for jo(1) _jo() { # Don't split words on =, for =@ and =% handling COMP_WORDBREAKS=${COMP_WORDBREAKS//=} # No completion if an exit causing flag is around local i for i in ${!COMP_WORDS[@]}; do [[ $i -ne $COMP_CWORD && ${COMP_WORDS[i]} == -*[hvV]* ]] && return 0 done # Complete available options following a dash if [[ $2 == -* ]]; then COMPREPLY=( $(compgen -W '-a -B -h -p -v -V' -- "$2") ) return 0 fi # Complete filenames on =@ and =% if [[ $2 == *=[@%]* ]]; then local file prefix file="${2#*=[@%]}" prefix="${2:0:${#2}-${#file}}" compopt -o filenames COMPREPLY=( $(compgen -f -- "$file") ) if [[ ${#COMPREPLY[@]} -eq 1 ]]; then if [[ -d "${COMPREPLY[0]}" ]]; then COMPREPLY[0]+=/ compopt -o nospace fi COMPREPLY[0]="$prefix${COMPREPLY[0]}" fi return 0 fi } && complete -F _jo jo jo-1.9/jo.c000066400000000000000000000471171433077231700125700ustar00rootroot00000000000000#include #include #include #include #include #include #ifndef _AIX # include #endif #include #if !defined(WIN32) && !defined(_AIX) # include #endif #include "json.h" #include "base64.h" /* * Copyright (C) 2016-2019 Jan-Piet Mens * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #define SPACER " " #define FLAG_ARRAY 0x01 #define FLAG_PRETTY 0x02 #define FLAG_NOBOOL 0x04 #define FLAG_BOOLEAN 0x08 #define FLAG_NOSTDIN 0x10 #define FLAG_SKIPNULLS 0x20 #define FLAG_MASK (FLAG_ARRAY | FLAG_PRETTY | FLAG_NOBOOL | FLAG_BOOLEAN | FLAG_NOSTDIN | FLAG_SKIPNULLS) /* Size of buffer blocks for pipe slurping */ #define SLURP_BLOCK_SIZE 4096 static JsonNode *pile; /* pile of nested objects/arrays */ #if defined(_WIN32) || defined(_AIX) #include #include static inline void err(int eval, const char *fmt, ...) { int errnum = errno; va_list ap; va_start(ap, fmt); fprintf(stderr, "jo: "); vfprintf(stderr, fmt, ap); fprintf(stderr, ": %s\n", strerror(errnum)); va_end(ap); exit(eval); } static inline void errx(int eval, const char *fmt, ...) { va_list ap; va_start(ap, fmt); fprintf(stderr, "jo: "); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); va_end(ap); exit(eval); } #endif #if defined(_WIN32) && !defined(fseeko) # define fseeko fseek # define ftello ftell #endif #define TAG_TO_FLAGS(tag) ((FLAG_MASK + 1) * (tag)) #define TAG_FLAG_BOOL (TAG_TO_FLAGS(JSON_BOOL)) #define TAG_FLAG_STRING (TAG_TO_FLAGS(JSON_STRING)) #define TAG_FLAG_NUMBER (TAG_TO_FLAGS(JSON_NUMBER)) #define COERCE_MASK (TAG_FLAG_BOOL | TAG_FLAG_STRING | TAG_FLAG_NUMBER) JsonTag flags_to_tag(int flags) { return flags / (FLAG_MASK + 1); } void json_copy_to_object(JsonNode * obj, JsonNode * object_or_array, int clobber) { JsonNode *node, *node_child, *obj_child; if (obj->tag != JSON_OBJECT && obj->tag != JSON_ARRAY) return; json_foreach(node, object_or_array) { if (!clobber & (json_find_member(obj, node->key) != NULL)) continue; /* Don't clobber existing keys */ if (obj->tag == JSON_OBJECT) { if (node->tag == JSON_STRING) json_append_member(obj, node->key, json_mkstring(node->string_)); else if (node->tag == JSON_NUMBER) json_append_member(obj, node->key, json_mknumber(node->number_)); else if (node->tag == JSON_BOOL) json_append_member(obj, node->key, json_mkbool(node->bool_)); else if (node->tag == JSON_NULL) json_append_member(obj, node->key, json_mknull()); else if (node->tag == JSON_OBJECT) { /* Deep-copy existing object to new object */ json_append_member(obj, node->key, (obj_child = json_mkobject())); json_foreach(node_child, node) { json_copy_to_object(obj_child, node_child, clobber); } } else fprintf(stderr, "PANIC: unhandled JSON type %d\n", node->tag); } else if (obj->tag == JSON_ARRAY) { if (node->tag == JSON_STRING) json_append_element(obj, json_mkstring(node->string_)); if (node->tag == JSON_NUMBER) json_append_element(obj, json_mknumber(node->number_)); if (node->tag == JSON_BOOL) json_append_element(obj, json_mkbool(node->bool_)); if (node->tag == JSON_NULL) json_append_element(obj, json_mknull()); } } } int slurp(FILE *fp, char **bufp, off_t bufblk_sz, int eos_char, size_t *out_len, bool fold_newlines) { char *buf; int result = 0; size_t i = 0; int ch = EOF; size_t buffer_len = bufblk_sz; if ((buf = malloc(buffer_len)) == NULL) { result = -1; } else { while ((ch = fgetc(fp)) != eos_char && ch != EOF) { if (i == (buffer_len - 1)) { buffer_len += bufblk_sz; if ((buf = realloc(buf, buffer_len)) == NULL) { result = -1; break; } } if (ch != '\n' || !fold_newlines) { buf[i++] = ch; } } } if (result < 0) { free(buf); buf = NULL; } else { buf[i] = 0; } *out_len = i; *bufp = buf; return result; } char *slurp_file(const char* filename, size_t *out_len, bool fold_newlines) { char *buf; off_t buffer_len; FILE *fp; bool use_stdin = strcmp(filename, "-") == 0; if (use_stdin) fp = stdin; else if ((fp = fopen(filename, "r")) == NULL) { perror(filename); errx(1, "Cannot open %s for reading", filename); } if (fseeko(fp, 0, SEEK_END) != 0) { /* If we cannot seek, we're operating off a pipe and need to dynamically grow the buffer that we're reading into */ buffer_len = SLURP_BLOCK_SIZE; } else { buffer_len = ftello(fp) + 1; fseeko(fp, 0, SEEK_SET); } if (slurp(fp, &buf, buffer_len, EOF, out_len, fold_newlines) < 0) { errx(1, "File %s is too large to be read into memory", filename); } if (!use_stdin) fclose(fp); return buf; } char *slurp_line(FILE *fp, size_t *out_len) { char *buf; if (slurp(fp, &buf, SLURP_BLOCK_SIZE, '\n', out_len, false) < 0) { errx(1, "Line too large to be read into memory"); } return buf; } JsonNode *jo_mknull(JsonTag type) { switch (type) { case JSON_STRING: return json_mkstring(""); break; case JSON_NUMBER: return json_mknumber(0); break; case JSON_BOOL: return json_mkbool(false); break; default: return json_mknull(); break; } } JsonNode *jo_mkbool(bool b, JsonTag type) { switch (type) { case JSON_STRING: return json_mkstring(b ? "true" : "false"); break; case JSON_NUMBER: return json_mknumber(b ? 1 : 0); break; default: return json_mkbool(b); break; } } JsonNode *jo_mkstring(char *str, JsonTag type) { switch (type) { case JSON_NUMBER: /* Length of string */ return json_mknumber(strlen(str)); break; case JSON_BOOL: /* True if not empty */ return json_mkbool(strlen(str) > 0); break; default: return json_mkstring(str); break; } } JsonNode *jo_mknumber(char *str, JsonTag type) { /* ASSUMPTION: str already tested as valid number */ double n = strtod(str, NULL); switch (type) { case JSON_STRING: /* Just return the original representation */ return json_mkstring(str); break; case JSON_BOOL: return json_mkbool(n != 0); break; default: /* ASSUMPTION: str already tested as valid number */ return json_mknumber(n); break; } } /* * Attempt to "sniff" the type of data in `str' and return * a JsonNode of the correct JSON type. */ JsonNode *vnode(char *str, int flags) { JsonTag type = flags_to_tag(flags); if (strlen(str) == 0) { return (flags & FLAG_SKIPNULLS) ? (JsonNode *)NULL : jo_mknull(type); } /* If str begins with a double quote, keep it a string */ if (*str == '"') { #if 0 char *bp = str + strlen(str) - 1; if (bp > str && *bp == '"') *bp = 0; /* Chop closing double quote */ return json_mkstring(str + 1); #endif return jo_mkstring(str, type); } char *endptr; double num = strtod(str, &endptr); if (!*endptr && isfinite(num)) { return jo_mknumber(str, type); } if (!(flags & FLAG_NOBOOL)) { if (strcmp(str, "true") == 0) { return jo_mkbool(true, type); } else if (strcmp(str, "false") == 0) { return jo_mkbool(false, type); } else if (strcmp(str, "null") == 0) { return jo_mknull(type); } } if (*str == '\\') { ++str; } else { if (*str == '@' || *str == '%' || *str == ':') { char *filename = str + 1, *content; bool binmode = (*str == '%'); bool jsonmode = (*str == ':'); size_t len = 0; JsonNode *j = NULL; if ((content = slurp_file(filename, &len, false)) == NULL) { errx(1, "Error reading file %s", filename); } if (binmode) { char *encoded; if ((encoded = base64_encode(content, len)) == NULL) { errx(1, "Cannot base64-encode file %s", filename); } j = json_mkstring(encoded); free(encoded); } else if (jsonmode) { j = json_decode(content); if (j == NULL) { errx(1, "Cannot decode JSON in file %s", filename); } } // If it got this far without valid JSON, just consider it a string if (j == NULL) { char *bp = content + strlen(content) - 1; if (*bp == '\n') *bp-- = 0; if (*bp == '\r') *bp = 0; j = json_mkstring(content); } free(content); return (j); } } if (*str == '{' || *str == '[') { if (type == JSON_STRING) { return json_mkstring(str); } JsonNode *obj = json_decode(str); if (obj == NULL) { /* JSON cannot be decoded; return the string */ // fprintf(stderr, "Cannot decode JSON from %s\n", str); obj = json_mkstring(str); } return (obj); } return jo_mkstring(str, type); } /* * Attempt to sniff `str' into a boolean; return a * corresponding JsonNode for it. */ JsonNode *boolnode(char *str) { if (strlen(str) == 0) { return json_mknull(); } if (tolower((unsigned char) *str) == 't') { return json_mkbool(1); } return json_mkbool(atoi(str)); } int usage(char *prog) { fprintf(stderr, "Usage: %s [-a] [-B] [-D] [-d keydelim] [-p] [-e] [-n] [-o outfile] [-v] [-V] [-f file] [--] [-s|-n|-b] [word...]\n", prog); fprintf(stderr, "\tword is key=value or key@value\n"); fprintf(stderr, "\t-a creates an array of words\n"); fprintf(stderr, "\t-B disable boolean true/false/null detection\n"); fprintf(stderr, "\t-D deduplicate object keys\n"); fprintf(stderr, "\t-d key will be object path separated by keydelim\n"); fprintf(stderr, "\t-f load file as JSON object or array\n"); fprintf(stderr, "\t-p pretty-prints JSON on output\n"); fprintf(stderr, "\t-e quit if stdin is empty do not wait for input\n"); fprintf(stderr, "\t-s coerce type guessing to string\n"); fprintf(stderr, "\t-b coerce type guessing to bool\n"); fprintf(stderr, "\t-n coerce type guessing to number\n"); fprintf(stderr, "\t-o output to the given file\n"); fprintf(stderr, "\t-v show version\n"); fprintf(stderr, "\t-V show version in JSON\n"); return (-1); } /* * Check whether we're being given nested arrays or objects. * `kv' contains the "key" such as "number" or "point[]" or * "geo[lat]". `value' the actual value for that element. * * Returns true if nesting is completely handled, otherwise: * *keyp -> remaining key for caller to insert "value" * *baseop -> object node in which caller should insert "value" */ bool resolve_nested(int flags, char **keyp, char key_delim, JsonNode *value, JsonNode **baseop) { char *member = NULL, *bo, *bc, *so; /* bracket open, close, sub-object */ JsonNode *op; int found = false; (void)flags; if (key_delim) { /* First construct nested object */ while ((so = strchr(*keyp, key_delim)) != NULL) { *so = 0; if ((op = json_find_member(*baseop, *keyp)) == NULL) { /* Add a nested object node */ op = json_mkobject(); json_append_member(*baseop, *keyp, op); } *baseop = op; *keyp = so + 1; } } /* Now check for trailing geo[] or geo[lat] */ if ((bo = strchr(*keyp, '[')) != NULL) { if (*(bo+1) == ']') { *bo = 0; } else if ((bc = strchr(bo + 1, ']')) == NULL) { fprintf(stderr, "missing closing bracket on %s\n", *keyp); return (false); } else { *bo = *bc = 0; member = bo + 1; } /* * *keyp is now `geo' for both `geo[]` and `geo[lat]` * member is null for the former and "lat" for the latter. * Find an existing object in *baseop for this member name * or create a new one if we don't have it. */ if ((op = json_find_member(*baseop, *keyp)) != NULL) { found = true; } else { op = (member == NULL) ? json_mkarray() : json_mkobject(); } if (member == NULL) { /* we're doing an array */ json_append_element(op, value); } else { /* we're doing an object */ json_append_member(op, member, value); } if (!found) { json_append_member(*baseop, *keyp, op); } return (true); } return (false); } int member_to_object(JsonNode *object, int flags, char key_delim, char *kv) { /* we expect key=value or key:value (boolean on last) */ char *p = strchr(kv, '='); char *q = strchr(kv, '@'); char *r = strchr(kv, ':'); if ((r && *(r+1) == '=') && !q) { char *filename = p + 1; char *content; size_t len; if ((content = slurp_file(filename, &len, false)) == NULL) { errx(1, "Error reading file %s", filename); } JsonNode *o = json_decode(content); free(content); if (o == NULL) { errx(1, "Cannot decode JSON in file %s", filename); } *r = 0; /* Chop at ":=" */ if (!resolve_nested(flags, &kv, key_delim, o, &object)) json_append_member(object, kv, o); return (0); } if (!p && !q && !r) { return (-1); } JsonNode *val; if (p) { *p = 0; val = vnode(p+1, flags); if (!resolve_nested(flags, &kv, key_delim, val, &object)) json_append_member(object, kv, val); } else { if (q) { *q = 0; val = boolnode(q+1); if (!resolve_nested(flags | FLAG_BOOLEAN, &kv, key_delim, val, &object)) json_append_member(object, kv, val); } } return (0); } /* * Append kv to the array or object. */ void append_kv(JsonNode *object_or_array, int flags, char key_delim, char *kv) { if (flags & FLAG_ARRAY) { json_append_element(object_or_array, vnode(kv, flags)); } else { if (member_to_object(object_or_array, flags, key_delim, kv) == -1) { fprintf(stderr, "Argument `%s' is neither k=v nor k@v\n", kv); } } } #ifdef _WIN32 #include char* utf8_from_locale(const char *str, size_t len) { wchar_t* wcsp; char* mbsp; size_t mbssize, wcssize; if (len == 0) { return strdup(""); } if (len == (size_t)-1) { len = strlen(str); } wcssize = MultiByteToWideChar(GetACP(), 0, str, len, NULL, 0); wcsp = (wchar_t*) malloc((wcssize + 1) * sizeof(wchar_t)); if (!wcsp) { return NULL; } wcssize = MultiByteToWideChar(GetACP(), 0, str, len, wcsp, wcssize + 1); wcsp[wcssize] = 0; mbssize = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR) wcsp, -1, NULL, 0, NULL, NULL); mbsp = (char*) malloc((mbssize + 1)); if (!mbsp) { free(wcsp); return NULL; } mbssize = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR) wcsp, -1, mbsp, mbssize, NULL, NULL); mbsp[mbssize] = 0; free(wcsp); return mbsp; } # define utf8_free(p) free(p) char* locale_from_utf8(const char *utf8, size_t len) { wchar_t* wcsp; char* mbsp; size_t mbssize, wcssize; if (len == 0) { return strdup(""); } if (len == (size_t)-1) { len = strlen(utf8); } wcssize = MultiByteToWideChar(CP_UTF8, 0, utf8, len, NULL, 0); wcsp = (wchar_t*) malloc((wcssize + 1) * sizeof(wchar_t)); if (!wcsp) { return NULL; } wcssize = MultiByteToWideChar(CP_UTF8, 0, utf8, len, wcsp, wcssize + 1); wcsp[wcssize] = 0; mbssize = WideCharToMultiByte(GetACP(), 0, (LPCWSTR) wcsp, -1, NULL, 0, NULL, NULL); mbsp = (char*) malloc((mbssize + 1)); if (!mbsp) { free(wcsp); return NULL; } mbssize = WideCharToMultiByte(GetACP(), 0, (LPCWSTR) wcsp, -1, mbsp, mbssize, NULL, NULL); mbsp[mbssize] = 0; free(wcsp); return mbsp; } # define locale_free(p) free(p) #else # define utf8_from_locale(p, l) (p) # define utf8_free(p) do {} while (0) # define locale_from_utf8(p, l) (p) # define locale_free(p) do {} while (0) #endif char *stringify(JsonNode *json, int flags) { int pretty = flags & FLAG_PRETTY; return json_stringify(json, (pretty) ? SPACER : NULL); } int version(int flags) { JsonNode *json = json_mkobject(); char *js; json_append_member(json, "program", json_mkstring("jo")); json_append_member(json, "author", json_mkstring("Jan-Piet Mens")); json_append_member(json, "repo", json_mkstring("https://github.com/jpmens/jo")); json_append_member(json, "version", json_mkstring(PACKAGE_VERSION)); if ((js = stringify(json, flags)) != NULL) { printf("%s\n", js); free(js); } json_delete(json); return (0); } int main(int argc, char **argv) { int c, key_delim = 0; bool showversion = false; char *kv, *js_string, *progname, *buf, *p; char *in_file = NULL, *in_str; char *out_file = NULL; FILE *out = stdout; size_t in_len = 0; int ttyin = isatty(fileno(stdin)); int ttyout = isatty(fileno(stdout)); int flags = 0; JsonNode *json, *op; #if HAVE_PLEDGE if (pledge("stdio rpath", NULL) == -1) { err(1, "pledge"); } #endif progname = (progname = strrchr(*argv, '/')) ? progname + 1 : *argv; while ((c = getopt(argc, argv, "aBDd:f:hpeno:vV")) != EOF) { switch (c) { case 'a': flags |= FLAG_ARRAY; break; case 'B': flags |= FLAG_NOBOOL; break; case 'D': json_dedup_members(true); break; case 'd': key_delim = optarg[0]; break; case 'f': in_file = optarg; break; case 'h': usage(progname); return (0); case 'p': flags |= FLAG_PRETTY; break; case 'e': flags |= FLAG_NOSTDIN; break; case 'n': flags |= FLAG_SKIPNULLS; break; case 'o': out_file = optarg; break; case 'v': printf("jo %s\n", PACKAGE_VERSION); exit(0); case 'V': showversion = true; break; default: exit(usage(progname)); } } if (showversion) { return(version(flags)); } argc -= optind; argv += optind; pile = json_mkobject(); if (in_file != NULL) { if ((in_str = slurp_file(in_file, &in_len, false)) == NULL) { errx(1, "Error reading file %s", in_file); } json = json_decode(in_str); if (json) { switch (json->tag) { case JSON_ARRAY: flags |= FLAG_ARRAY; break; case JSON_OBJECT: break; default: errx(1, "Input JSON not an array or object: %s", stringify(json, flags)); } } else json = (flags & FLAG_ARRAY) ? json_mkarray() : json_mkobject(); } else { json = (flags & FLAG_ARRAY) ? json_mkarray() : json_mkobject(); } if (argc == 0) { if (flags & FLAG_NOSTDIN) { return(0); } while ((buf = slurp_line(stdin, &in_len)) != NULL && in_len > 0) { p = ttyin ? utf8_from_locale(buf, -1) : buf; append_kv(json, flags, key_delim, p); if (ttyin) utf8_free(p); if (buf) free(buf); } } else { while ((kv = *argv++)) { if (kv[0] == '-' && !(flags & COERCE_MASK)) { /* Set one-shot coerce flag */ switch (kv[1]) { case 'b': flags |= TAG_FLAG_BOOL; break; case 's': flags |= TAG_FLAG_STRING; break; case 'n': flags |= TAG_FLAG_NUMBER; break; default: /* Treat as normal input */ p = utf8_from_locale(kv, -1); append_kv(json, flags, key_delim, p); utf8_free(p); /* Reset any one-shot coerce flags */ flags &= ~(COERCE_MASK); } } else { p = utf8_from_locale(kv, -1); append_kv(json, flags, key_delim, p); utf8_free(p); /* Reset any one-shot coerce flags */ flags &= ~(COERCE_MASK); } } } /* * See if we have any nested objects or arrays in the pile, * and copy these into our main object if so. */ json_foreach(op, pile) { JsonNode *o; if (op->tag == JSON_ARRAY) { o = json_mkarray(); } else if (op->tag == JSON_OBJECT) { o = json_mkobject(); } else { continue; } json_copy_to_object(o, op, 0); json_append_member(json, op->key, o); } if ((js_string = stringify(json, flags)) == NULL) { fprintf(stderr, "Invalid JSON\n"); exit(2); } if (out_file != NULL) { out = fopen(out_file, "w"); if (out == NULL) { perror(out_file); errx(1, "Cannot open %s for writing", out_file); } ttyout = isatty(fileno(out)); } p = ttyout ? locale_from_utf8(js_string, -1) : js_string; fprintf(out, "%s\n", p); if (ttyout) locale_free(p); free(js_string); json_delete(json); json_delete(pile); return (0); } jo-1.9/jo.md000066400000000000000000000232051433077231700127360ustar00rootroot00000000000000# NAME jo - JSON output from a shell # SYNOPSIS jo \[-p\] \[-a\] \[-B\] \[-D\] \[-e\] \[-n\] \[-v\] \[-V\] \[-d keydelim\] \[-f file\] \[--\] \[ \[-s|-n|-b\] word ...\] # DESCRIPTION *jo* creates a JSON string on *stdout* from *word*s given it as arguments or read from *stdin*. If `-f` is specified, *jo* first loads the contents of *file* as a JSON object or array, then modifies it with subsequent *word*s before printing the final JSON string to *stdout*. *file* may be specified as `-` to read from *jo*'s standard input; this takes precedence over reading *word*s from *stdin*. Without option `-a` it generates an object whereby each *word* is a `key=value` (or `key@value`) pair with *key* being the JSON object element and *value* its value. *jo* attempts to guess the type of *value* in order to create number (using *strtod(3)*), string, or null values in JSON. A missing or empty *value* normally results in an element whose value is `null`. If `-n` is specified, this element is not created. *jo* normally treats *key* as a literal string value. If the `-d` option is specified, *key* will be interpreted as an *object path*, whose individual components are separated by the first character of *keydelim*. *jo* normally treats *value* as a literal string value, unless it begins with one of the following characters: | value | action | | ----- | ------------------------------------------------------------------- | | @file | substitute the contents of *file* as-is | | %file | substitute the contents of *file* in base64-encoded form | | :file | interpret the contents of *file* as JSON, and substitute the result | Escape the special character with a backslash to prevent this interpretation. *jo* treats `key@value` specifically as boolean JSON elements: if the value begins with `T`, `t`, or the numeric value is greater than zero, the result is `true`, else `false`. *jo* creates an array instead of an object when `-a` is specified. When the `:=` operator is used in a *word*, the name to the right of `:=` is a file containing JSON which is parsed and assigned to the key left of the operator. The file may be specified as `-` to read from *jo*'s standard input. # TYPE COERCION *jo*'s type guesses can be overridden on a per-word basis by prefixing *word* with `-s` for *string*, `-n` for *number*, or `-b` for *boolean*. The list of *word*s *must* be prefixed with `--`, to indicate to *jo* that there are no more global options. Type coercion works as follows: | word | \-s | \-n | \-b | default | | :--------- | :------------- | :-------- | :-------- | :------------- | | a= | "a":"" | "a":0 | "a":false | "a":null | | a=string | "a":"string" | "a":6 | "a":true | "a":"string" | | a="quoted" | "a":""quoted"" | "a":8 | "a":true | "a":""quoted"" | | a=12345 | "a":"12345" | "a":12345 | "a":true | "a":12345 | | a=true | "a":"true" | "a":1 | "a":true | "a":true | | a=false | "a":"false" | "a":0 | "a":false | "a":false | | a=null | "a":"" | "a":0 | "a":false | "a":null | Coercing a non-number string to number outputs the *length* of the string. Coercing a non-boolean string to boolean outputs `false` if the string is empty, `true` otherwise. Type coercion only applies to `key=value` words, and individual words in a `-a` array. Coercing other words has no effect. # EXAMPLES Create an object. Note how the incorrectly-formatted float value becomes a string: $ jo tst=1457081292 lat=12.3456 cc=FR badfloat=3.14159.26 name="JP Mens" nada= coffee@T {"tst":1457081292,"lat":12.3456,"cc":"FR","badfloat":"3.14159.26","name":"JP Mens","nada":null,"coffee":true} Pretty-print an array with a list of files in the current directory: $ jo -p -a * [ "Makefile", "README.md", "jo.1", "jo.c", "jo.pandoc", "json.c", "json.h" ] Create objects within objects; this works because if the first character of value is an open brace or a bracket we attempt to decode the remainder as JSON. Beware spaces in strings ... $ jo -p name=JP object=$(jo fruit=Orange hungry@0 point=$(jo x=10 y=20 list=$(jo -a 1 2 3 4 5)) number=17) sunday@0 { "name": "JP", "object": { "fruit": "Orange", "hungry": false, "point": { "x": 10, "y": 20, "list": [ 1, 2, 3, 4, 5 ] }, "number": 17 }, "sunday": false } Booleans as strings or as boolean (pay particular attention to *switch*; the `-B` option disables the default detection of the "`true`", "`false`", and "`null`" strings): $ jo switch=true morning@0 {"switch":true,"morning":false} $ jo -B switch=true morning@0 {"switch":"true","morning":false} Elements (objects and arrays) can be nested. The following example nests an array called *point* and an object named *geo*: $ jo -p name=Jane point[]=1 point[]=2 geo[lat]=10 geo[lon]=20 { "name": "Jane", "point": [ 1, 2 ], "geo": { "lat": 10, "lon": 20 } } The same example, using object paths: $ jo -p -d. name=Jane point[]=1 point[]=2 geo.lat=10 geo.lon=20 { "name": "Jane", "point": [ 1, 2 ], "geo": { "lat": 10, "lon": 20 } } Without `-d`, a different object is generated: $ jo -p name=Jane point[]=1 point[]=2 geo.lat=10 geo.lon=20 { "name": "Jane", "point": [ 1, 2 ], "geo.lat": 10, "geo.lon": 20 } Create empty objects or arrays, intentionally or potentially: $ jo < /dev/null {} $ MY_ARRAY=(a=1 b=2) $ jo -a "${MY_ARRAY[@]}" < /dev/null ["a=1","b=2"] Type coercion: $ jo -p -- -s a=true b=true -s c=123 d=123 -b e="1" -b f="true" -n g="This is a test" -b h="This is a test" { "a": "true", "b": true, "c": "123", "d": 123, "e": true, "f": true, "g": 14, "h": true } $ jo -a -- -s 123 -n "This is a test" -b C_Rocks 456 ["123",14,true,456] Read element values from files: a value which starts with `@` is read in plain whereas if it begins with a `%` it will be base64-encoded and if it starts with `:` the contents are interpreted as JSON: $ jo program=jo authors=@AUTHORS {"program":"jo","authors":"Jan-Piet Mens "} $ jo filename=AUTHORS content=%AUTHORS {"filename":"AUTHORS","content":"SmFuLVBpZXQgTWVucyA8anBtZW5zQGdtYWlsLmNvbT4K"} $ jo nested=:nested.json {"nested":{"field1":123,"field2":"abc"}} These characters can be escaped to avoid interpretation: $ jo name="JP Mens" twitter='\@jpmens' {"name":"JP Mens","twitter":"@jpmens"} $ jo char=" " URIescape=\\%20 {"char":" ","URIescape":"%20"} $ jo action="split window" vimcmd="\:split" {"action":"split window","vimcmd":":split"} Read element values from a file in order to overcome ARG\_MAX limits during object assignment: $ ls | jo -a > child.json $ jo files:=child.json {"files":["AUTHORS","COPYING","ChangeLog" .... $ ls *.c | jo -a > source.json; ls *.h | jo -a > headers.json $ jo -a :source.json :headers.json [["base64.c","jo.c","json.c"],["base64.h","json.h"]] Add elements to existing JSON: $ jo -f source.json 1 | jo -f - 2 3 ["base64.c","jo.c","json.c",1,2,3] $ curl -s 'https://noembed.com/embed?url=https://www.youtube.com/watch?v=dQw4w9WgXcQ' | jo -f - status=Rickrolled { ...., "type":"video","author_url":"https://www.youtube.com/user/RickAstleyVEVO","status":"Rickrolled"} Deduplicate object keys (*jo* appends duplicate object keys by default): $ jo a=1 b=2 a=3 {"a":1,"b":2,"a":3} $ jo -D a=1 b=2 a=3 {"a":3,"b":2} # OPTIONS *jo* understands the following global options. - \-a Interpret the list of *words* as array values and produce an array instead of an object. - \-B By default, *jo* interprets the strings "`true`" and "`false`" as boolean elements `true` and `false` respectively, and "`null`" as `null`. Disable with this option. - \-D Deduplicate object keys. - \-e Ignore empty stdin (i.e. don't produce a diagnostic error when *stdin* is empty) - \-n Do not add keys with empty values. - \-p Pretty-print the JSON string on output instead of the terse one-line output it prints by default. - \-v Show version and exit. - \-V Show version as a JSON object and exit. # BUGS Probably. If a value given to *jo* expands to empty in the shell, then *jo* produces a `null` in object mode, and might appear to hang in array mode; it is not hanging, rather it's reading *stdin*. This is not a bug. Numeric values are converted to numbers which can produce undesired results. If you quote a numeric value, *jo* will make it a string. Compare the following: $ jo a=1.0 {"a":1} $ jo a=\"1.0\" {"a":"1.0"} Omitting a closing bracket on a nested element causes a diagnostic message to print, but the output contains garbage anyway. This was designed thusly. # RETURN CODES *jo* exits with a code 0 on success and non-zero on failure after indicating what caused the failure. # AVAILABILITY # CREDITS - This program uses `json.[ch]`, by Joseph A. Adams. # SEE ALSO - - - - strtod(3) # AUTHOR Jan-Piet Mens jo-1.9/jo.pandoc000066400000000000000000000216561433077231700136120ustar00rootroot00000000000000% JO(1) User Manuals # NAME jo - JSON output from a shell # SYNOPSIS jo [-p] [-a] [-B] [-D] [-e] [-n] [-v] [-V] [-d keydelim] [-f file] [--] [ [-s|-n|-b] word ...] # DESCRIPTION *jo* creates a JSON string on _stdout_ from *word*s given it as arguments or read from _stdin_. If `-f` is specified, *jo* first loads the contents of _file_ as a JSON object or array, then modifies it with subsequent *word*s before printing the final JSON string to _stdout_. _file_ may be specified as `-` to read from _jo_'s standard input; this takes precedence over reading *word*s from _stdin_. Without option `-a` it generates an object whereby each _word_ is a `key=value` (or `key@value`) pair with _key_ being the JSON object element and _value_ its value. *jo* attempts to guess the type of _value_ in order to create number (using _strtod(3)_), string, or null values in JSON. A missing or empty _value_ normally results in an element whose value is `null`. If `-n` is specified, this element is not created. *jo* normally treats _key_ as a literal string value. If the `-d` option is specified, _key_ will be interpreted as an _object path_, whose individual components are separated by the first character of _keydelim_. *jo* normally treats _value_ as a literal string value, unless it begins with one of the following characters: value action ----- ------ @file substitute the contents of _file_ as-is %file substitute the contents of _file_ in base64-encoded form :file interpret the contents of _file_ as JSON, and substitute the result Escape the special character with a backslash to prevent this interpretation. *jo* treats `key@value` specifically as boolean JSON elements: if the value begins with `T`, `t`, or the numeric value is greater than zero, the result is `true`, else `false`. *jo* creates an array instead of an object when `-a` is specified. When the `:=` operator is used in a _word_, the name to the right of `:=` is a file containing JSON which is parsed and assigned to the key left of the operator. The file may be specified as `-` to read from _jo_'s standard input. # TYPE COERCION *jo*'s type guesses can be overridden on a per-word basis by prefixing _word_ with `-s` for _string_, `-n` for _number_, or `-b` for _boolean_. The list of *word*s *must* be prefixed with `--`, to indicate to *jo* that there are no more global options. Type coercion works as follows: word -s -n -b default ------------ ---------------- ------------ --------- ---------------- a= "a":"" "a":0 "a":false "a":null a=string "a":"string" "a":6 "a":true "a":"string" a=\"quoted\" "a":"\"quoted\"" "a":8 "a":true "a":"\"quoted\"" a=12345 "a":"12345" "a":12345 "a":true "a":12345 a=true "a":"true" "a":1 "a":true "a":true a=false "a":"false" "a":0 "a":false "a":false a=null "a":"" "a":0 "a":false "a":null Coercing a non-number string to number outputs the _length_ of the string. Coercing a non-boolean string to boolean outputs `false` if the string is empty, `true` otherwise. Type coercion only applies to `key=value` words, and individual words in a `-a` array. Coercing other words has no effect. # EXAMPLES Create an object. Note how the incorrectly-formatted float value becomes a string: $ jo tst=1457081292 lat=12.3456 cc=FR badfloat=3.14159.26 name="JP Mens" nada= coffee@T {"tst":1457081292,"lat":12.3456,"cc":"FR","badfloat":"3.14159.26","name":"JP Mens","nada":null,"coffee":true} Pretty-print an array with a list of files in the current directory: $ jo -p -a * [ "Makefile", "README.md", "jo.1", "jo.c", "jo.pandoc", "json.c", "json.h" ] Create objects within objects; this works because if the first character of value is an open brace or a bracket we attempt to decode the remainder as JSON. Beware spaces in strings ... $ jo -p name=JP object=$(jo fruit=Orange hungry@0 point=$(jo x=10 y=20 list=$(jo -a 1 2 3 4 5)) number=17) sunday@0 { "name": "JP", "object": { "fruit": "Orange", "hungry": false, "point": { "x": 10, "y": 20, "list": [ 1, 2, 3, 4, 5 ] }, "number": 17 }, "sunday": false } Booleans as strings or as boolean (pay particular attention to _switch_; the `-B` option disables the default detection of the "`true`", "`false`", and "`null`" strings): $ jo switch=true morning@0 {"switch":true,"morning":false} $ jo -B switch=true morning@0 {"switch":"true","morning":false} Elements (objects and arrays) can be nested. The following example nests an array called _point_ and an object named _geo_: $ jo -p name=Jane point[]=1 point[]=2 geo[lat]=10 geo[lon]=20 { "name": "Jane", "point": [ 1, 2 ], "geo": { "lat": 10, "lon": 20 } } The same example, using object paths: $ jo -p -d. name=Jane point[]=1 point[]=2 geo.lat=10 geo.lon=20 { "name": "Jane", "point": [ 1, 2 ], "geo": { "lat": 10, "lon": 20 } } Without `-d`, a different object is generated: $ jo -p name=Jane point[]=1 point[]=2 geo.lat=10 geo.lon=20 { "name": "Jane", "point": [ 1, 2 ], "geo.lat": 10, "geo.lon": 20 } Create empty objects or arrays, intentionally or potentially: $ jo < /dev/null {} $ MY_ARRAY=(a=1 b=2) $ jo -a "${MY_ARRAY[@]}" < /dev/null ["a=1","b=2"] Type coercion: $ jo -p -- -s a=true b=true -s c=123 d=123 -b e="1" -b f="true" -n g="This is a test" -b h="This is a test" { "a": "true", "b": true, "c": "123", "d": 123, "e": true, "f": true, "g": 14, "h": true } $ jo -a -- -s 123 -n "This is a test" -b C_Rocks 456 ["123",14,true,456] Read element values from files: a value which starts with `@` is read in plain whereas if it begins with a `%` it will be base64-encoded and if it starts with `:` the contents are interpreted as JSON: $ jo program=jo authors=@AUTHORS {"program":"jo","authors":"Jan-Piet Mens "} $ jo filename=AUTHORS content=%AUTHORS {"filename":"AUTHORS","content":"SmFuLVBpZXQgTWVucyA8anBtZW5zQGdtYWlsLmNvbT4K"} $ jo nested=:nested.json {"nested":{"field1":123,"field2":"abc"}} These characters can be escaped to avoid interpretation: $ jo name="JP Mens" twitter='\@jpmens' {"name":"JP Mens","twitter":"@jpmens"} $ jo char=" " URIescape=\\%20 {"char":" ","URIescape":"%20"} $ jo action="split window" vimcmd="\:split" {"action":"split window","vimcmd":":split"} Read element values from a file in order to overcome ARG_MAX limits during object assignment: $ ls | jo -a > child.json $ jo files:=child.json {"files":["AUTHORS","COPYING","ChangeLog" .... $ ls *.c | jo -a > source.json; ls *.h | jo -a > headers.json $ jo -a :source.json :headers.json [["base64.c","jo.c","json.c"],["base64.h","json.h"]] Add elements to existing JSON: $ jo -f source.json 1 | jo -f - 2 3 ["base64.c","jo.c","json.c",1,2,3] $ curl -s 'https://noembed.com/embed?url=https://www.youtube.com/watch?v=dQw4w9WgXcQ' | jo -f - status=Rickrolled { ...., "type":"video","author_url":"https://www.youtube.com/user/RickAstleyVEVO","status":"Rickrolled"} Deduplicate object keys (*jo* appends duplicate object keys by default): $ jo a=1 b=2 a=3 {"a":1,"b":2,"a":3} $ jo -D a=1 b=2 a=3 {"a":3,"b":2} # OPTIONS *jo* understands the following global options. -a : Interpret the list of _words_ as array values and produce an array instead of an object. -B : By default, *jo* interprets the strings "`true`" and "`false`" as boolean elements `true` and `false` respectively, and "`null`" as `null`. Disable with this option. -D : Deduplicate object keys. -e : Ignore empty stdin (i.e. don't produce a diagnostic error when *stdin* is empty) -n : Do not add keys with empty values. -p : Pretty-print the JSON string on output instead of the terse one-line output it prints by default. -v : Show version and exit. -V : Show version as a JSON object and exit. # BUGS Probably. If a value given to *jo* expands to empty in the shell, then *jo* produces a `null` in object mode, and might appear to hang in array mode; it is not hanging, rather it's reading _stdin_. This is not a bug. Numeric values are converted to numbers which can produce undesired results. If you quote a numeric value, *jo* will make it a string. Compare the following: $ jo a=1.0 {"a":1} $ jo a=\"1.0\" {"a":"1.0"} Omitting a closing bracket on a nested element causes a diagnostic message to print, but the output contains garbage anyway. This was designed thusly. # RETURN CODES *jo* exits with a code 0 on success and non-zero on failure after indicating what caused the failure. # AVAILABILITY # CREDITS * This program uses `json.[ch]`, by Joseph A. Adams. # SEE ALSO * * * * strtod(3) # AUTHOR Jan-Piet Mens jo-1.9/jo.zsh000066400000000000000000000036011433077231700131400ustar00rootroot00000000000000#compdef jo # Completion function for zsh # Store this file in a directory listed in $fpath for it to be picked up # by compinit. It needs to be named with an initial underscore, e.g. _jo local curcontext="$curcontext" local -i aopt nm=$compstate[nmatches] local -a expl line state state_descr local -A opt_args _arguments -C -s -A "-*" \ '(-h)-p[pretty-print JSON on output]' \ '(-d -h)-a[create an array of words]' \ '(-v -V -h)-B[disable interpretation of true/false/null strings]' \ "(-v -V -h)-e[if stdin is empty don't wait for input - quit]" \ '(- *)-v[show version information]' \ '(-a -B -e -h -v *)-V[show version in JSON]' \ '(-a -h -v -V)-d+[key will be object path separated by given delimiter]:key delimiter' \ '(- *)-h[show usage information]' \ '*::word:->words' if [[ -n $state ]]; then aopt=$+opt_args[-a] _arguments \ '*-s[coerce type guessing to string]: :->words' \ '*-b[coerce type guessing to bool]: :->words' \ '*-n[coerce type guessing to number]: :->words' \ '*: :->words' if (( aopt )); then _message -e words 'array element' elif compset -P 1 '*:='; then _alternative 'files:file:_files' 'operators:stdin:(-)' elif compset -P 1 '*='; then if compset -P '[@%:]'; then _files else _describe -t operators "file prefix" '( @:substitute\ file\ as-is %:substitute\ file\ in\ base64-encoded\ form \\::substitute\ file\ as\ JSON )' -S '' _message -e values value fi elif compset -P 1 '?*@'; then _description booleans expl 'boolean' compadd -M 'm:{a-zA-Z}={A-Za-z} m:{10}={TF}' "$expl[@]" True False else if compset -P '[^-]*'; then _describe -t suffixes suffix '( @:boolean\ element \=:value \\:=:substitute\ JSON\ file \[\]:array\ element )' -S '' fi _message -e keys key fi fi [[ nm -ne compstate[nmatches] ]] jo-1.9/json.c000066400000000000000000000751351433077231700131320ustar00rootroot00000000000000/* Copyright (C) 2011 Joseph A. Adams (joeyadams3.14159@gmail.com) All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "json.h" #include #include #include #include #include #define out_of_memory() do { \ fprintf(stderr, "Out of memory.\n"); \ exit(EXIT_FAILURE); \ } while (0) #if defined(_WIN32) || defined(_AIX) # define failx(e, n, f, ...) if (!(e)) { \ fprintf(stderr, "jo: JSON_ERR: " f "\n", __VA_ARGS__); \ exit(n); \ } #else # include # define failx(e, n, f, ...) if (!(e)) { \ errx(n, "JSON_ERR: " f, __VA_ARGS__); \ } #endif /* Sadly, strdup is not portable. */ static char *json_strdup(const char *str) { size_t n = strlen(str) + 1; char *ret = (char*) malloc(n); if (ret == NULL) out_of_memory(); #if HAVE_STRLCPY strlcpy(ret, str, n); #else strcpy(ret, str); #endif return ret; } /* String buffer */ typedef struct { char *cur; char *end; char *start; } SB; static void sb_init(SB *sb) { sb->start = (char*) malloc(17); if (sb->start == NULL) out_of_memory(); sb->cur = sb->start; sb->end = sb->start + 16; } /* sb and need may be evaluated multiple times. */ #define sb_need(sb, need) do { \ if ((sb)->end - (sb)->cur < (need)) \ sb_grow(sb, need); \ } while (0) static void sb_grow(SB *sb, int need) { size_t length = sb->cur - sb->start; size_t alloc = sb->end - sb->start; do { alloc *= 2; } while (alloc < length + need); sb->start = (char*) realloc(sb->start, alloc + 1); if (sb->start == NULL) out_of_memory(); sb->cur = sb->start + length; sb->end = sb->start + alloc; } static void sb_put(SB *sb, const char *bytes, int count) { sb_need(sb, count); memcpy(sb->cur, bytes, count); sb->cur += count; } #define sb_putc(sb, c) do { \ if ((sb)->cur >= (sb)->end) \ sb_grow(sb, 1); \ *(sb)->cur++ = (c); \ } while (0) static void sb_puts(SB *sb, const char *str) { sb_put(sb, str, strlen(str)); } static char *sb_finish(SB *sb) { *sb->cur = 0; assert(sb->start <= sb->cur && strlen(sb->start) == (size_t)(sb->cur - sb->start)); return sb->start; } static void sb_free(SB *sb) { free(sb->start); } /* * Unicode helper functions * * These are taken from the ccan/charset module and customized a bit. * Putting them here means the compiler can (choose to) inline them, * and it keeps ccan/json from having a dependency. */ /* * Type for Unicode codepoints. * We need our own because wchar_t might be 16 bits. */ typedef uint32_t js_uchar_t; /* * Validate a single UTF-8 character starting at @s. * The string must be null-terminated. * * If it's valid, return its length (1 thru 4). * If it's invalid or clipped, return 0. * * This function implements the syntax given in RFC3629, which is * the same as that given in The Unicode Standard, Version 6.0. * * It has the following properties: * * * All codepoints U+0000..U+10FFFF may be encoded, * except for U+D800..U+DFFF, which are reserved * for UTF-16 surrogate pair encoding. * * UTF-8 byte sequences longer than 4 bytes are not permitted, * as they exceed the range of Unicode. * * The sixty-six Unicode "non-characters" are permitted * (namely, U+FDD0..U+FDEF, U+xxFFFE, and U+xxFFFF). */ static int utf8_validate_cz(const char *s) { unsigned char c = *s++; if (c <= 0x7F) { /* 00..7F */ return 1; } else if (c <= 0xC1) { /* 80..C1 */ /* Disallow overlong 2-byte sequence. */ return 0; } else if (c <= 0xDF) { /* C2..DF */ /* Make sure subsequent byte is in the range 0x80..0xBF. */ if (((unsigned char)*s++ & 0xC0) != 0x80) return 0; return 2; } else if (c <= 0xEF) { /* E0..EF */ /* Disallow overlong 3-byte sequence. */ if (c == 0xE0 && (unsigned char)*s < 0xA0) return 0; /* Disallow U+D800..U+DFFF. */ if (c == 0xED && (unsigned char)*s > 0x9F) return 0; /* Make sure subsequent bytes are in the range 0x80..0xBF. */ if (((unsigned char)*s++ & 0xC0) != 0x80) return 0; if (((unsigned char)*s++ & 0xC0) != 0x80) return 0; return 3; } else if (c <= 0xF4) { /* F0..F4 */ /* Disallow overlong 4-byte sequence. */ if (c == 0xF0 && (unsigned char)*s < 0x90) return 0; /* Disallow codepoints beyond U+10FFFF. */ if (c == 0xF4 && (unsigned char)*s > 0x8F) return 0; /* Make sure subsequent bytes are in the range 0x80..0xBF. */ if (((unsigned char)*s++ & 0xC0) != 0x80) return 0; if (((unsigned char)*s++ & 0xC0) != 0x80) return 0; if (((unsigned char)*s++ & 0xC0) != 0x80) return 0; return 4; } else { /* F5..FF */ return 0; } } /* Validate a null-terminated UTF-8 string. */ static bool utf8_validate(const char *s) { int len; for (; *s != 0; s += len) { len = utf8_validate_cz(s); if (len == 0) return false; } return true; } /* * Read a single UTF-8 character starting at @s, * returning the length, in bytes, of the character read. * * This function assumes input is valid UTF-8, * and that there are enough characters in front of @s. */ static int utf8_read_char(const char *s, js_uchar_t *out) { const unsigned char *c = (const unsigned char*) s; assert(utf8_validate_cz(s)); if (c[0] <= 0x7F) { /* 00..7F */ *out = c[0]; return 1; } else if (c[0] <= 0xDF) { /* C2..DF (unless input is invalid) */ *out = ((js_uchar_t)c[0] & 0x1F) << 6 | ((js_uchar_t)c[1] & 0x3F); return 2; } else if (c[0] <= 0xEF) { /* E0..EF */ *out = ((js_uchar_t)c[0] & 0xF) << 12 | ((js_uchar_t)c[1] & 0x3F) << 6 | ((js_uchar_t)c[2] & 0x3F); return 3; } else { /* F0..F4 (unless input is invalid) */ *out = ((js_uchar_t)c[0] & 0x7) << 18 | ((js_uchar_t)c[1] & 0x3F) << 12 | ((js_uchar_t)c[2] & 0x3F) << 6 | ((js_uchar_t)c[3] & 0x3F); return 4; } } /* * Write a single UTF-8 character to @s, * returning the length, in bytes, of the character written. * * @unicode must be U+0000..U+10FFFF, but not U+D800..U+DFFF. * * This function will write up to 4 bytes to @out. */ static int utf8_write_char(js_uchar_t unicode, char *out) { unsigned char *o = (unsigned char*) out; failx( unicode <= 0x10FFFF && !(unicode >= 0xD800 && unicode <= 0xDFFF), 1, "Illegal Unicode codepoint 0x%08X found", unicode ); if (unicode <= 0x7F) { /* U+0000..U+007F */ *o++ = unicode; return 1; } else if (unicode <= 0x7FF) { /* U+0080..U+07FF */ *o++ = 0xC0 | unicode >> 6; *o++ = 0x80 | (unicode & 0x3F); return 2; } else if (unicode <= 0xFFFF) { /* U+0800..U+FFFF */ *o++ = 0xE0 | unicode >> 12; *o++ = 0x80 | (unicode >> 6 & 0x3F); *o++ = 0x80 | (unicode & 0x3F); return 3; } else { /* U+10000..U+10FFFF */ *o++ = 0xF0 | unicode >> 18; *o++ = 0x80 | (unicode >> 12 & 0x3F); *o++ = 0x80 | (unicode >> 6 & 0x3F); *o++ = 0x80 | (unicode & 0x3F); return 4; } } /* * Compute the Unicode codepoint of a UTF-16 surrogate pair. * * @uc should be 0xD800..0xDBFF, and @lc should be 0xDC00..0xDFFF. * If they aren't, this function returns false. */ static bool from_surrogate_pair(uint16_t uc, uint16_t lc, js_uchar_t *unicode) { if (uc >= 0xD800 && uc <= 0xDBFF && lc >= 0xDC00 && lc <= 0xDFFF) { *unicode = 0x10000 + ((((js_uchar_t)uc & 0x3FF) << 10) | (lc & 0x3FF)); return true; } else { return false; } } /* * Construct a UTF-16 surrogate pair given a Unicode codepoint. * * @unicode must be U+10000..U+10FFFF. */ static void to_surrogate_pair(js_uchar_t unicode, uint16_t *uc, uint16_t *lc) { js_uchar_t n; failx( unicode >= 0x10000 && unicode <= 0x10FFFF, 1, "Cannot construct UTF-16 surrogate pair for Unicode codepoint 0x%08X", unicode ); n = unicode - 0x10000; *uc = ((n >> 10) & 0x3FF) | 0xD800; *lc = (n & 0x3FF) | 0xDC00; } #define is_space(c) ((c) == '\t' || (c) == '\n' || (c) == '\r' || (c) == ' ') #define is_digit(c) ((c) >= '0' && (c) <= '9') static bool parse_value (const char **sp, JsonNode **out); static bool parse_string (const char **sp, char **out); static bool parse_number (const char **sp, double *out); static bool parse_array (const char **sp, JsonNode **out); static bool parse_object (const char **sp, JsonNode **out); static bool parse_hex16 (const char **sp, uint16_t *out); static bool expect_literal (const char **sp, const char *str); static void skip_space (const char **sp); static void emit_value (SB *out, const JsonNode *node); static void emit_value_indented (SB *out, const JsonNode *node, const char *space, int indent_level); static void emit_string (SB *out, const char *str); static void emit_number (SB *out, double num); static void emit_array (SB *out, const JsonNode *array); static void emit_array_indented (SB *out, const JsonNode *array, const char *space, int indent_level); static void emit_object (SB *out, const JsonNode *object); static void emit_object_indented (SB *out, const JsonNode *object, const char *space, int indent_level); static int write_hex16(char *out, uint16_t val); static JsonNode *mknode(JsonTag tag); static void append_node(JsonNode *parent, JsonNode *child); static void prepend_node(JsonNode *parent, JsonNode *child); static void insert_node(JsonNode *parent, JsonNode *child); static void append_member(JsonNode *object, char *key, JsonNode *value); static void (*append_member_node_fn)(JsonNode *parent, JsonNode *child) = append_node; /* Assertion-friendly validity checks */ static bool tag_is_valid(unsigned int tag); static bool number_is_valid(const char *num); JsonNode *json_decode(const char *json) { const char *s = json; JsonNode *ret; skip_space(&s); if (!parse_value(&s, &ret)) return NULL; skip_space(&s); if (*s != 0) { json_delete(ret); return NULL; } return ret; } char *json_encode(const JsonNode *node) { return json_stringify(node, NULL); } char *json_encode_string(const char *str) { SB sb; sb_init(&sb); emit_string(&sb, str); return sb_finish(&sb); } char *json_stringify(const JsonNode *node, const char *space) { SB sb; sb_init(&sb); if (space != NULL) emit_value_indented(&sb, node, space, 0); else emit_value(&sb, node); return sb_finish(&sb); } void json_delete(JsonNode *node) { if (node != NULL) { json_remove_from_parent(node); switch (node->tag) { case JSON_STRING: free(node->string_); break; case JSON_ARRAY: case JSON_OBJECT: { JsonNode *child, *next; for (child = node->children.head; child != NULL; child = next) { next = child->next; json_delete(child); } break; } default:; } free(node); } } bool json_validate(const char *json) { const char *s = json; skip_space(&s); if (!parse_value(&s, NULL)) return false; skip_space(&s); if (*s != 0) return false; return true; } JsonNode *json_find_element(JsonNode *array, int index) { JsonNode *element; int i = 0; if (array == NULL || array->tag != JSON_ARRAY) return NULL; json_foreach(element, array) { if (i == index) return element; i++; } return NULL; } JsonNode *json_find_member(JsonNode *object, const char *name) { JsonNode *member; if (object == NULL || object->tag != JSON_OBJECT) return NULL; json_foreach(member, object) if (strcmp(member->key, name) == 0) return member; return NULL; } JsonNode *json_first_child(const JsonNode *node) { if (node != NULL && (node->tag == JSON_ARRAY || node->tag == JSON_OBJECT)) return node->children.head; return NULL; } static JsonNode *mknode(JsonTag tag) { JsonNode *ret = (JsonNode*) calloc(1, sizeof(JsonNode)); if (ret == NULL) out_of_memory(); ret->tag = tag; return ret; } JsonNode *json_mknull(void) { return mknode(JSON_NULL); } JsonNode *json_mkbool(bool b) { JsonNode *ret = mknode(JSON_BOOL); ret->bool_ = b; return ret; } static JsonNode *mkstring(char *s) { JsonNode *ret = mknode(JSON_STRING); ret->string_ = s; return ret; } JsonNode *json_mkstring(const char *s) { return mkstring(json_strdup(s)); } JsonNode *json_mknumber(double n) { JsonNode *node = mknode(JSON_NUMBER); node->number_ = n; return node; } JsonNode *json_mkarray(void) { return mknode(JSON_ARRAY); } JsonNode *json_mkobject(void) { return mknode(JSON_OBJECT); } static void append_node(JsonNode *parent, JsonNode *child) { if (!child) return; child->parent = parent; child->prev = parent->children.tail; child->next = NULL; if (parent->children.tail != NULL) parent->children.tail->next = child; else parent->children.head = child; parent->children.tail = child; } static void prepend_node(JsonNode *parent, JsonNode *child) { if (!child) return; child->parent = parent; child->prev = NULL; child->next = parent->children.head; if (parent->children.head != NULL) parent->children.head->prev = child; else parent->children.tail = child; parent->children.head = child; } static void insert_node(JsonNode *parent, JsonNode *child) { if (!child) return; JsonNode *this = parent->children.head; while (this != NULL && strcmp(this->key, child->key)) this = this->next; if (this != NULL) { /* we found a matching key, insert before this node */ if (this->prev == NULL) { prepend_node(parent, child); } else { child->parent = parent; child->next = this->next; child->prev = this->prev; this->prev->next = child; this->prev = child; } json_delete(this); } else append_node(parent, child); } static void append_member(JsonNode *object, char *key, JsonNode *value) { if (!value) return; value->key = key; append_member_node_fn(object, value); } void json_append_element(JsonNode *array, JsonNode *element) { if (!element) return; failx( array->tag == JSON_ARRAY, 1, "Cannot append %s to non-array %s", json_encode(element), json_encode(array) ); assert(element->parent == NULL); append_node(array, element); } void json_prepend_element(JsonNode *array, JsonNode *element) { if (!element) return; failx( array->tag == JSON_ARRAY, 1, "Cannot append %s to non-array %s", json_encode(element), json_encode(array) ); assert(element->parent == NULL); prepend_node(array, element); } void json_append_member(JsonNode *object, const char *key, JsonNode *value) { if (!value) return; failx( object->tag == JSON_OBJECT, 1, "Cannot add {\"%s\":%s} to non-object %s", key, json_encode(value), json_encode(object) ); assert(value->parent == NULL); append_member(object, json_strdup(key), value); } void json_prepend_member(JsonNode *object, const char *key, JsonNode *value) { if (!value) return; failx( object->tag == JSON_OBJECT, 1, "Cannot add {\"%s\":%s} to non-object %s", key, json_encode(value), json_encode(object) ); assert(value->parent == NULL); value->key = json_strdup(key); prepend_node(object, value); } void json_dedup_members(bool b) { append_member_node_fn = b ? insert_node : append_node; } void json_remove_from_parent(JsonNode *node) { JsonNode *parent = node->parent; if (parent != NULL) { if (node->prev != NULL) node->prev->next = node->next; else parent->children.head = node->next; if (node->next != NULL) node->next->prev = node->prev; else parent->children.tail = node->prev; free(node->key); node->parent = NULL; node->prev = node->next = NULL; node->key = NULL; } } static bool parse_value(const char **sp, JsonNode **out) { const char *s = *sp; switch (*s) { case 'n': if (expect_literal(&s, "null")) { if (out) *out = json_mknull(); *sp = s; return true; } return false; case 'f': if (expect_literal(&s, "false")) { if (out) *out = json_mkbool(false); *sp = s; return true; } return false; case 't': if (expect_literal(&s, "true")) { if (out) *out = json_mkbool(true); *sp = s; return true; } return false; case '"': { char *str; if (parse_string(&s, out ? &str : NULL)) { if (out) *out = mkstring(str); *sp = s; return true; } return false; } case '[': if (parse_array(&s, out)) { *sp = s; return true; } return false; case '{': if (parse_object(&s, out)) { *sp = s; return true; } return false; default: { double num; if (parse_number(&s, out ? &num : NULL)) { if (out) *out = json_mknumber(num); *sp = s; return true; } return false; } } } static bool parse_array(const char **sp, JsonNode **out) { const char *s = *sp; JsonNode *ret = out ? json_mkarray() : NULL; JsonNode *element; if (*s++ != '[') goto failure; skip_space(&s); if (*s == ']') { s++; goto success; } for (;;) { if (!parse_value(&s, out ? &element : NULL)) goto failure; skip_space(&s); if (out) json_append_element(ret, element); if (*s == ']') { s++; goto success; } if (*s++ != ',') goto failure; skip_space(&s); } success: *sp = s; if (out) *out = ret; return true; failure: json_delete(ret); return false; } static bool parse_object(const char **sp, JsonNode **out) { const char *s = *sp; JsonNode *ret = out ? json_mkobject() : NULL; char *key; JsonNode *value; if (*s++ != '{') goto failure; skip_space(&s); if (*s == '}') { s++; goto success; } for (;;) { if (!parse_string(&s, out ? &key : NULL)) goto failure; skip_space(&s); if (*s++ != ':') goto failure_free_key; skip_space(&s); if (!parse_value(&s, out ? &value : NULL)) goto failure_free_key; skip_space(&s); if (out) append_member(ret, key, value); if (*s == '}') { s++; goto success; } if (*s++ != ',') goto failure; skip_space(&s); } success: *sp = s; if (out) *out = ret; return true; failure_free_key: if (out) free(key); failure: json_delete(ret); return false; } bool parse_string(const char **sp, char **out) { const char *s = *sp; SB sb; char throwaway_buffer[4]; /* enough space for a UTF-8 character */ char *b; if (*s++ != '"') return false; if (out) { sb_init(&sb); sb_need(&sb, 4); b = sb.cur; } else { b = throwaway_buffer; } while (*s != '"') { unsigned char c = *s++; /* Parse next character, and write it to b. */ if (c == '\\') { c = *s++; switch (c) { case '"': case '\\': case '/': *b++ = c; break; case 'b': *b++ = '\b'; break; case 'f': *b++ = '\f'; break; case 'n': *b++ = '\n'; break; case 'r': *b++ = '\r'; break; case 't': *b++ = '\t'; break; case 'u': { uint16_t uc, lc; js_uchar_t unicode; if (!parse_hex16(&s, &uc)) goto failed; if (uc >= 0xD800 && uc <= 0xDFFF) { /* Handle UTF-16 surrogate pair. */ if (*s++ != '\\' || *s++ != 'u' || !parse_hex16(&s, &lc)) goto failed; /* Incomplete surrogate pair. */ if (!from_surrogate_pair(uc, lc, &unicode)) goto failed; /* Invalid surrogate pair. */ } else if (uc == 0) { /* Disallow "\u0000". */ goto failed; } else { unicode = uc; } b += utf8_write_char(unicode, b); break; } default: /* Invalid escape */ goto failed; } } else if (c <= 0x1F) { /* Control characters are not allowed in string literals. */ goto failed; } else { /* Validate and echo a UTF-8 character. */ int len; s--; len = utf8_validate_cz(s); if (len == 0) goto failed; /* Invalid UTF-8 character. */ while (len--) *b++ = *s++; } /* * Update sb to know about the new bytes, * and set up b to write another character. */ if (out) { sb.cur = b; sb_need(&sb, 4); b = sb.cur; } else { b = throwaway_buffer; } } s++; if (out) *out = sb_finish(&sb); *sp = s; return true; failed: if (out) sb_free(&sb); return false; } /* * The JSON spec says that a number shall follow this precise pattern * (spaces and quotes added for readability): * '-'? (0 | [1-9][0-9]*) ('.' [0-9]+)? ([Ee] [+-]? [0-9]+)? * * However, some JSON parsers are more liberal. For instance, PHP accepts * '.5' and '1.'. JSON.parse accepts '+3'. * * This function takes the strict approach. */ bool parse_number(const char **sp, double *out) { const char *s = *sp; /* '-'? */ if (*s == '-') s++; /* (0 | [1-9][0-9]*) */ if (*s == '0') { s++; } else { if (!is_digit(*s)) return false; do { s++; } while (is_digit(*s)); } /* ('.' [0-9]+)? */ if (*s == '.') { s++; if (!is_digit(*s)) return false; do { s++; } while (is_digit(*s)); } /* ([Ee] [+-]? [0-9]+)? */ if (*s == 'E' || *s == 'e') { s++; if (*s == '+' || *s == '-') s++; if (!is_digit(*s)) return false; do { s++; } while (is_digit(*s)); } if (out) *out = strtod(*sp, NULL); *sp = s; return true; } static void skip_space(const char **sp) { const char *s = *sp; while (is_space(*s)) s++; *sp = s; } static void emit_value(SB *out, const JsonNode *node) { assert(tag_is_valid(node->tag)); switch (node->tag) { case JSON_NULL: sb_puts(out, "null"); break; case JSON_BOOL: sb_puts(out, node->bool_ ? "true" : "false"); break; case JSON_STRING: emit_string(out, node->string_); break; case JSON_NUMBER: emit_number(out, node->number_); break; case JSON_ARRAY: emit_array(out, node); break; case JSON_OBJECT: emit_object(out, node); break; default: assert(false); } } void emit_value_indented(SB *out, const JsonNode *node, const char *space, int indent_level) { assert(tag_is_valid(node->tag)); switch (node->tag) { case JSON_NULL: sb_puts(out, "null"); break; case JSON_BOOL: sb_puts(out, node->bool_ ? "true" : "false"); break; case JSON_STRING: emit_string(out, node->string_); break; case JSON_NUMBER: emit_number(out, node->number_); break; case JSON_ARRAY: emit_array_indented(out, node, space, indent_level); break; case JSON_OBJECT: emit_object_indented(out, node, space, indent_level); break; default: assert(false); } } static void emit_array(SB *out, const JsonNode *array) { const JsonNode *element; sb_putc(out, '['); json_foreach(element, array) { emit_value(out, element); if (element->next != NULL) sb_putc(out, ','); } sb_putc(out, ']'); } static void emit_array_indented(SB *out, const JsonNode *array, const char *space, int indent_level) { const JsonNode *element = array->children.head; int i; if (element == NULL) { sb_puts(out, "[]"); return; } sb_puts(out, "[\n"); while (element != NULL) { for (i = 0; i < indent_level + 1; i++) sb_puts(out, space); emit_value_indented(out, element, space, indent_level + 1); element = element->next; sb_puts(out, element != NULL ? ",\n" : "\n"); } for (i = 0; i < indent_level; i++) sb_puts(out, space); sb_putc(out, ']'); } static void emit_object(SB *out, const JsonNode *object) { const JsonNode *member; sb_putc(out, '{'); json_foreach(member, object) { emit_string(out, member->key); sb_putc(out, ':'); emit_value(out, member); if (member->next != NULL) sb_putc(out, ','); } sb_putc(out, '}'); } static void emit_object_indented(SB *out, const JsonNode *object, const char *space, int indent_level) { const JsonNode *member = object->children.head; int i; if (member == NULL) { sb_puts(out, "{}"); return; } sb_puts(out, "{\n"); while (member != NULL) { for (i = 0; i < indent_level + 1; i++) sb_puts(out, space); emit_string(out, member->key); sb_puts(out, ": "); emit_value_indented(out, member, space, indent_level + 1); member = member->next; sb_puts(out, member != NULL ? ",\n" : "\n"); } for (i = 0; i < indent_level; i++) sb_puts(out, space); sb_putc(out, '}'); } void emit_string(SB *out, const char *str) { bool escape_unicode = false; const char *s = str; char *b; assert(utf8_validate(str)); /* * 14 bytes is enough space to write up to two * \uXXXX escapes and two quotation marks. */ sb_need(out, 14); b = out->cur; *b++ = '"'; while (*s != 0) { unsigned char c = *s++; /* Encode the next character, and write it to b. */ switch (c) { case '"': *b++ = '\\'; *b++ = '"'; break; case '\\': *b++ = '\\'; *b++ = '\\'; break; case '\b': *b++ = '\\'; *b++ = 'b'; break; case '\f': *b++ = '\\'; *b++ = 'f'; break; case '\n': *b++ = '\\'; *b++ = 'n'; break; case '\r': *b++ = '\\'; *b++ = 'r'; break; case '\t': *b++ = '\\'; *b++ = 't'; break; default: { int len; s--; len = utf8_validate_cz(s); if (len == 0) { /* * Handle invalid UTF-8 character gracefully in production * by writing a replacement character (U+FFFD) * and skipping a single byte. * * This should never happen when assertions are enabled * due to the assertion at the beginning of this function. */ assert(false); if (escape_unicode) { #if HAVE_STRLCPY strlcpy(b, "\\uFFFD", out->end - out->start ); #else strcpy(b, "\\uFFFD"); #endif b += 6; } else { *b++ = (char)0xEF; *b++ = (char)0xBF; *b++ = (char)0xBD; } s++; } else if (c < 0x1F || (c >= 0x80 && escape_unicode)) { /* Encode using \u.... */ uint32_t unicode; s += utf8_read_char(s, &unicode); if (unicode <= 0xFFFF) { *b++ = '\\'; *b++ = 'u'; b += write_hex16(b, unicode); } else { /* Produce a surrogate pair. */ uint16_t uc, lc; assert(unicode <= 0x10FFFF); to_surrogate_pair(unicode, &uc, &lc); *b++ = '\\'; *b++ = 'u'; b += write_hex16(b, uc); *b++ = '\\'; *b++ = 'u'; b += write_hex16(b, lc); } } else { /* Write the character directly. */ while (len--) *b++ = *s++; } break; } } /* * Update *out to know about the new bytes, * and set up b to write another encoded character. */ out->cur = b; sb_need(out, 14); b = out->cur; } *b++ = '"'; out->cur = b; } static void emit_number(SB *out, double num) { /* * This isn't exactly how JavaScript renders numbers, * but it should produce valid JSON for reasonable numbers * preserve precision well enough, and avoid some oddities * like 0.3 -> 0.299999999999999988898 . */ char buf[64]; snprintf(buf, sizeof(buf), "%.16g", num); if (number_is_valid(buf)) sb_puts(out, buf); else sb_puts(out, "null"); } static bool tag_is_valid(unsigned int tag) { return (/* tag >= JSON_NULL && */ tag <= JSON_OBJECT); } static bool number_is_valid(const char *num) { return (parse_number(&num, NULL) && *num == '\0'); } static bool expect_literal(const char **sp, const char *str) { const char *s = *sp; while (*str != '\0') if (*s++ != *str++) return false; *sp = s; return true; } /* * Parses exactly 4 hex characters (capital or lowercase). * Fails if any input chars are not [0-9A-Fa-f]. */ static bool parse_hex16(const char **sp, uint16_t *out) { const char *s = *sp; uint16_t ret = 0; uint16_t i; uint16_t tmp; char c; for (i = 0; i < 4; i++) { c = *s++; if (c >= '0' && c <= '9') tmp = c - '0'; else if (c >= 'A' && c <= 'F') tmp = c - 'A' + 10; else if (c >= 'a' && c <= 'f') tmp = c - 'a' + 10; else return false; ret <<= 4; ret += tmp; } if (out) *out = ret; *sp = s; return true; } /* * Encodes a 16-bit number into hexadecimal, * writing exactly 4 hex chars. */ static int write_hex16(char *out, uint16_t val) { const char *hex = "0123456789ABCDEF"; *out++ = hex[(val >> 12) & 0xF]; *out++ = hex[(val >> 8) & 0xF]; *out++ = hex[(val >> 4) & 0xF]; *out++ = hex[ val & 0xF]; return 4; } bool json_check(const JsonNode *node, char errmsg[256]) { #define problem(...) do { \ if (errmsg != NULL) \ snprintf(errmsg, 256, __VA_ARGS__); \ return false; \ } while (0) if (node->key != NULL && !utf8_validate(node->key)) problem("key contains invalid UTF-8"); if (!tag_is_valid(node->tag)) problem("tag is invalid (%u)", node->tag); if (node->tag == JSON_BOOL) { if (node->bool_ != false && node->bool_ != true) problem("bool_ is neither false (%d) nor true (%d)", (int)false, (int)true); } else if (node->tag == JSON_STRING) { if (node->string_ == NULL) problem("string_ is NULL"); if (!utf8_validate(node->string_)) problem("string_ contains invalid UTF-8"); } else if (node->tag == JSON_ARRAY || node->tag == JSON_OBJECT) { JsonNode *head = node->children.head; JsonNode *tail = node->children.tail; if (head == NULL || tail == NULL) { if (head != NULL) problem("tail is NULL, but head is not"); if (tail != NULL) problem("head is NULL, but tail is not"); } else { JsonNode *child; JsonNode *last = NULL; if (head->prev != NULL) problem("First child's prev pointer is not NULL"); for (child = head; child != NULL; last = child, child = child->next) { if (child == node) problem("node is its own child"); if (child->next == child) problem("child->next == child (cycle)"); if (child->next == head) problem("child->next == head (cycle)"); if (child->parent != node) problem("child does not point back to parent"); if (child->next != NULL && child->next->prev != child) problem("child->next does not point back to child"); if (node->tag == JSON_ARRAY && child->key != NULL) problem("Array element's key is not NULL"); if (node->tag == JSON_OBJECT && child->key == NULL) problem("Object member's key is NULL"); if (!json_check(child, errmsg)) return false; } if (last != tail) problem("tail does not match pointer found by starting at head and following next links"); } } return true; #undef problem } jo-1.9/json.h000066400000000000000000000067051433077231700131340ustar00rootroot00000000000000/* Copyright (C) 2011 Joseph A. Adams (joeyadams3.14159@gmail.com) All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef CCAN_JSON_H #define CCAN_JSON_H #include #include typedef enum { JSON_NULL, JSON_BOOL, JSON_STRING, JSON_NUMBER, JSON_ARRAY, JSON_OBJECT, } JsonTag; typedef struct JsonNode JsonNode; struct JsonNode { /* only if parent is an object or array (NULL otherwise) */ JsonNode *parent; JsonNode *prev, *next; /* only if parent is an object (NULL otherwise) */ char *key; /* Must be valid UTF-8. */ JsonTag tag; union { /* JSON_BOOL */ bool bool_; /* JSON_STRING */ char *string_; /* Must be valid UTF-8. */ /* JSON_NUMBER */ double number_; /* JSON_ARRAY */ /* JSON_OBJECT */ struct { JsonNode *head, *tail; } children; }; }; /*** Encoding, decoding, and validation ***/ JsonNode *json_decode (const char *json); char *json_encode (const JsonNode *node); char *json_encode_string (const char *str); char *json_stringify (const JsonNode *node, const char *space); void json_delete (JsonNode *node); bool json_validate (const char *json); /*** Lookup and traversal ***/ JsonNode *json_find_element (JsonNode *array, int index); JsonNode *json_find_member (JsonNode *object, const char *key); JsonNode *json_first_child (const JsonNode *node); #define json_foreach(i, object_or_array) \ for ((i) = json_first_child(object_or_array); \ (i) != NULL; \ (i) = (i)->next) /*** Construction and manipulation ***/ JsonNode *json_mknull(void); JsonNode *json_mkbool(bool b); JsonNode *json_mkstring(const char *s); JsonNode *json_mknumber(double n); JsonNode *json_mkarray(void); JsonNode *json_mkobject(void); void json_append_element(JsonNode *array, JsonNode *element); void json_prepend_element(JsonNode *array, JsonNode *element); void json_append_member(JsonNode *object, const char *key, JsonNode *value); void json_prepend_member(JsonNode *object, const char *key, JsonNode *value); void json_dedup_members(bool b); void json_remove_from_parent(JsonNode *node); /*** Debugging ***/ /* * Look for structure and encoding problems in a JsonNode or its descendents. * * If a problem is detected, return false, writing a description of the problem * to errmsg (unless errmsg is NULL). */ bool json_check(const JsonNode *node, char errmsg[256]); #endif jo-1.9/meson.build000066400000000000000000000045321433077231700141500ustar00rootroot00000000000000project('jo', 'c', version: '1.9', license: 'GPL-2.0-or-later', meson_version: '>=0.57.0', default_options: ['warning_level=3', 'optimization=2']) PACKAGE_VERSION = meson.project_version() cc = meson.get_compiler('c') headers = [ 'stddef.h', 'stdint.h', 'stdlib.h', 'string.h', 'unistd.h', 'stdbool.h' ] functions = [ 'strchr', 'strrchr', 'strlcpy', 'strlcat', 'snprintf', 'pledge', 'err', 'errx' ] foreach h: headers cc.has_header(h, required: true) endforeach foreach f: functions add_project_arguments( '-DHAVE_@0@='.format(f.to_upper()) + cc.has_function(f).to_int().to_string(), language: 'c') endforeach add_project_arguments('-DPACKAGE_VERSION="@0@"'.format(PACKAGE_VERSION), language: 'c') pandoc = find_program('pandoc', required: false) if not pandoc.found() warning('pandoc not found, man pages rebuild will not be possible') jo1 = 'jo.1' else pandoc_commands = [pandoc, '-s', '-w', 'man', '-f', 'markdown', '-o'] jo1 = custom_target('jo.1', output: 'jo.1', input: 'jo.pandoc', build_always_stale: true, command: [pandoc_commands, '@OUTPUT@', '@INPUT@']).full_path() run_command(pandoc_commands, join_paths(meson.current_build_dir(), 'jo.1'), join_paths(meson.current_source_dir(), 'jo.pandoc'), check: false) custom_target('jo.md', output: 'jo.md', input: 'jo.pandoc', build_always_stale: true, command: [pandoc, '-s', '-w', 'gfm', '-f', 'markdown-smart', '-o', '@OUTPUT@', '@INPUT@']) endif install_man(jo1) bashcomp = dependency('bash-completion', required: false) if bashcomp.found() bashcompdir = bashcomp.get_variable(pkgconfig: 'completionsdir') else bashcompdir = join_paths(get_option('sysconfdir'), 'bash_completion.d') endif install_data('jo.bash', install_dir: bashcompdir) m_dep = cc.find_library('m', required : false) executable('jo', 'jo.c', 'base64.c', 'base64.h', 'json.c', dependencies: m_dep, install: true) summary({'Prefix': get_option('prefix'), 'C compiler': cc.get_id(), 'Pandoc': pandoc, 'Bash completion': join_paths(bashcompdir, 'jo.bash'), }) jo-1.9/press.md000066400000000000000000000061361433077231700134660ustar00rootroot00000000000000## "Press" reports * [Hacker News](https://news.ycombinator.com/item?id=11230023) * [Lobsters](https://lobste.rs/s/tyehi1/a_shell_command_to_create_json_jo) * [reddit](https://www.reddit.com/r/programming/comments/49sx6x/a_shell_command_to_create_json_jo) * [Hacker News](https://news.ycombinator.com/item?id=11272678) * [Trivium](http://chneukirchen.org/trivium/2016-05-13) jo-1.9/rpm-build/000077500000000000000000000000001433077231700136755ustar00rootroot00000000000000jo-1.9/rpm-build/jo.spec000066400000000000000000000026411433077231700151640ustar00rootroot00000000000000Name: jo Version: 1.9 Release: 2%{?dist} Summary: jo is a small utility to create JSON objects License: GPL2 URL: https://github.com/jpmens/jo Source0: https://github.com/jpmens/jo/releases/download/%{version}/jo-%{version}.tar.gz BuildRequires: autoconf BuildRequires: pandoc %description jo is a small utility to create JSON objects %prep %setup -q %build %configure make %{?_smp_mflags} make check %install rm -rf $RPM_BUILD_ROOT %make_install %files %doc %{_bindir}/* %{_mandir}/man1/* %if 0%{?suse_version} %{_datadir}/bash-completion/completions %else %{_sysconfdir}/bash_completion.d/%{name}.bash %endif %changelog * Thu Nov 04 2022 JP Mens 1.9 - bump version -- see Changelog * Thu Nov 04 2022 JP Mens 1.8 - bump version -- see Changelog * Sat Oct 29 2022 JP Mens 1.7 - bump version -- see Changelog * Sat Jul 18 2020 JP Mens 1.4 - bump version -- see Changelog * Tue Apr 28 2020 Christian Albrecht 1.3-2 - Fix broken download url - Make bash completion work on RHEL based distros * Tue Apr 7 2020 Kilian Cavalotti 1.3-1 - Bumped to 1.3 release version - Include bash-completion file in package * Thu May 18 2017 Fabian Arrotin 1.1-1 - Bumped to 1.1 release version * Wed Mar 15 2017 Fabian Arrotin 1.0-1 - initial spec jo-1.9/snapcraft.yaml000066400000000000000000000005721433077231700146530ustar00rootroot00000000000000name: jo version: "1.9" summary: jo description: | This is jo, a small utility to create JSON objects or arrays. confinement: strict grade: stable base: core20 apps: jo: command: usr/local/bin/jo plugs: [home, removable-media] parts: jo: plugin: autotools source-type: git source: https://github.com/jpmens/jo build-packages: - pkg-config jo-1.9/tests/000077500000000000000000000000001433077231700131445ustar00rootroot00000000000000jo-1.9/tests/jo-creator.txt000066400000000000000000000000411433077231700157450ustar00rootroot00000000000000Jan-Piet Mens jo-1.9/tests/jo-large1.json000066400000000000000000000675251433077231700156370ustar00rootroot00000000000000{"timezone":"Asia/Kolkata","cities":["Abhayāpuri","Abohar","Abrama","Achalpur","Achhnera","Adampur","Addanki","Adirampattinam","Adra","Adūr","Afzalgarh","Afzalpur","Agar","Agartala","Agra","Ahmadnagar","Ahmadpur","Ahmedabad","Ahraura","Airoli","Aistala","Aizawl","Ajmer","Ajnāla","Ajodhya","Ajra","Akalkot","Akaltara","Akbarpur","Akbarpur","Akivīdu","Akkarampalle","Aklera","Akola","Akot","Aland","Alandi","Alandur","Alangad","Alangāyam","Alappuzha","Allahābād","Almora","Alnāvar","Along","Alot","Aluva","Alwar","Alwaye","Alībāg","Alīganj","Alīgarh","Alīpur","Alīpur Duār","Amalner","Amalāpuram","Amaravati","Amarnāth","Amarpur","Amarpātan","Ambad","Ambasamudram","Ambattūr","Ambikāpur","Ambur","Ambāh","Ambājogāi","Ambāla","Amet","Amla","Amod","Amreli","Amritsar","Amroha","Amroli","Amrāvati","Amudālavalasa","Anakāpalle","Anamalais","Anand","Anandpur","Anantapur","Anantnag","Andol","Anekal","Angamāli","Angul","Anjad","Anjangaon","Anjār","Ankleshwar","Annigeri","Annur","Anshing","Anta","Anthiyur","Anūpgarh","Anūppur","Anūpshahr","Aonla","Arakkonam","Arang","Arantāngi","Arcot","Ariyalūr","Arkalgūd","Arni","Aroor","Arrah","Arsikere","Arukutti","Arumuganeri","Aruppukkottai","Arāmbāgh","Arāria","Ashoknagar","Ashoknagar Kalyangarh","Ashta","Ashta","Asifābād","Atarra","Athni","Atmakūr","Atraulī","Attili","Attingal","Attur","Auraiya","Aurangabad","Aurangābād","Aurād","Ausa","Avanigadda","Avinashi","Ayakudi","Azamgarh","Azhiyūr","Baberu","Babrāla","Babīna","Bachhraon","Bada Barabīl","Badagara","Baddi","Badlapur","Badnāwar","Badvel","Badūria","Bagaha","Bagaha","Bagalkot","Bagar","Bagasra","Bagdogra","Bagulā","Baharampur","Baheri","Bahjoi","Bahraigh","Bahula","Bahādurganj","Bahādurgarh","Baidyabāti","Baihar","Bail-Hongal","Bairāgnia","Bakhtiyārpur","Balarāmpur","Balasore","Ballālpur","Balod","Baloda Bāzār","Balrāmpur","Balāngīr","Banat","Banda","Bandipura","Banga","Banganapalle","Bangaon","Bangaon","Bangarapet","Bankra","Banmankhi","Bannūr","Bantvāl","Banūr","Bar Bigha","Bara Uchāna","Baranagar","Barauli","Baraut","Barbil","Barddhamān","Bareilly","Bargarh","Bargi","Barhiya","Bari Sādri","Barjala","Barki Saria","Barkā Kānā","Barnāla","Barpeta","Barpeta Road","Barpāli","Baruipur","Barwāla","Barwāni","Basavakalyān","Basavana Bāgevādi","Basi","Basi","Basi","Basmat","Basni","Bastī","Baswa","Bathinda","Batāla","Baud","Bawāna","Bayāna","Bedi","Begamganj","Begusarai","Begūn","Behat","Behror","Bela","Beldānga","Belgaum","Bellampalli","Bellary","Belonia","Belsand","Belūr","Bemetāra","Bengaluru","Beohāri","Berasia","Beri Khās","Betamcherla","Bettiah","Betūl","Bewar","Beypore","Beāwar","Bhabhua","Bhachāu","Bhadaur","Bhadohi","Bhadrakh","Bhadreswar","Bhadrāchalam","Bhadrāvati","Bhainsdehi","Bhaisa","Bhandāra","Bhanjanagar","Bharatpur","Bharthana","Bharwāri","Bharūch","Bhasāwar","Bhatkal","Bhattiprolu","Bhavnagar","Bhavāni","Bhawanipur","Bhawāni Mandi","Bhawānipatna","Bhawānīgarh","Bhayandar","Bhikangaon","Bhilai","Bhind","Bhindār","Bhinga","Bhitarwār","Bhiwadi","Bhiwandi","Bhiwāni","Bhogpur","Bhongaon","Bhongīr","Bhopāl","Bhor","Bhuban","Bhubaneshwar","Bhudgaon","Bhuj","Bhusāval","Bhādra","Bhādāsar","Bhāgalpur","Bhālki","Bhānder","Bhānpura","Bhānpurī","Bhānvad","Bhātpāra","Bhātāpāra","Bhāyāvadar","Bhīkhi","Bhīlwāra","Bhīmavaram","Bhīmunipatnam","Bhīnmāl","Bhūm","Biaora","Bidhūna","Bihār Sharīf","Bihārīganj","Bijapur","Bijbehara","Bijnor","Bijāwar","Bikramganj","Bilgi","Bilgrām","Bilhaur","Bilimora","Bilsanda","Bilsi","Bilthra","Bilāra","Bilāri","Bilāsipāra","Bilāspur","Bindki","Binka","Birmitrapur","Birpara","Birūr","Bisauli","Bishnupur","Bissāu","Biswān","Bobbili","Bodhan","Bodināyakkanūr","Boisar","Bokajān","Bokāro","Bolpur","Bongaigaon","Borivli","Borsad","Botād","Brahmapur","Brājarājnagar","Budaun","Buddh Gaya","Budge Budge","Budhlāda","Budhāna","Bulandshahr","Buldāna","Burhar","Burhānpur","Burla","Buxar","Byndoor","Byādgi","Bābai","Bābra","Bādāmi","Bāgepalli","Bāgha Purāna","Bāghpat","Bāh","Bāli","Bāli","Bālotra","Bālugaon","Bālurghāt","Bālāchor","Bālāghāt","Bālāpur","Bāmor Kalān","Bānapur","Bānda","Bāndīkūi","Bāngarmau","Bānka","Bānki","Bānkura","Bānsbāria","Bānsdīh","Bānsi","Bānswāda","Bānswāra","Bāntva","Bāpatla","Bāramūla","Bārdoli","Bārh","Bāri","Bārmer","Bārsi","Bāruni","Bārākpur","Bārāmati","Bārān","Bārāsat","Bāsoda","Bāsudebpur","Bāzpur","Bīdar","Bīkaner","Bīlāspur","Bīrpur","Bīsalpur","Būndi","Būndu","Calangute","Canning","Chakapara","Chaklāsi","Chakradharpur","Chaksu","Challakere","Challapalle","Chalāla","Chamba","Chamrajnagar","Chandannagar","Chandauli","Chanderi","Chandrakona","Chanduasi","Chandīgarh","Changanācheri","Channagiri","Channapatna","Channarāyapatna","Charkhi Dādri","Charkhāri","Charthāwal","Chatrapur","Chatrā","Chemmumiahpet","Chengalpattu","Chengam","Chengannūr","Chennai","Chennimalai","Cherpulassery","Cherthala","Chetput","Chettipālaiyam","Chetwayi","Cheyyar","Chhabra","Chhala","Chhaprauli","Chharra","Chhatarpur","Chhibrāmau","Chhindwāra","Chhota Udepur","Chhoti Sādri","Chhāpar","Chhāta","Chhātāpur","Chicholi","Chidambaram","Chidawa","Chik Ballāpur","Chikhli","Chikmagalūr","Chiknāyakanhalli","Chikodi","Chilakalūrupet","Chillupār","Chincholi","Chinna Salem","Chinnachowk","Chinnamanūr","Chintāmani","Chiplūn","Chirmiri","Chitradurga","Chittaranjan","Chittaurgarh","Chittūr","Chodavaram","Chopda","Chotila","Chunār","Churāchāndpur","Chākan","Chākia","Chālisgaon","Chāmpa","Chānasma","Chānda","Chāndor","Chāndpur","Chāndur","Chāndūr","Chāndūr Bāzār","Chāpar","Chāpra","Chās","Chāībāsa","Chīpurupalle","Chīrāla","Chītāpur","Chūru","Clement Town","Closepet","Cochin","Coimbatore","Colachel","Colgong","Colonelganj","Contai","Coondapoor","Cuddalore","Cuddapah","Cumbum","Cumbum","Cuncolim","Curchorem","Cuttack","Dabhoi","Daboh","Dabra","Dabwāli","Dahegām","Dalkola","Dalsingh Sarai","Daltonganj","Dam Dam","Damoh","Damān","Dandeli","Darbhanga","Darsi","Daryāpur","Dasnapur","Dasūya","Datia","Dattāpur","Daudnagar","Daund","Dausa","Davangere","Dehra Dūn","Dehri","Delhi","Denkanikota","Deoband","Deogarh","Deoghar","Deoli","Deoli","Deoli","Deolāli","Deoraniān","Deori Khās","Deoria","Depālpur","Deshnoke","Devakottai","Devanhalli","Devarkonda","Devgadh Bāriya","Devgarh","Dewas","Deūlgaon Rāja","Dhamtari","Dhanaula","Dhanaura","Dhanbad","Dhandhuka","Dhanera","Dharampur","Dharamsala","Dharangaon","Dharapuram","Dharmadam","Dharmanagar","Dharmapuri","Dharmavaram","Dharmābād","Dhaulpur","Dhaurahra","Dhekiajuli","Dhenkānāl","Dhing","Dholka","Dhone","Dhorāji","Dhrol","Dhrāngadhra","Dhuburi","Dhulagari","Dhuliān","Dhupgāri","Dhāka","Dhāmnod","Dhāmpur","Dhār","Dhāri","Dhāriwāl","Dhāruhera","Dhārūr","Dhūlia","Dhūri","Diamond Harbour","Dibai","Dibrugarh","Dicholi","Digboi","Dighwāra","Digras","Dimāpur","Dinapore","Dindigul","Dindori","Diphu","Diu","Doda","Doddaballapura","Dohad","Dombivli","Dondaicha","Dongargarh","Dornakal","Dorāha","Dubrājpur","Dugda","Duliāgaon","Dum Duma","Dumjor","Dumka","Dumra","Dumraon","Durg","Durgapur","Durgāpur","Dwārka","Dādri","Dāhānu","Dākor","Dāmnagar","Dārjiling","Dārwha","Dāsna","Dātāganj","Dīdwāna","Dīg","Dīglūr","Dīnhāta","Dīnānagar","Dīsa","Dūngarpur","Egra","Elamanchili","Ellenabad","Ellore","Elūr","Emmiganūr","Erandol","Erode","Erraguntla","Erāttupetta","Etāwa","Etāwah","Faizpur","Farakka","Faridabad","Farrukhnagar","Farrukhābād","Farīdkot","Farīdpur","Fatehganj West","Fatehgarh Chūriān","Fatehpur","Fatehpur","Fatehpur","Fatehpur Sīkri","Fatehābād","Fatehābād","Fatwa","Ferokh","Ferozepore","Forbesganj","Fort Gloster","French Rocks","Fyzābād","Fālākāta","Fāzilka","Fīrozpur Jhirka","Fīrozābād","Gadag","Gadag-Betageri","Gaddi Annaram","Gadhada","Gadhinglaj","Gadwāl","Gajendragarh","Gajraula","Gajuwaka","Gandevi","Gandhinagar","Gangoh","Gangolli","Gangtok","Gangākher","Gangānagar","Gangāpur","Gangāpur","Gangāpur","Gangārāmpur","Gangāwati","Ganj Dundwāra","Gannavaram","Garhmuktesar","Garhshankar","Garhwa","Garhākota","Gariadhar","Garui","Gauribidanur","Gauripur","Gaya","Gevrai","Gharaunda","Ghatkesar","Ghazīpur","Ghosī","Ghoti Budrukh","Ghugus","Ghātampur","Ghātanji","Ghātsīla","Ghātāl","Ghāziābād","Giddalūr","Giddarbāha","Gingee","Gobichettipalayam","Gobindpur","Gobārdānga","Godda","Godhra","Gohadi","Gohāna","Gokak","Gokarna","Gokavaram","Gola Gokarannāth","Golāghāt","Gomoh","Gondal","Gondiā","Gondā City","Gopālganj","Gorakhpur","Gorakhpur","Gorantla","Gosāba","Govardhan","Goyerkāta","Goālpāra","Greater Noida","Gubbi","Gudalur","Gudivāda","Gudiyatham","Gulbarga","Guledagudda","Gulābpura","Gulāothi","Gumia","Gumlā","Gummidipundi","Guna","Gundlupēt","Gunnaur","Guntakal Junction","Guntur","Gunupur","Gurgaon","Gurmatkāl","Gursahāiganj","Gursarāi","Guru Har Sahāi","Guruvāyūr","Guskhara","Guwahati","Gwalior","Gyānpur","Gādarwāra","Gāndarbal","Gāndhīdhām","Gīrīdīh","Gūduvāncheri","Gūdūr","Hadagalli","Hadgāon","Hailākāndi","Haldaur","Haldia","Haldwani","Haliyal","Halvad","Hamīrpur","Hamīrpur","Handiā","Hanumāngarh","Harda Khās","Hardoī","Haridwar","Harihar","Harpanahalli","Harpālpur","Harsūd","Harūr","Hasanpur","Hassan","Hastināpur","Hatta","Hazāribāgh","Hilsa","Himatnagar","Hindaun","Hindoria","Hindupur","Hinganghāt","Hingoli","Hinjilikatu","Hirekerūr","Hiriyūr","Hisar","Hisuā","Hodal","Hojāi","Holalkere","Hole Narsipur","Homnābād","Honavar","Honnāli","Hosdurga","Hoshangābād","Hoshiārpur","Hoskote","Hospet","Hosūr","Howli","Hubli","Hugli","Hukeri","Hungund","Hunsūr","Husainābād","Hyderābād","Hābra","Hāflong","Hājo","Hājīpur","Hālol","Hālīsahar","Hāngal","Hānsi","Hāora","Hāpur","Hārij","Hāsimāra","Hāthras","Hāveri","Hīrākud","Ichalkaranji","Ichchāpuram","Idappadi","Igatpuri","Ilkal","Imphāl","Indergarh","Indi","Indore","Indri","Indāpur","Ingrāj Bāzār","Injambakkam","Iringal","Irinjālakuda","Irugūr","Islāmnagar","Islāmpur","Islāmpur","Itimādpur","Itānagar","Itārsi","Jabalpur","Jagalūr","Jagatsinghapur","Jagdalpur","Jagdīshpur","Jagdīspur","Jaggayyapeta","Jagraon","Jagtiāl","Jagādhri","Jahāngīrābād","Jahānābād","Jahāzpur","Jaigaon","Jainagar","Jaipur","Jais","Jaisalmer","Jaisingpur","Jaito","Jaitāran","Jalalpore","Jalandhar","Jalesar","Jaleshwar","Jalgaon","Jalgaon Jamod","Jalor","Jalpāiguri","Jalālpur","Jalālābad","Jalālābād","Jalālābād","Jalālī","Jalārpet","Jambusar","Jamkhandi","Jammalamadugu","Jammu","Jamnagar","Jamshedpur","Jamālpur","Jamūī","Jandiāla","Jangaon","Jangipur","Jaorā","Jarwal","Jasdan","Jashpurnagar","Jasidih","Jaspur","Jaswantnagar","Jatani","Jatāra","Jaunpur","Jayamkondacholapuram","Jaynagar Majilpur","Jetpur","Jevargi","Jewar","Jeypore","Jhajjar","Jhalidā","Jhanjhārpur","Jharia","Jharsuguda","Jhinjhāna","Jhumri Telaiya","Jhunjhunūn","Jhā-Jhā","Jhābua","Jhālrapātan","Jhālu","Jhālāwār","Jhānsi","Jhārgrām","Jhīnjhak","Jhūsi","Jintūr","Jodhpur","Jodhpur","Jodiya Bandar","Jogbani","Jora","Jorhāt","Jugsālai","Junnar","Jājpur","Jālaun","Jālna","Jāmadoba","Jāmai","Jāmtāra","Jāmuria","Jānjgīr","Jānsath","Jāwad","Jīnd","Jūnāgadh","Jūnāgarh","Kabrāi","Kachhwa","Kadakkavoor","Kadayanallur","Kadi","Kadiri","Kadod","Kadūr","Kaikalūr","Kailāras","Kailāshahar","Kaimganj","Kaimori","Kairāna","Kaithal","Kakching","Kakdwip","Kakrāla","Kalakkādu","Kalamassery","Kalamb","Kalamnūri","Kalavoor","Kalghatgi","Kallakkurichchi","Kallakurichi","Kallidaikurichi","Kalmeshwar","Kalpatta","Kalugumalai","Kalyandurg","Kalyani","Kalyān","Kalānaur","Kamalganj","Kampli","Kanchipuram","Kandukūr","Kangayam","Kanigiri","Kankauli","Kannad","Kannauj","Kanniyākumāri","Kannod","Kannur","Kanpur","Kantābānji","Kanuru","Kapadvanj","Kapūrthala","Karamsad","Karanpur","Karauli","Kareli","Karera","Karhal","Karjat","Karmāla","Karnāl","Karol Bāgh","Kartārpur","Karur","Karwar","Karād","Karīmganj","Karīmnagar","Kasba","Kashipur","Kasrāwad","Katangi","Katangi","Katghora","Kathua","Katihar","Kattanam","Kattivākkam","Kawardha","Kayalpattinam","Keelakarai","Kekri","Kemrī","Kenda","Kendrāparha","Kerūr","Keshod","Keshorai Pātan","Kesinga","Khada","Khadki","Khagaria","Khagaul","Khair","Khairābād","Khairāgarh","Khairāgarh","Khajuraho Group of Monuments","Khalīlābād","Khamaria","Khambhāliya","Khambhāt","Khammam","Khandela","Khandwa","Khanna","Kharagpur","Kharagpur","Kharakvasla","Kharar","Khardah","Khargone","Kharkhauda","Kharsia","Khatauli","Khatīma","Kheda","Khedbrahma","Khekra","Kheri","Kherālu","Khetia","Khetri","Khilchipur","Khirkiyān","Khopoli","Khowai","Khuldābād","Khunti","Khurai","Khurda","Khurja","Khāchrod","Khāmgaon","Khānāpur","Khāpa","Khārupatia","Khātegaon","Khātra","Khūtār","Kichha","Kinwat","Kirandul","Kiraoli","Kishanganj","Kishangarh","Kishtwār","Kithor","Kizhake Chālakudi","Koch Bihār","Kodaikānāl","Kodarmā","Kodoli","Kodungallūr","Kodār","Kodīnar","Koelwār","Kohīma","Kokrajhar","Kolasib","Kolhāpur","Kolkata","Kollam","Kollegāl","Kolār","Kolāras","Konch","Kondagaon","Kondapalle","Konnagar","Konnūr","Konārka","Koothanallur","Kopargaon","Koppal","Kopāganj","Koratla","Korba","Koregaon","Korwai","Korāput","Kosamba","Kosi","Kosigi","Kota","Kotagiri","Kotamangalam","Kotapārh","Kotdwāra","Kotkapura","Kotma","Kotputli","Kottagūdem","Kottayam","Kottūru","Kotā","Kovilpatti","Kovvūr","Kovūr","Koynanagar","Kozhikode","Koāth","Krishnagiri","Krishnanagar","Krishnarājpet","Kuchaiburi","Kuchera","Kuchāman","Kudachi","Kuju","Kukshi","Kulgam","Kulittalai","Kulpahār","Kulti","Kulu","Kumarapalayam","Kumbakonam","Kumbalam","Kumbhrāj","Kumhāri","Kumta","Kunda","Kundarkhi","Kundgol","Kundla","Kunigal","Kunnamangalam","Kunnamkulam","Kuppam","Kurandvād","Kurduvādi","Kurinjippādi","Kurnool","Kushtagi","Kutiatodu","Kutiyāna","Kuttampuzha","Kuzhithurai","Kyathampalle","Kāgal","Kākināda","Kākori","Kālimpong","Kāliyāganj","Kālka","Kālna","Kālol","Kālpi","Kālānwāli","Kālāvad","Kāman","Kāmthi","Kāmākhyānagar","Kāmāreddi","Kāmārhāti","Kānchrāpāra","Kāndhla","Kāndi","Kāndla","Kānke","Kānker","Kānkānhalli","Kānnangād","Kānt","Kānth","Kāpren","Kāraikkudi","Kāraikāl","Kāramadai","Kāranja","Kārkala","Kārsiyāng","Kāsaragod","Kāsganj","Kāthor","Kātol","Kātoya","Kātpādi","Kātrās","Kāvali","Kāyankulam","Kīl Bhuvanagiri","Kīratpur","Kūdligi","Kūkatpalli","Kūmher","Lachhmangarh Sīkar","Lahār","Lakhnādon","Lakhyabad","Lakhīmpur","Laksar","Lakshmeshwar","Lal Bahadur Nagar","Lalgudi","Lalitpur","Lar","Latur","Laungowāl","Leh","Leteri","Limbdi","Lingsugūr","Lohārdagā","Lonar","Lonavla","Loni","Losal","Luckeesarai","Lucknow","Ludhiāna","Lumding Railway Colony","Lunglei","Lādnūn","Lādwa","Lāharpur","Lākheri","Lālganj","Lālganj","Lālgola","Lālpur","Lālsot","Lātehār","Lāthi","Lāwar Khās","Lūnāvāda","Machhlīshahr","Machilīpatnam","Madambakkam","Madanapalle","Maddūr","Madgaon","Madhipura","Madhubani","Madhugiri","Madhupur","Madhyamgram","Madikeri","Madipakkam","Madukkarai","Madukkūr","Madurai","Madurāntakam","Maghar","Maham","Mahbūbnagar","Mahbūbābād","Mahemdāvād","Mahendragarh","Maheshtala","Maheshwar","Mahgawān","Mahiari","Mahmudābād","Mahobā","Maholi","Mahudha","Mahwah","Mahād","Mahālingpur","Mahārāganj","Mahārājgani","Mahāsamund","Mahē","Mahīshādal","Maihar","Mainpuri","Maināguri","Mairwa","Majalgaon","Makrāna","Maksi","Malakanagiri","Malappuram","Malaut","Malavalli","Malkajgiri","Malkapur","Malkāpur","Mallasamudram","Malpe","Malīhābād","Manali","Manamadurai","Manapparai","Mancherāl","Mandamarri","Mandapam","Mandapeta","Mandi","Mandideep","Mandlā","Mandsaur","Mandya","Mandāwar","Maner","Mangalagiri","Mangaldai","Mangalore","Manglaur","Mangrūl Pīr","Maniar","Manihāri","Manipal","Manjeri","Manjhanpur","Mankāchar","Manmād","Mannargudi","Mannārakkāt","Manoharpur","Manthani","Manuguru","Manāsa","Manāwar","Marakkanam","Marayur","Margherita","Marhaura","Mariāhu","Mariāni","Masaurhi Buzurg","Mathura","Mattanur","Mau","Mau","Mau Aimma","Maudaha","Mauganj","Maur","Mavoor","Mawāna","Mayiladuthurai","Mayāng Imphāl","Medak","Medinīpur","Meerut","Mehkar","Mehndāwal","Melur","Memāri","Mendarda","Merta","Mettupalayam","Mettur","Mhāsvād","Mihona","Milak","Miriālgūda","Mirzāpur","Misrikh","Modāsa","Moga","Mohali","Moirāng","Mokameh","Mokokchūng","Mon","Monghyr","Monoharpur","Moram","Morbi","Morena","Morigaon","Morinda","Mormugao","Morsi","Morwa","Morādābād","Morār","Mothīhāri","Mubarakpur","Muddebihāl","Mudgal","Mudhol","Mudkhed","Mughal Sarāi","Muhammadābād","Muhammadābād","Muhammadābād","Mukeriān","Mukher","Muktsar","Mulbāgal","Mulgund","Multai","Mulugu","Muluppilagadu","Mumbai","Mundargi","Mundgod","Mundra","Mungaoli","Mungeli","Munnar","Murbād","Murlīganj","Murshidābād","Murtajāpur","Murudeshwara","Murwāra","Murādnagar","Mushābani","Musiri","Mussoorie","Muttupet","Muvattupuzha","Muzaffarnagar","Muzaffarpur","Mysore","Mācherla","Māchhīwāra","Māgadi","Mākum","Mālegaon","Māler Kotla","Mālpura","Mālvan","Mālūr","Māndal","Māndalgarh","Māndvi","Māndvi","Māngrol","Māngrol","Mānsa","Mānsa","Mānvi","Mānwat","Mānāvadar","Māpuca","Mārahra","Mārkāpur","Mātābhānga","Māvelikara","Mīnjūr","Mīrganj","Mīrānpur","Mīrānpur Katra","Mūdbidri","Mūl","Mūlki","Mūndwa","Mūvattupula","Nabīnagar","Nadiād","Naduvannūr","Nagar","Nagari","Nagda","Nagpur","Nagīna","Naharlagun","Nahorkatiya","Naihāti","Naini Tāl","Nainpur","Nainwa","Najafgarh","Najībābād","Nakodar","Naksalbāri","Nakūr","Naldurg","Nalgonda","Nalhāti","Nambiyūr","Nanauta","Nanded","Nandigāma","Nandikotkūr","Nandurbar","Nandyāl","Nangal","Nanjangūd","Napāsar","Naraina","Naraini","Narasannapeta","Narasapur","Narasaraopet","Narauli","Naraura","Naregal","Narela","Nargund","Narsimhapur","Narsinghgarh","Narsīpatnam","Narwar","Narwāna","Narāyangarh","Nashik","Nasrullāhganj","Nasīrābād","Nattam","Naugachhia","Nautanwa","Navadwīp","Navalgund","Navi Mumbai","Navsari","Nawalgarh","Nawanshahr","Nawābganj","Nawābganj","Nawābganj","Nawāda","Nayāgarh","Nedumangād","Neelankarai","Neem ka Thana","Negapatam","Nelamangala","Nellikkuppam","Nellore","Nepānagar","Neral","New Delhi","Neyveli","Neyyāttinkara","Nichlaul","Nidadavole","Nihtaur","Nilakottai","Nilanga","Nimāparha","Nipāni","Nirmal","Nirmāli","Niwai","Nizāmābād","Nohar","Noida","Nokha","Nongstoin","North Guwāhāti","North Lakhimpur","Nowrangapur","Noāmundi","Nābha","Nādbai","Nādāpuram","Nāgamangala","Nāgar Karnūl","Nāgaur","Nāgercoil","Nāgod","Nāhan","Nāmagiripettai","Nāmakkal","Nāmrup","Nāndgaon","Nāndūra Buzurg","Nāngloi Jāt","Nānpāra","Nāravārikuppam","Nārnaul","Nārnaund","Nārāyanpet","Nāspur","Nāsriganj","Nāthdwāra","Nāwa","Nāyudupet","Nīlgiri","Nīlokheri","Nīlēshwar","Nīmbāhera","Nīmāj","Nūrpur","Nūzvīd","Obra","Okha","Ongole","Ooty","Orai","Osmanabad","Ottappālam","Ozar","Pachperwa","Padam","Padampur","Padampur","Padmanābhapuram","Padra","Padrauna","Pahāsu","Paithan","Pakur","Palani","Palera","Paliā Kalān","Palladam","Pallappatti","Pallikondai","Pallippatti","Pallāvaram","Palmaner","Palwal","Palwancha","Palāsa","Panaji","Panchkula","Pandharpur","Pandua","Panna","Panruti","Panvel","Panāgar","Papanasam","Paramagudi","Paravūr Tekkumbhāgam","Parbhani","Pariyāpuram","Parli Vaijnāth","Parlākimidi","Parola","Partūr","Parvatsar","Parādīp Garh","Parāsia","Parīchhatgarh","Pasān","Patancheru","Pataudi","Pathalgaon","Pathanāmthitta","Patharia","Pathānkot","Patiāla","Patna","Patnāgarh","Patti","Pattukkottai","Patāmundai","Pauri","Pawni","Pawāyan","Payyannūr","Pedana","Peddapalli","Peddāpuram","Pehowa","Pen","Pennādam","Pennāgaram","Penugonda","Penukonda","Perambalur","Peranāmpattu","Peravurani","Periyakulam","Periyanayakkanpalaiyam","Perumbavoor","Perumpāvūr","Perundurai","Perungudi","Petlād","Phagwāra","Phalauda","Phalodi","Phaltan","Phaphūnd","Phek","Phillaur","Phirangipuram","Phulbāni","Phulera","Phulpur","Phusro","Pihānī","Pilibangan","Pilkhua","Pilāni","Pimpri","Pindwāra","Pinjaur","Pināhat","Pipili","Pipraich","Piravam","Piriyāpatna","Piro","Pithampur","Pithorāgarh","Pithāpuram","Pokaran","Polasara","Polavaram","Pollachi","Polūr","Ponda","Ponmana","Ponneri","Ponnur","Ponnāni","Ponnūru","Poonamalle","Porbandar","Porsa","Port Blair","Porur","Powai","Pratāpgarh","Proddatūr","Puducherry","Pudukkottai","Pujali","Pukhrāyān","Pulgaon","Pulivendla","Puliyangudi","Pulwama","Punalūr","Pune","Punganūru","Punjai Puliyampatti","Punāsa","Pupri","Puri","Purnia","Puruliya","Purwā","Pusad","Pushkar","Puttūr","Puttūr","Pāchora","Pākāla","Pālakkodu","Pālakollu","Pālanpur","Pālghar","Pālghāt","Pāli","Pāli","Pālitāna","Pālkonda","Pāloncha","Pānchla","Pāndhurnā","Pānihāti","Pānīpat","Pāonta Sāhib","Pāppinisshēri","Pārdi","Pārvatipuram","Pāsighāt","Pātan","Pāthardi","Pāthardih","Pāthri","Pātūr","Pāvugada","Pīlibhīt","Pīpri","Pīpār","Pūnch","Pūndri","Pūnāhāna","Pūranpur","Pūrna","Quthbullapur","Qādiān","Rabkavi","Raebareli","Rafiganj","Raghunathpur","Rahimatpur","Raigarh","Raipur","Raipur","Raisen","Rajaori","Rajapalaiyam","Rajpur","Rajpur","Rajpur Sonarpur","Ramagundam","Ramanathapuram","Ramanayyapeta","Rameswaram","Rampachodavaram","Rampur Hat","Ranchi","Rangia","Rangāpāra","Rasipuram","Rasrā","Ratangarh","Ratanpur","Ratia","Ratlām","Ratnagiri","Raurkela","Raxaul","Raybag","Rehli","Remuna","Renigunta","Renukūt","Reoti","Repalle","Revelganj","Rewa","Rewāri","Richha","Rishra","Rishīkesh","Risod","Robertsganj","Robertsonpet","Roha","Rohini","Rohtak","Ron","Roorkee","Ropar","Rura","Rusera","Rādhanpur","Rāghogarh","Rāhatgarh","Rāhuri","Rāichūr","Rāiganj","Rāikot","Rāipur","Rāisinghnagar","Rāj-Nāndgaon","Rājahmundry","Rājaldesar","Rājgarh","Rājgarh","Rājgarh","Rājgarh","Rājgurunagar","Rājgīr","Rājkot","Rājmahal","Rājpura","Rājpīpla","Rājsamand","Rājula","Rājākhera","Rājūra","Rāmachandrapuram","Rāmganj Mandi","Rāmgarh","Rāmgarh","Rāmgundam","Rāmjībanpur","Rāmnagar","Rāmnagar","Rāmnagar","Rāmpur","Rāmpur","Rāmpura","Rāmpura","Rāmtek","Rāmāpuram","Rānia","Rānikhet","Rānipet","Rānāghāt","Rānāvāv","Rānībennur","Rānīganj","Rānīpur","Rāpar","Rāth","Rāver","Rāwatbhāta","Rāwatsār","Rāya","Rāyachoti","Rāyadrug","Rāzampeta","Rāzām","Rīngas","Rūdarpur","Sabalgarh","Sadalgi","Sadashivpet","Sadābād","Safidon","Safīpur","Sagauli","Saharsa","Sahaspur","Sahaswān","Sahāranpur","Sahāwar","Saidpur","Saiha","Saint Thomas Mount","Sainthia","Sakleshpur","Saktī","Salem","Salāya","Sambalpur","Sambhal","Samdari","Samrāla","Samthar","Samālkha","Samāstipur","Sanaur","Sancoale","Sandīla","Sandūr","Sangamner","Sangariā","Sangod","Sangrūr","Sangāreddi","Sankeshwar","Sanāwad","Saoner","Saraipali","Sarauli","Sardhana","Sardulgarh","Sardārshahr","Sarkhej","Sarwār","Sarāi Mīr","Sarāi Ākil","Satara","Sathupalli","Sathyamangalam","Satna","Sattenapalle","Sattur","Satānā","Saugor","Saundatti","Sausar","Savanūr","Savarkundla","Sawāi Mādhopur","Secunderabad","Sehore","Selu","Sendhwa","Seohāra","Seondha","Seoni","Seoni Mālwa","Seram","Serchhīp","Serilingampalle","Shahbazpur","Shahdol","Shamsābād","Shamsābād","Shegaon","Sheikhpura","Sheoganj","Sheohar","Sheopur","Sherghāti","Sherkot","Shertallai","Shiggaon","Shikohābād","Shikārpur","Shikārpūr","Shillong","Shimla","Shimoga","Shiraguppi","Shirdi","Shirhatti","Shirpur","Shivaji Nagar","Shivpuri","Sholinghur","Shorāpur","Shrīgonda","Shrīrangapattana","Shrīrāmpur","Shujālpur","Shyamnagar","Shāhganj","Shāhi","Shāhjānpur","Shāhpur","Shāhpur","Shāhpur","Shāhpur","Shāhpura","Shāhpura","Shāhābād","Shāhābād","Shāhābād","Shāhābād","Shāhāda","Shājāpur","Shāmgarh","Shāmli","Shāntipur","Shīshgarh","Shōranūr","Sibsāgar","Siddhapur","Siddipet","Sidhaulī","Sidhi","Sidlaghatta","Sihor","Sihorā","Sijua","Sikandarpur","Sikandarābād","Sikandra Rao","Sikka","Silao","Silapathar","Silchar","Siliguri","Sillod","Silvassa","Simdega","Sindgi","Sindhnūr","Singarāyakonda","Singrauli","Singur","Singānallūr","Singāpur","Sinnar","Sirhind","Sirohi","Sironj","Sirsa","Sirsi","Sirsi","Sirsilla","Sirsāganj","Siruguppa","Sirumugai","Sirūr","Sisauli","Siswā Bāzār","Sitārganj","Siuri","Sivaganga","Sivagiri","Sivagiri","Sivakasi","Siwān","Siwāna","Sohna","Sohāgpur","Sojat","Sojītra","Solan","Solāpur","Someshwar","Sompeta","Sonepur","Songadh","Sonāmukhi","Sonāri","Sonīpat","Sopur","Sorada","Soro","Soron","Soygaon","Soyībug","Sri Dūngargarh","Sri Mādhopur","Srikakulam","Srinagar","Srirāmpur","Srivaikuntam","Srivilliputhur","Srīnagar","Srīnivāspur","Srīperumbūdūr","Srīrāmnagar","Srīsailain","Srīvardhan","Suket","Sultanpur","Sultānpur","Sulur","Sulya","Sundargarh","Sundarnagar","Sunel","Sunām","Supaul","Surat","Surendranagar","Suriānwān","Suriāpet","Suār","Sādri","Sāgar","Sāhibganj","Sālūmbar","Sālūr","Sāmalkot","Sāmba","Sāmbhar","Sānand","Sānchor","Sāndi","Sāngli","Sāngola","Sānkrāil","Sārangpur","Sāsvad","Sāvantvādi","Sāvda","Sāyla","Sīkar","Sīra","Sīrkāzhi","Sītāmarhi","Sītāpur","Sūjāngarh","Sūlūru","Sūrajgarh","Sūrandai","Sūratgarh","Takhatgarh","Takhatpur","Talegaon Dābhāde","Taleigao","Talipparamba","Taloda","Talwandi Bhai","Talwāra","Talāja","Tambaram","Tamlūk","Tanakpur","Tanjore","Tanuku","Tarakeswar","Tarikere","Tarn Tāran","Tarāna","Teghra","Tehri","Tekkalakote","Tekkali","Tekāri","Telhāra","Tellicherry","Teni","Teonthar","Terdāl","Tezpur","Thakurdwara","Tharangambadi","Tharād","Thenkasi","Thiruthani","Thiruvananthapuram","Thiruvarur","Thoothukudi","Thoubāl","Thrissur","Thākurganj","Thān","Thāna Bhawan","Thāne","Thānesar","Thāsra","Tijāra","Tilhar","Tindivanam","Tinnanūr","Tinsukia","Tiptūr","Tiruchchendur","Tiruchengode","Tiruchirappalli","Tirukkoyilur","Tirumala","Tirunelveli","Tirupati","Tirupparangunram","Tiruppur","Tiruppuvanam","Tirur","Tiruttangal","Tiruvalla","Tiruvallur","Tiruvannāmalai","Tiruvottiyūr","Tisaiyanvilai","Titlāgarh","Titāgarh","Todabhim","Todaraisingh","Tohāna","Tondi","Tonk","Tuensang","Tufānganj","Tuljāpur","Tulsīpur","Tumkūr","Tumsar","Tuni","Tura","Turaiyūr","Tādepalle","Tādepallegūdem","Tādpatri","Tājpur","Tāki","Tālcher","Tālīkota","Tānda","Tāndā","Tāndūr","Tāoru","Tāramangalam","Tārānagar","Tāsgaon","Tīkamgarh","Tīrthahalli","Tūndla","Udaipur","Udaipur","Udaipur","Udaipura","Udalguri","Udangudi","Udgīr","Udhampur","Udumalaippettai","Udupi","Ujhāni","Ujjain","Ulhasnagar","Ullal","Umarga","Umaria","Umarkhed","Umarkot","Umred","Umreth","Un","Una","Una","Unhel","Unjha","Unnāo","Upleta","Uppal Kalan","Uran","Uravakonda","Usehat","Usilampatti","Utraula","Uttamapālaiyam","Uttarkāshi","Uttiramerūr","V.S.K.Valasai (Dindigul-Dist.)","Vadakku Valliyūr","Vadakku Viravanallur","Vadamadurai","Vadigenhalli","Vadlapūdi","Vadnagar","Vadodara","Vaijāpur","Vaikam","Valabhīpur","Vallabh Vidyanagar","Valparai","Valsād","Vandavāsi","Vaniyambadi","Vapi","Varanasi","Varangaon","Varkala","Vasa","Vasco da Gama","Vasind","Vattalkundu","Vayalār","Vedaraniyam","Vejalpur","Vellore","Velur","Vemalwāda","Venkatagiri","Vepagunta","Verāval","Vetapālem","Vettaikkaranpudur","Vettūr","Vidisha","Vijayawada","Vijāpur","Vikārābād","Villupuram","Vinukonda","Virajpet","Virudunagar","Virār","Visakhapatnam","Visnagar","Vite","Vizianagaram","Vriddhāchalam","Vrindāvan","Vuyyūru","Vyāra","Vāda","Vādippatti","Vāsudevanallūr","Vīsāvadar","Wai","Walajapet","Wani","Wanparti","Warangal","Wardha","Warora","Warud","Wazīrganj","Wellington","Wer","Wokha","Wādi","Wānkāner","Wāris Alīganj","Wārāseonī","Wāshīm","Yamunānagar","Yanam","Yanamalakuduru","Yavatmāl","Yelahanka","Yellandu","Yellāpur","Yeola","Yādgīr","Yāval","Zahirābād","Zaidpur","Zamānia","Zira","Zunheboto","Ābu","Ābu Road","Ādilābād","Ādoni","Ālangulam","Āmli","Āmlāgora","Āmta","Āndippatti","Ārangaon","Āron","Ārvi","Āsandh","Āsansol","Āsika","Āsind","Āthagarh","Āvadi","Ūn"]} jo-1.9/tests/jo-large2.json000066400000000000000000000437321433077231700156320ustar00rootroot00000000000000{"timezone":"America/New_York","cities":["Aberdeen","Abington","Abington","Acton","Acworth","Adams Morgan","Adelphi","Agawam","Aiken","Akron","Alafaya","Albany","Albany","Albemarle","Alexandria","Allapattah","Allentown","Alliance","Allison Park","Alpharetta","Altamonte Springs","Altoona","Americus","Amesbury","Amherst","Amherst","Amherst Center","Amsterdam","Anderson","Annandale","Annapolis","Ansonia","Apex","Apopka","Arbutus","Arlington","Arlington","Arnold","Asbury Park","Ashburn","Asheboro","Asheville","Ashland","Ashland","Ashland","Ashtabula","Aspen Hill","Astoria","Athens","Athens","Atlanta","Atlantic City","Attleboro","Auburn","Auburn","Auburn","Auburndale","Augusta","Augusta","Aurora","Austintown","Avenel","Aventura","Avon","Avon Center","Avon Lake","Back Mountain","Baileys Crossroads","Baldwin","Baldwin","Ballenger Creek","Baltimore","Bangor","Barberton","Barnstable","Barrington","Bartow","Basking Ridge","Batavia","Bath Beach","Bay Shore","Bay Village","Baychester","Bayonet Point","Bayonne","Bayshore Gardens","Bayside","Bayville","Bear","Beavercreek","Beckley","Bedford","Bel Air North","Bel Air South","Belle Glade","Belleville","Bellmore","Belmont","Beltsville","Belvedere Park","Bensalem","Bensonhurst","Berea","Bergenfield","Bethel Park","Bethesda","Bethlehem","Bethpage","Beverly","Beverly Cove","Biddeford","Billerica","Binghamton","Blacksburg","Bloomfield","Bloomingdale","Bluffton","Boardman","Boca Del Mar","Boca Raton","Bon Air","Bonita Springs","Boone","Borough Park","Boston","Bowie","Bowling Green","Boynton Beach","Bradenton","Braintree","Brandon","Branford","Brentwood","Briarwood","Bridgeport","Bridgeton","Bridgewater","Brighton","Brighton Beach","Bristol","Bristol","Bristol","Bristol","Broadview Heights","Brockton","Brook Park","Brookhaven","Brookline","Brooklyn","Brooklyn Heights","Brownsville","Brownsville","Brunswick","Brunswick","Brunswick","Buckhall","Buenaventura Lakes","Buffalo","Burke","Burlington","Burlington","Burlington","Burlington","Bushwick","Calhoun","Calverton","Cambria Heights","Cambridge","Camden","Camp Springs","Canarsie","Candler-McAfee","Canton","Canton","Canton","Cape Coral","Carlisle","Carney","Carol City","Carrboro","Carrollton","Carrollwood","Carrollwood Village","Carteret","Cartersville","Cary","Casselberry","Catonsville","Cave Spring","Center City","Centereach","Centerville","Central Falls","Central Islip","Centreville","Chambersburg","Chamblee","Chantilly","Chapel Hill","Charleston","Charleston","Charlotte","Charlottesville","Chattanooga","Cheektowaga","Chelmsford","Chelsea","Cherry Hill","Cherry Hill","Chesapeake","Cheshire","Chester","Chester","Chicopee","Chillicothe","Chillum","Chinatown","Christiansburg","Cicero","Cincinnati","Citrus Park","City of Milford (balance)","Clark-Fulton","Clarksburg","Clay","Clayton","Clearwater","Clemmons","Clemson","Clermont","Cleveland","Cleveland","Cleveland Heights","Cliffside Park","Clifton","Clifton Park","Clinton","Cloverly","Cockeysville","Cocoa","Coconut Creek","Coconut Grove","Cohoes","Colchester","College Park","College Point","Collinwood","Colonia","Colonial Heights","Columbia","Columbia","Columbus","Columbus","Commack","Concord","Concord","Concord","Coney Island","Conway","Conyers","Cooper City","Copiague","Coral Gables","Coral Springs","Coral Terrace","Coram","Cornelius","Corona","Cortland","Cortlandt Manor","Country Club","Country Walk","Coventry","Covington","Cranberry Township","Cranford","Cranston","Crofton","Culpeper","Cumberland","Cumberland","Cutler","Cutler Bay","Cutler Ridge","Cuyahoga Falls","Cypress Hills","Dale City","Dalton","Damascus","Danbury","Dania Beach","Danvers","Danville","Danville","Darien","Davie","Dayton","Daytona Beach","DeBary","DeLand","Decatur","Dedham","Deer Park","Deerfield Beach","Defiance","Delaware","Delray Beach","Deltona","Denville","Depew","Derry","Derry Village","Detroit-Shoreway","Dix Hills","Doral","Douglasville","Dover","Dover","Dover","Dracut","Drexel Hill","Dublin","Dublin","Duluth","Dumont","Dundalk","Dunedin","Dunwoody","Durham","Duxbury","Dyker Heights","Easley","East Amherst","East Brainerd","East Brunswick","East Chattanooga","East Cleveland","East Concord","East Elmhurst","East Flatbush","East Hampton","East Harlem","East Hartford","East Haven","East Lake","East Lake-Orient Park","East Longmeadow","East Massapequa","East Meadow","East Naples","East New York","East Northport","East Norwalk","East Orange","East Patchogue","East Point","East Providence","East Ridge","East Riverdale","East Setauket","East Tremont","East Village","Eastchester","Easthampton","Eastlake","Easton","Easton","Easton","Eden","Edgewater","Edgewood","Edison","Eggertsville","Egypt Lake-Leto","Eldersburg","Elizabeth","Elizabeth City","Elizabethtown","Elkridge","Elkton","Ellicott City","Elmhurst","Elmira","Elmont","Elmwood Park","Elyria","Emerson Hill","Enfield","Englewood","Erie","Erlanger","Essex","Estero","Euclid","Eustis","Evans","Everett","Ewing","Fair Lawn","Fairborn","Fairfax","Fairfield","Fairfield","Fairhaven","Fairland","Fairmont","Fairview Park","Fall River","Far Rockaway","Farmington","Farmingville","Farragut","Fayetteville","Fayetteville","Ferndale","Financial District","Findlay","Fitchburg","Flagami","Flatbush","Flatlands","Fleming Island","Floral Park","Florence","Florence","Florida Ridge","Fordham","Fords","Forest Hills","Forest Park","Forest Park","Fort Bragg","Fort Hamilton","Fort Hunt","Fort Lauderdale","Fort Lee","Fort Myers","Fort Pierce","Fort Thomas","Fort Washington","Fountainebleau","Four Corners","Framingham","Framingham Center","Franconia","Frankfort","Franklin","Franklin Square","Frederick","Fredericksburg","Freeport","Fremont","Fresh Meadows","Front Royal","Fruit Cove","Fuquay-Varina","Gahanna","Gainesville","Gainesville","Gaithersburg","Garden City","Gardner","Garfield","Garfield Heights","Garner","Gastonia","Gates-North Gates","Georgetown","Germantown","Glassboro","Glassmanor","Glastonbury","Glen Burnie","Glen Cove","Glendale","Glenvar Heights","Glenville","Gloucester","Gloversville","Golden Gate","Golden Glades","Goldsboro","Goose Creek","Grafton","Gramercy Park","Grand Island","Graniteville","Gravesend","Great Falls","Great Kills","Greater Northdale","Greater Upper Marlboro","Green","Green Haven","Greenacres City","Greenbelt","Greenburgh","Greeneville","Greenfield","Greenpoint","Greensboro","Greenville","Greenville","Greenwood","Greer","Griffin","Grove City","Guilford","Gwynn Oak","Hackensack","Hagerstown","Haines City","Hallandale Beach","Hamden","Hamilton","Hampton","Hanahan","Hanover","Hanover","Hanover","Harlem","Harrisburg","Harrison","Harrison","Harrisonburg","Hartford","Hauppauge","Havelock","Haverhill","Hawthorne","Hazleton","Hell's Kitchen","Hempstead","Henderson","Henrietta","Hermitage","Herndon","Hialeah","Hialeah Gardens","Hickory","Hicksville","High Point","Highland Springs","Hillcrest Heights","Hilliard","Hillsborough","Hillside","Hillside","Hilton Head","Hilton Head Island","Hinesville","Hoboken","Holbrook","Holden","Holiday","Hollis","Holly Springs","Hollywood","Holtsville","Holyoke","Homestead","Hopatcong Hills","Hope Mills","Hopewell","Hough","Howard Beach","Huber Heights","Hudson","Hunt Valley","Huntersville","Huntington","Huntington","Huntington Station","Hunts Point","Hyattsville","Hybla Valley","Idylwood","Ilchester","Immokalee","Independence","Indian Trail","Iona","Irondequoit","Ironville","Irvington","Iselin","Islip","Ithaca","Ives Estates","Jackson","Jackson Heights","Jacksonville","Jacksonville","Jacksonville Beach","Jamaica","Jamaica Plain","Jamestown","Jasmine Estates","Jersey City","Johns Creek","Johnson City","Johnston","Johnstown","Jupiter","Kannapolis","Kearny","Keene","Kendale Lakes","Kendall","Kendall West","Kenmore","Kennesaw","Kensington","Kent","Kernersville","Kettering","Kew Gardens","Kew Gardens Hills","Key West","Keystone","Killingly Center","King of Prussia","Kings Bridge","Kings Park","Kingsland","Kingsport","Kingston","Kinston","Kiryas Joel","Kissimmee","Knoxville","LaGrange","Lackawanna","Laconia","Lake Butler","Lake Magdalene","Lake Mary","Lake Ridge","Lake Ronkonkoma","Lake Shore","Lake Wales","Lake Worth","Lake Worth Corridor","Lakeland","Lakeside","Lakewood","Lakewood","Lancaster","Lancaster","Land O' Lakes","Landover","Langley Park","Lanham-Seabrook","Lansdale","Largo","Latham","Lauderdale Lakes","Lauderhill","Laurel","Laurel","Laurelton","Laurinburg","Lawrence","Lawrenceville","Lealman","Lebanon","Lebanon","Ledyard","Leesburg","Leesburg","Lehigh Acres","Leisure City","Leland","Lenoir","Leominster","Levittown","Levittown","Lewiston","Lexington","Lexington","Lexington","Lexington","Lexington-Fayette","Lima","Limerick","Lincoln","Lincolnia","Linden","Lindenhurst","Lindenwold","Linton Hall","Lithia Springs","Livingston","Lochearn","Lockport","Lodi","Long Beach","Long Branch","Long Island City","Longmeadow","Lorain","Lorton","Lowell","Ludlow","Lumberton","Lutherville-Timonium","Lutz","Lynbrook","Lynchburg","Lyndhurst","Lynn","Mableton","Macon","Madison","Madison","Mahwah","Maitland","Malden","Mamaroneck","Manassas","Manassas Park","Manchester","Manchester","Manhattan","Mansfield","Mansfield","Mansfield City","Maple Heights","Maple Shade","Maplewood","Marblehead","Marco Island","Margate","Marietta","Mariners Harbor","Marion","Marlboro","Marlborough","Martinez","Martinsburg","Maryland City","Marysville","Maryville","Mason","Maspeth","Massapequa","Massapequa Park","Massillon","Mastic","Matthews","Mauldin","Mayfield Heights","McDonough","McKeesport","McLean","Meadow Woods","Meadowbrook","Meads","Mechanicsville","Medford","Medford","Medina","Melbourne","Melrose","Melrose","Melville","Mentor","Mercerville-Hamilton Square","Meriden","Merrick","Merrifield","Merrimack","Merritt Island","Methuen","Miami","Miami Beach","Miami Gardens","Miami Lakes","Miamisburg","Middle River","Middle Village","Middleborough","Middleburg Heights","Middletown","Middletown","Middletown","Middletown","Middletown","Middletown","Milford","Milford","Milford Mill","Milledgeville","Millville","Milton","Milton","Mineola","Mint Hill","Miramar","Monroe","Monroeville","Monsey","Montclair","Montclair","Montgomery Village","Montville Center","Mooresville","Morganton","Morgantown","Morningside Heights","Morris Heights","Morrisania","Morristown","Morristown","Morrisville","Mott Haven","Mount Laurel","Mount Lebanon","Mount Pleasant","Mount Vernon","Mount Vernon","Murrysville","Myrtle Beach","Nanuet","Naples","Narragansett","Nashua","Natick","Naugatuck","Needham","New Bedford","New Bern","New Britain","New Brunswick","New Canaan","New Castle","New City","New Haven","New London","New Milford","New Philadelphia","New Port Richey","New Rochelle","New Smyrna Beach","New Springville","New York City","Newark","Newark","Newark","Newburgh","Newburyport","Newington","Newnan","Newport","Newport","Newport News","Newton","Niagara Falls","Nicetown-Tioga","Nicholasville","Niles","Norcross","Norfolk","Norland","Norristown","North Amityville","North Andover","North Arlington","North Atlanta","North Attleborough Center","North Augusta","North Babylon","North Bay Shore","North Bel Air","North Bellmore","North Bergen","North Bethesda","North Canton","North Charleston","North Chicopee","North Decatur","North Druid Hills","North Fort Myers","North Haven","North Kingstown","North Lauderdale","North Massapequa","North Miami","North Miami Beach","North Myrtle Beach","North Olmsted","North Plainfield","North Port","North Potomac","North Providence","North Ridgeville","North Royalton","North Stamford","North Tonawanda","North Valley Stream","Northampton","Northdale","Norton","Norwalk","Norwalk","Norwich","Norwood","Norwood","Nutley","Oak Hill","Oak Ridge","Oak Ridge","Oakland Park","Oakleaf Plantation","Oakton","Ocala","Ocean Acres","Oceanside","Ocoee","Odenton","Ojus","Old Bridge","Olney","Opa-locka","Orange","Oregon","Orlando","Ormond Beach","Ossining","Oswego","Oviedo","Owings Mills","Oxford","Oxon Hill","Oxon Hill-Glassmanor","Ozone Park","Painesville","Palisades Park","Palm Bay","Palm Beach Gardens","Palm City","Palm Coast","Palm Harbor","Palm River-Clair Mel","Palm Springs","Palm Valley","Palmer","Palmetto Bay","Paramus","Park Slope","Parkchester","Parkersburg","Parkland","Parkville","Parma","Parma Heights","Parole","Parsippany","Pasadena","Passaic","Pataskala","Paterson","Pawtucket","Peabody","Peachtree City","Peachtree Corners","Pearl River","Peekskill","Pembroke Pines","Penn Hills","Pennsauken","Pennsport","Perry","Perry Hall","Perrysburg","Perth Amboy","Petersburg","Phenix City","Philadelphia","Phoenixville","Pickerington","Pikesville","Pine Hills","Pinecrest","Pinehurst","Pinellas Park","Pinewood","Piqua","Piscataway","Pittsburgh","Pittsfield","Plainfield","Plainfield","Plainview","Plainville","Plant City","Plantation","Plattsburgh","Pleasantville","Plum","Poinciana","Point Pleasant","Pompano Beach","Ponte Vedra Beach","Pooler","Port Charlotte","Port Chester","Port Orange","Port Richmond","Port Saint Lucie","Port Washington","Portland","Portsmouth","Portsmouth","Portsmouth","Portsmouth","Portsmouth Heights","Potomac","Pottstown","Poughkeepsie","Princeton","Princeton","Providence","Punta Gorda","Punta Gorda Isles","Queens","Queens Village","Queensbury","Quincy","Radcliff","Radford","Radnor","Rahway","Raleigh","Ramsey","Randallstown","Randolph","Randolph","Reading","Reading","Redan","Redland","Rego Park","Reisterstown","Reston","Revere","Reynoldsburg","Richmond","Richmond","Richmond Hill","Richmond West","Ridgewood","Ridgewood","Riverdale","Riverside","Riverview","Riviera Beach","Roanoke","Roanoke Rapids","Rochester","Rochester","Rock Hill","Rockland","Rockledge","Rockville","Rockville Centre","Rocky Mount","Rocky River","Rome","Rome","Ronkonkoma","Roosevelt","Rose Hill","Rosedale","Rosedale","Roselle","Rossville","Rossville","Roswell","Rotterdam","Royal Palm Beach","Ruskin","Rutherford","Rutland","Rye","Saco","Safety Harbor","Saint Andrews","Saint Charles","Saint Cloud","Salem","Salem","Salem","Salisbury","Salisbury","San Carlos Park","Sandalfoot Cove","Sandusky","Sandy Springs","Sanford","Sanford","Sanford","Sarasota","Saratoga Springs","Saugus","Savannah","Sayreville","Sayreville Junction","Sayville","Scaggsville","Scarsdale","Schenectady","Scotch Plains","Scranton","Seabrook","Seaford","Sebastian","Secaucus","Selden","Seminole","Setauket-East Setauket","Seven Oaks","Severn","Severna Park","Sevierville","Sewell","Seymour","Shaker Heights","Shaw","Sheepshead Bay","Shelby","Shelbyville","Shelton","Shirley","Short Pump","Shrewsbury","Sicklerville","Sidney","Silver Spring","Simpsonville","Smithfield","Smithtown","Smyrna","Snellville","Socastee","Solon","Somerset","Somerset","Somerville","South Bel Air","South Boston","South Bradenton","South Burlington","South Euclid","South Gate","South Hadley","South Kingstown","South Laurel","South Miami Heights","South Old Bridge","South Orange","South Ozone Park","South Peabody","South Plainfield","South Portland","South Portland Gardens","South Riding","South River","South Suffolk","South Vineland","South Windsor","Southbridge","Southbury","Southchase","Sparta","Spartanburg","Spring Hill","Spring Valley","Springboro","Springfield","Springfield","Springfield","Springfield","Springfield Gardens","St. Charles","St. Johns","St. Marys","St. Petersburg","Stallings","Stamford","State College","Staten Island","Statesboro","Statesville","Staunton","Sterling","Steubenville","Stockbridge","Stonecrest","Stoneham","Storrs","Stoughton","Stow","Stratford","Streetsboro","Strongsville","Stuart","Sudbury","Sudley","Suffolk","Sugar Hill","Suitland","Suitland-Silver Hill","Summerville","Summit","Sumter","Sun City Center","Sunny Isles Beach","Sunnyside","Sunrise","Sunset","Sunset Park","Suwanee","Swansea","Sweetwater","Sylvania","Syosset","Syracuse","Takoma Park","Tallahassee","Tallmadge","Tamarac","Tamiami","Tampa","Tarpon Springs","Taunton","Tavares","Taylors","Teaneck","Temple Terrace","Terrace Heights","Tewksbury","The Acreage","The Bronx","The Crossings","The Hammocks","The Villages","Thomasville","Thomasville","Three Lakes","Throgs Neck","Tiffin","Tifton","Tinton Falls","Titusville","Toledo","Toms River","Torrington","Town 'n' Country","Towson","Tremont","Trenton","Trotwood","Troy","Troy","Trumbull","Tuckahoe","Tucker","Twinsburg","Tysons Corner","Union","Union City","Union City","Uniondale","Unionport","University","University Heights","University Park","Upper Arlington","Upper Saint Clair","Utica","Valdosta","Valley Stream","Valrico","Van Nest","Vandalia","Venice","Vero Beach","Vero Beach South","Vestal","Vienna","Vincentown","Vineland","Virginia Beach","Voorhees","Wade Hampton","Wadsworth","Wake Forest","Wakefield","Wakefield","Waldorf","Wallingford","Wallingford Center","Waltham","Wantagh","Warner Robins","Warren","Warren Township","Warwick","Washington","Washington Heights","Waterbury","Waterford","Watertown","Watertown","Waterville","Wayne","Wayne","Waynesboro","Weirton","Weirton Heights","Wekiwa Springs","Wellesley","Wellington","Wesley Chapel","West Albany","West Babylon","West Chester","West Columbia","West Elkridge","West Falls Church","West Hartford","West Haven","West Hempstead","West Hollywood","West Islip","West Little River","West Lynchburg","West Melbourne","West Mifflin","West Milford","West New York","West Orange","West Palm Beach","West Park","West Raleigh","West Scarborough","West Seneca","West Springfield","West Springfield","West Torrington","West Warwick","West and East Lealman","Westbrook","Westbury","Westchase","Westchester","Westerly","Westerville","Westfield","Westfield","Westford","Westlake","Westminster","Weston","Westport","Wethersfield","Weymouth","Wharton","Wheaton","Wheeling","White Oak","White Oak","White Plains","Whitehall","Whitehall Township","Whitestone","Whitman","Wilkes-Barre","Wilkinsburg","Williamsburg","Williamsburg","Williamsport","Williamstown","Willimantic","Willingboro","Willoughby","Willow Grove","Wilmington","Wilmington","Wilmington","Wilmington Island","Wilson","Wilton","Winchester","Winchester","Winchester","Winder","Windham","Windsor","Winston-Salem","Winter Garden","Winter Haven","Winter Park","Winter Springs","Winthrop","Woburn","Wolcott","Wolf Trap","Woodbridge","Woodhaven","Woodlawn","Woodlawn","Woodmere","Woodrow","Woodside","Woodstock","Woonsocket","Wooster","Worcester","Wyckoff","Xenia","Yarmouth","Yonkers","York","Youngstown","Zanesville"]} jo-1.9/tests/jo-logo.png000066400000000000000000000176221433077231700152300ustar00rootroot00000000000000PNG  IHDR,,y}usRGBLIDATx \E}2d/$d@@!`)rTrE DA q *k&Ȓ >I&dzҙ:}Oי~yt9TթS%B Y1Ν!ȎqSRMMU/PaႥnuQGN(HU:f>fu?v<'U1T to: A `eq1be=! *BzN̂ Y@7,߸ C! &8X>7P@~+: O/kd#:zl\q^QD `B@?c*Xt Me00!`P" ` 7` `B2D@ n@eB8,+܀ ˄q + XV#  %@V@pF@&,Jā `YL X&XA @L(e00!`P" ` 7` `B2D@ n@eB8,+܀ ˄q + XV#  %@V@pF@&,Jā `YL X&XA @L(e00!`P" ` 7` `B2D@ n@eB8,+܀ ˄q + XV#  %@V@pF@&,Jā `YL X&XA @L(e00!`P" ` 7` `B2D@ n@eB8,+܀ ˄q + XV#  %@V@pF@&,Jā `YL X&XA @L(e00!`P" ` 7` `B2D@ n@eB8,+܀ ˄q + XV#  %@V@pF@&,Jā `YL X&XA @L(e00!`P" ` 7` `B2D@ n@eB8,+܀ ˄q + XV#  %@V@pF@&,Jā `YL X&XA @L(e00!`P" ` 7` `B2D@ n@eB8,+܀ ˄q + XV#  %@V@pF@&,Jā `YL X&XA @L(e00!c8سYNrFݫ*J]Ƃo>LX( 901_t&ysu}J;WV:52CCoԯcR\t"X@XZAZZN|m,}qP\i'nihNu۳5f,i\:Ugv{n 0)!y&QUt<{ef: W.::ѢU.)֥nύ+w}m@?,QH|䩔"g^']ھ%7'gRn -_.ߗG޹UP&$fؗMqNb`G=!`Y o i/-ǿ#Fm,( 9P&onHưRSJeꤢ%Tu [ݭ7b-:n/2Ne 䕮8f!;"c!/'K1"_뛫䩗} tZ)Y8bu׭GT ݱU #>;vX~ =UR#{+{=\Yft:G ]ת~),P\%Xs?Waؙ/Ax$m] Ϗ[ ##~TGE Ǐ70?[n_TYƄQY` A5mL<1Tɠ}K;O3tX?a[ e2~9KgGt!U+ wnuJO"oã**1:G6vxdG 'Jpzvz,&Ej5̻|>k5j.5q"W9Tpw)cg #u I5lC;3l'mFtՊ`e,2IdW!=yz_3j9,O ɶ]>"G ;TkW;?9./Thٷ':5wDG,u?qakj9q0[CuM$sգAQ@gvHe VG:[NŹmWS"r&Ew1zPz+e'B}MM,?"::;z󑚃uuO}.b&1\_Eu7 |F|CiQ \I?`uӄY}Cj)mlK+Y#j]յ)P.zl\V5\(o^A򇟬rku5wz(jwHJ@|tߪfd^z&K{nu)m< X31%.~F~DCOӏ&8s" j.!s$u@46&}Gʤǎ/x0")MFYyj2Իj ~ɲř}bGVs7z~>t6nuՅ}OFȅ-3z^jrqrڠaŢo`jy\LKH[$ѡOn"yoiUR3"VyK/+}Y7z͎]~2Po\.LD~|syiTUMh3M6JOkulW:,;++VoXiyzB70eN [NuLˀ7jꝫyG[Md9 StU"wzzM>%_:eOyn1oŕ4o 9l8ޅj$MzG Cz+ճXb8^ANw֭Z&Skכx6q͟,-VW2rFg,5B1x,V;8 }x+߭s4ײכjz>85+)47Wkg;E?el=^q2L;+.E9A=ln7jeNc&ee A ?'=ϭ/w?3hm(1CsѴ`K3D,ufR++X [U\-v>mp6@k; 2ae^˒|rCzF>2q G%+/+gT',8V3'*Z:ލ#]04 slV.+ul9l;M?D^|`T{%*ղzF&df 7xcDdxB?)眔9QJ cӎwds"?w,w|c*A* K|姂,?y˥;ECs8t7pL1,k- ,\?;ۿF~ဝ&`Fַ]VQF)/yC1,o86^\`SAVAH(KVh}MMkmwKCWlxzO9Sχ6 7!R{9,H=ZX)d\*zqxXmHݸK>G?C8ҶS}/&c)؜XN)٪wgd֝[,'qę'+913/uasSf3:lO/8WpNBO[zU?}X k+u++O cayL#`eǩ/|L0F4S_ca:2i1td+v6]oZY$q5e/oK<R5M]Y]"3f,RvɴvK̜^f֗g!@|>\팣 /3֨§{\G#dh5/?/[yPj !w+d^ *MηOѓYfV003X][O׫ϜN#6?"_ع\#Z>X zѣȼ/ӏ+G.yku4+,`7nֹwedf Q>6ڸnUjsǟ- 5KD;Pnj9qrq|e:YēM;u-%i{h}R+.B-VwX; ]TF5dju5QCbKU++꽭?r+Vow?>tS h(ܷx7c7V5/^R TP_JۯO\nS콰2#@ +1n֧҂]G 7}=szY)*zl٢AF;ZZ6 G+ ]?m /SwuqΠ[Iܸ\542<y3$8/@}lntVpOox+l1ۛ$ ֒ ˄ C5Uqp(T5# 7}8*+ʆ֨K?}A$P%)'FT+ol$GtP1aCư+Gw]-~iq|69ٽW  \Yp- gg/ d6?yM8;SxdRy=;͍2-? ?[&Rغs.Ks7o _`\r?ѽ}ѭdBybzLܷZXR좮 _}~ti?)M'#SJ5aoQ-=`x ZM_;E;~\="ͻS jneZ%ٲ*޲`.Y=_VV8[x]{oLyi |/l&>-UqAvEš_wYs2eqj=ZdB;a[r'-z@\t׷%EyޡuRUN,R5B gG'=!VO-Q/=j]dɔ'7\qzU׭# q:TŅxUV{*t*L} Uk|_~KuRQ;d3>J>Zd4?!e?j$L +)A-jZwtW' ڧ1xgIJZFu3]F5J:@*(鳊 `yA9e1!ʨYRleNydc#zOsdj] p qMr Ηfrw*˙߁|f"= xJ7An Xn)n @#- )S!`GZ@S) pCrC,OqS i!O X0@  =B@"} {"foo":"bar","baz":""} jo-1.9/tests/jo.08.sh000066400000000000000000000001741433077231700143400ustar00rootroot00000000000000# data from file: text ${JO:-jo} program="jo" authors=@${srcdir:=.}/tests/jo-creator.txt ${JO:-jo} foo="bar" baz=@/dev/null jo-1.9/tests/jo.09.exp000066400000000000000000000001121433077231700145130ustar00rootroot00000000000000{"program":"jo","authors":"SmFuLVBpZXQgTWVucyA8anBtZW5zQGdtYWlsLmNvbT4K"} jo-1.9/tests/jo.09.sh000066400000000000000000000001431433077231700143350ustar00rootroot00000000000000# data from file: base64-encoded ${JO:-jo} program="jo" authors=%${srcdir:=.}/tests/jo-creator.txt jo-1.9/tests/jo.10.exp000066400000000000000000000000461433077231700145110ustar00rootroot00000000000000["spring","summer","autumn","winter"] jo-1.9/tests/jo.10.sh000066400000000000000000000000711433077231700143250ustar00rootroot00000000000000# array: simple ${JO:-jo} -a spring summer autumn winter jo-1.9/tests/jo.11.exp000066400000000000000000000000641433077231700145120ustar00rootroot00000000000000[true,false,null,"\"true\"","\"false\"","\"null\""] jo-1.9/tests/jo.11.sh000066400000000000000000000001461433077231700143310ustar00rootroot00000000000000# array: true,false,null (native and string) ${JO:-jo} -a true false null '"true"' '"false"' '"null"' jo-1.9/tests/jo.12.exp000066400000000000000000000002201433077231700145050ustar00rootroot00000000000000{ "type": "location", "cog": 120, "t": "u", "lat": 48.85833, "lon": 2.29513, "acc": 5, "tid": "JJ", "tst": 1457767154 } jo-1.9/tests/jo.12.sh000066400000000000000000000001521433077231700143270ustar00rootroot00000000000000# object: geo ${JO:-jo} -p type=location cog=120 t=u lat=48.85833 lon=2.29513 acc=5 tid=JJ tst=1457767154 jo-1.9/tests/jo.13.exp000066400000000000000000000253451433077231700145250ustar00rootroot00000000000000{ "name": "This is jo", "px": [ 300, 300 ], "face": "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", "meta": { "width": 300, "height": 300, "bytes": 8082, "type": "png", "creator": "JP" } } jo-1.9/tests/jo.13.sh000066400000000000000000000003041433077231700143270ustar00rootroot00000000000000# object: face card jo logo ${JO:-jo} -p name="This is jo" px[]=300 face=%${srcdir:=.}/tests/jo-logo.png meta[width]=300 px[]=300 meta[height]=300 meta[bytes]=8082 meta[type]=png meta[creator]=JP jo-1.9/tests/jo.14.exp000066400000000000000000000000371433077231700145150ustar00rootroot00000000000000{"form":"=ok","this":"==sure"} jo-1.9/tests/jo.14.sh000066400000000000000000000001031433077231700143250ustar00rootroot00000000000000# values: with equals signs in them ${JO:-jo} form==ok this===sure jo-1.9/tests/jo.15.exp000066400000000000000000000000511433077231700145120ustar00rootroot00000000000000{"name":"Jane","obj":["eX","whY","Zed"]} jo-1.9/tests/jo.15.sh000066400000000000000000000002041433077231700143300ustar00rootroot00000000000000# object from file tmp=/tmp/jo.$$ trap "rm -f $tmp; exit" 0 1 2 15 ${JO:-jo} -a eX whY Zed > $tmp ${JO:-jo} name=Jane obj:=$tmp jo-1.9/tests/jo.16.exp000066400000000000000000000000441433077231700145150ustar00rootroot00000000000000{"msg":"\"All's Well\", she said."} jo-1.9/tests/jo.16.sh000066400000000000000000000001611433077231700143330ustar00rootroot00000000000000# quotes in quotes tmp=/tmp/jo.$$ trap "rm -f $tmp; exit" 0 1 2 15 ${JO:-jo} msg='"All'\''s Well", she said.' jo-1.9/tests/jo.17.exp000066400000000000000000000015221433077231700145200ustar00rootroot00000000000000{"s":"","n":0,"b":false,"a":null} {"s":"string","n":6,"b":true,"a":"string"} {"s":"\"quoted\"","n":8,"b":true,"a":"\"quoted\""} {"s":"12345","n":12345,"b":true,"a":12345} {"s":"true","n":1,"b":true,"a":true} {"s":"false","n":0,"b":false,"a":false} {"s":"","n":0,"b":false,"a":null} ["123",14,true,456] ["-s",2,true] ["--test","--toast"] {"--test":"--toast"} [true,"--toast","--test","--toast",6,"--toast"] {"s":false,"n":false,"b":false,"a":false} {"s":true,"n":true,"b":true,"a":true} {"s":"Jan-Piet Mens ","n":"Jan-Piet Mens ","b":"Jan-Piet Mens ","a":"Jan-Piet Mens "} {"s":"SmFuLVBpZXQgTWVucyA8anBtZW5zQGdtYWlsLmNvbT4K","n":"SmFuLVBpZXQgTWVucyA8anBtZW5zQGdtYWlsLmNvbT4K","b":"SmFuLVBpZXQgTWVucyA8anBtZW5zQGdtYWlsLmNvbT4K","a":"SmFuLVBpZXQgTWVucyA8anBtZW5zQGdtYWlsLmNvbT4K"} jo-1.9/tests/jo.17.sh000066400000000000000000000021131433077231700143330ustar00rootroot00000000000000# type coercion # coerce key=val for v in "" string \"quoted\" 12345 true false null; do ${JO:-jo} -- -s s="$v" -n n="$v" -b b="$v" a="$v" done # coerce array items ${JO:-jo} -a -- -s 123 -n "This is a test" -b C_Rocks 456 # coercion flag strings should be usable as inputs, when they aren't flags ${JO:-jo} -a -- -s -s -n -n -b -b # non-flag strings should be read as normal strings, even if they begin with "-" ${JO:-jo} -a -- --test --toast ${JO:-jo} -- --test=--toast # coercion is one-shot, so all "--toast" strings are normal input ${JO:-jo} -a -- -b --test --toast -s --test --toast -n --test --toast ### These should NOT be coerced # @ booleans for v in 0 1; do ${JO:-jo} -- -s s@"$v" -n n@"$v" -b b@"$v" a@"$v" done # @/% file inclusions ${JO:-jo} -- -s s=@${srcdir:=.}/tests/jo-creator.txt -n n=@${srcdir:=.}/tests/jo-creator.txt -b b=@${srcdir:=.}/tests/jo-creator.txt a=@${srcdir:=.}/tests/jo-creator.txt ${JO:-jo} -- -s s=%${srcdir:=.}/tests/jo-creator.txt -n n=%${srcdir:=.}/tests/jo-creator.txt -b b=%${srcdir:=.}/tests/jo-creator.txt a=%${srcdir:=.}/tests/jo-creator.txt jo-1.9/tests/jo.18.exp000066400000000000000000000003241433077231700145200ustar00rootroot00000000000000{"a.b":0,"a.c.d":1,"a.d.e":[2,"sam"],"a.c":{"f":true},"b.e":["hi"]} {"a":{"b":0,"c":{"d":1,"f":true},"d":{"e":[2,"sam"]}},"b":{"e":["hi"]}} {"a":{"b":0,"c":{"d":1,"f":true},"d":{"e":[2,"sam"]}},"b":{"e":["hi"]}} jo-1.9/tests/jo.18.sh000066400000000000000000000005271433077231700143430ustar00rootroot00000000000000# nested objects with user-specified delimiter # without delimiter ${JO:-jo} a.b=0 a.c.d=1 a.d.e[]=2 a.d.e[]=sam a.c[f]@1 b.e[]g=hi # with delimiter ${JO:-jo} -d. a.b=0 a.c.d=1 a.d.e[]=2 a.d.e[]=sam a.c[f]@1 b.e[]g=hi # with more complex delimiter ${JO:-jo} -d\|first_char_only a\|b=0 a\|c\|d=1 a\|d\|e[]=2 a\|d\|e[]=sam a\|c[f]@1 b\|e[]g=hi jo-1.9/tests/jo.19.exp000066400000000000000000000000301433077231700145130ustar00rootroot00000000000000{"foo":["hello world"]} jo-1.9/tests/jo.19.sh000066400000000000000000000000741433077231700143410ustar00rootroot00000000000000# read from pipe echo '["hello world"]' | ${JO:-jo} foo:=- jo-1.9/tests/jo.20.exp000066400000000000000000002232621433077231700145210ustar00rootroot00000000000000[{"a":1,"b":"val"},{"a":478,"b":"other"}] [{"timezone":"Asia/Kolkata","cities":["Abhayāpuri","Abohar","Abrama","Achalpur","Achhnera","Adampur","Addanki","Adirampattinam","Adra","Adūr","Afzalgarh","Afzalpur","Agar","Agartala","Agra","Ahmadnagar","Ahmadpur","Ahmedabad","Ahraura","Airoli","Aistala","Aizawl","Ajmer","Ajnāla","Ajodhya","Ajra","Akalkot","Akaltara","Akbarpur","Akbarpur","Akivīdu","Akkarampalle","Aklera","Akola","Akot","Aland","Alandi","Alandur","Alangad","Alangāyam","Alappuzha","Allahābād","Almora","Alnāvar","Along","Alot","Aluva","Alwar","Alwaye","Alībāg","Alīganj","Alīgarh","Alīpur","Alīpur Duār","Amalner","Amalāpuram","Amaravati","Amarnāth","Amarpur","Amarpātan","Ambad","Ambasamudram","Ambattūr","Ambikāpur","Ambur","Ambāh","Ambājogāi","Ambāla","Amet","Amla","Amod","Amreli","Amritsar","Amroha","Amroli","Amrāvati","Amudālavalasa","Anakāpalle","Anamalais","Anand","Anandpur","Anantapur","Anantnag","Andol","Anekal","Angamāli","Angul","Anjad","Anjangaon","Anjār","Ankleshwar","Annigeri","Annur","Anshing","Anta","Anthiyur","Anūpgarh","Anūppur","Anūpshahr","Aonla","Arakkonam","Arang","Arantāngi","Arcot","Ariyalūr","Arkalgūd","Arni","Aroor","Arrah","Arsikere","Arukutti","Arumuganeri","Aruppukkottai","Arāmbāgh","Arāria","Ashoknagar","Ashoknagar Kalyangarh","Ashta","Ashta","Asifābād","Atarra","Athni","Atmakūr","Atraulī","Attili","Attingal","Attur","Auraiya","Aurangabad","Aurangābād","Aurād","Ausa","Avanigadda","Avinashi","Ayakudi","Azamgarh","Azhiyūr","Baberu","Babrāla","Babīna","Bachhraon","Bada Barabīl","Badagara","Baddi","Badlapur","Badnāwar","Badvel","Badūria","Bagaha","Bagaha","Bagalkot","Bagar","Bagasra","Bagdogra","Bagulā","Baharampur","Baheri","Bahjoi","Bahraigh","Bahula","Bahādurganj","Bahādurgarh","Baidyabāti","Baihar","Bail-Hongal","Bairāgnia","Bakhtiyārpur","Balarāmpur","Balasore","Ballālpur","Balod","Baloda Bāzār","Balrāmpur","Balāngīr","Banat","Banda","Bandipura","Banga","Banganapalle","Bangaon","Bangaon","Bangarapet","Bankra","Banmankhi","Bannūr","Bantvāl","Banūr","Bar Bigha","Bara Uchāna","Baranagar","Barauli","Baraut","Barbil","Barddhamān","Bareilly","Bargarh","Bargi","Barhiya","Bari Sādri","Barjala","Barki Saria","Barkā Kānā","Barnāla","Barpeta","Barpeta Road","Barpāli","Baruipur","Barwāla","Barwāni","Basavakalyān","Basavana Bāgevādi","Basi","Basi","Basi","Basmat","Basni","Bastī","Baswa","Bathinda","Batāla","Baud","Bawāna","Bayāna","Bedi","Begamganj","Begusarai","Begūn","Behat","Behror","Bela","Beldānga","Belgaum","Bellampalli","Bellary","Belonia","Belsand","Belūr","Bemetāra","Bengaluru","Beohāri","Berasia","Beri Khās","Betamcherla","Bettiah","Betūl","Bewar","Beypore","Beāwar","Bhabhua","Bhachāu","Bhadaur","Bhadohi","Bhadrakh","Bhadreswar","Bhadrāchalam","Bhadrāvati","Bhainsdehi","Bhaisa","Bhandāra","Bhanjanagar","Bharatpur","Bharthana","Bharwāri","Bharūch","Bhasāwar","Bhatkal","Bhattiprolu","Bhavnagar","Bhavāni","Bhawanipur","Bhawāni Mandi","Bhawānipatna","Bhawānīgarh","Bhayandar","Bhikangaon","Bhilai","Bhind","Bhindār","Bhinga","Bhitarwār","Bhiwadi","Bhiwandi","Bhiwāni","Bhogpur","Bhongaon","Bhongīr","Bhopāl","Bhor","Bhuban","Bhubaneshwar","Bhudgaon","Bhuj","Bhusāval","Bhādra","Bhādāsar","Bhāgalpur","Bhālki","Bhānder","Bhānpura","Bhānpurī","Bhānvad","Bhātpāra","Bhātāpāra","Bhāyāvadar","Bhīkhi","Bhīlwāra","Bhīmavaram","Bhīmunipatnam","Bhīnmāl","Bhūm","Biaora","Bidhūna","Bihār Sharīf","Bihārīganj","Bijapur","Bijbehara","Bijnor","Bijāwar","Bikramganj","Bilgi","Bilgrām","Bilhaur","Bilimora","Bilsanda","Bilsi","Bilthra","Bilāra","Bilāri","Bilāsipāra","Bilāspur","Bindki","Binka","Birmitrapur","Birpara","Birūr","Bisauli","Bishnupur","Bissāu","Biswān","Bobbili","Bodhan","Bodināyakkanūr","Boisar","Bokajān","Bokāro","Bolpur","Bongaigaon","Borivli","Borsad","Botād","Brahmapur","Brājarājnagar","Budaun","Buddh Gaya","Budge Budge","Budhlāda","Budhāna","Bulandshahr","Buldāna","Burhar","Burhānpur","Burla","Buxar","Byndoor","Byādgi","Bābai","Bābra","Bādāmi","Bāgepalli","Bāgha Purāna","Bāghpat","Bāh","Bāli","Bāli","Bālotra","Bālugaon","Bālurghāt","Bālāchor","Bālāghāt","Bālāpur","Bāmor Kalān","Bānapur","Bānda","Bāndīkūi","Bāngarmau","Bānka","Bānki","Bānkura","Bānsbāria","Bānsdīh","Bānsi","Bānswāda","Bānswāra","Bāntva","Bāpatla","Bāramūla","Bārdoli","Bārh","Bāri","Bārmer","Bārsi","Bāruni","Bārākpur","Bārāmati","Bārān","Bārāsat","Bāsoda","Bāsudebpur","Bāzpur","Bīdar","Bīkaner","Bīlāspur","Bīrpur","Bīsalpur","Būndi","Būndu","Calangute","Canning","Chakapara","Chaklāsi","Chakradharpur","Chaksu","Challakere","Challapalle","Chalāla","Chamba","Chamrajnagar","Chandannagar","Chandauli","Chanderi","Chandrakona","Chanduasi","Chandīgarh","Changanācheri","Channagiri","Channapatna","Channarāyapatna","Charkhi Dādri","Charkhāri","Charthāwal","Chatrapur","Chatrā","Chemmumiahpet","Chengalpattu","Chengam","Chengannūr","Chennai","Chennimalai","Cherpulassery","Cherthala","Chetput","Chettipālaiyam","Chetwayi","Cheyyar","Chhabra","Chhala","Chhaprauli","Chharra","Chhatarpur","Chhibrāmau","Chhindwāra","Chhota Udepur","Chhoti Sādri","Chhāpar","Chhāta","Chhātāpur","Chicholi","Chidambaram","Chidawa","Chik Ballāpur","Chikhli","Chikmagalūr","Chiknāyakanhalli","Chikodi","Chilakalūrupet","Chillupār","Chincholi","Chinna Salem","Chinnachowk","Chinnamanūr","Chintāmani","Chiplūn","Chirmiri","Chitradurga","Chittaranjan","Chittaurgarh","Chittūr","Chodavaram","Chopda","Chotila","Chunār","Churāchāndpur","Chākan","Chākia","Chālisgaon","Chāmpa","Chānasma","Chānda","Chāndor","Chāndpur","Chāndur","Chāndūr","Chāndūr Bāzār","Chāpar","Chāpra","Chās","Chāībāsa","Chīpurupalle","Chīrāla","Chītāpur","Chūru","Clement Town","Closepet","Cochin","Coimbatore","Colachel","Colgong","Colonelganj","Contai","Coondapoor","Cuddalore","Cuddapah","Cumbum","Cumbum","Cuncolim","Curchorem","Cuttack","Dabhoi","Daboh","Dabra","Dabwāli","Dahegām","Dalkola","Dalsingh Sarai","Daltonganj","Dam Dam","Damoh","Damān","Dandeli","Darbhanga","Darsi","Daryāpur","Dasnapur","Dasūya","Datia","Dattāpur","Daudnagar","Daund","Dausa","Davangere","Dehra Dūn","Dehri","Delhi","Denkanikota","Deoband","Deogarh","Deoghar","Deoli","Deoli","Deoli","Deolāli","Deoraniān","Deori Khās","Deoria","Depālpur","Deshnoke","Devakottai","Devanhalli","Devarkonda","Devgadh Bāriya","Devgarh","Dewas","Deūlgaon Rāja","Dhamtari","Dhanaula","Dhanaura","Dhanbad","Dhandhuka","Dhanera","Dharampur","Dharamsala","Dharangaon","Dharapuram","Dharmadam","Dharmanagar","Dharmapuri","Dharmavaram","Dharmābād","Dhaulpur","Dhaurahra","Dhekiajuli","Dhenkānāl","Dhing","Dholka","Dhone","Dhorāji","Dhrol","Dhrāngadhra","Dhuburi","Dhulagari","Dhuliān","Dhupgāri","Dhāka","Dhāmnod","Dhāmpur","Dhār","Dhāri","Dhāriwāl","Dhāruhera","Dhārūr","Dhūlia","Dhūri","Diamond Harbour","Dibai","Dibrugarh","Dicholi","Digboi","Dighwāra","Digras","Dimāpur","Dinapore","Dindigul","Dindori","Diphu","Diu","Doda","Doddaballapura","Dohad","Dombivli","Dondaicha","Dongargarh","Dornakal","Dorāha","Dubrājpur","Dugda","Duliāgaon","Dum Duma","Dumjor","Dumka","Dumra","Dumraon","Durg","Durgapur","Durgāpur","Dwārka","Dādri","Dāhānu","Dākor","Dāmnagar","Dārjiling","Dārwha","Dāsna","Dātāganj","Dīdwāna","Dīg","Dīglūr","Dīnhāta","Dīnānagar","Dīsa","Dūngarpur","Egra","Elamanchili","Ellenabad","Ellore","Elūr","Emmiganūr","Erandol","Erode","Erraguntla","Erāttupetta","Etāwa","Etāwah","Faizpur","Farakka","Faridabad","Farrukhnagar","Farrukhābād","Farīdkot","Farīdpur","Fatehganj West","Fatehgarh Chūriān","Fatehpur","Fatehpur","Fatehpur","Fatehpur Sīkri","Fatehābād","Fatehābād","Fatwa","Ferokh","Ferozepore","Forbesganj","Fort Gloster","French Rocks","Fyzābād","Fālākāta","Fāzilka","Fīrozpur Jhirka","Fīrozābād","Gadag","Gadag-Betageri","Gaddi Annaram","Gadhada","Gadhinglaj","Gadwāl","Gajendragarh","Gajraula","Gajuwaka","Gandevi","Gandhinagar","Gangoh","Gangolli","Gangtok","Gangākher","Gangānagar","Gangāpur","Gangāpur","Gangāpur","Gangārāmpur","Gangāwati","Ganj Dundwāra","Gannavaram","Garhmuktesar","Garhshankar","Garhwa","Garhākota","Gariadhar","Garui","Gauribidanur","Gauripur","Gaya","Gevrai","Gharaunda","Ghatkesar","Ghazīpur","Ghosī","Ghoti Budrukh","Ghugus","Ghātampur","Ghātanji","Ghātsīla","Ghātāl","Ghāziābād","Giddalūr","Giddarbāha","Gingee","Gobichettipalayam","Gobindpur","Gobārdānga","Godda","Godhra","Gohadi","Gohāna","Gokak","Gokarna","Gokavaram","Gola Gokarannāth","Golāghāt","Gomoh","Gondal","Gondiā","Gondā City","Gopālganj","Gorakhpur","Gorakhpur","Gorantla","Gosāba","Govardhan","Goyerkāta","Goālpāra","Greater Noida","Gubbi","Gudalur","Gudivāda","Gudiyatham","Gulbarga","Guledagudda","Gulābpura","Gulāothi","Gumia","Gumlā","Gummidipundi","Guna","Gundlupēt","Gunnaur","Guntakal Junction","Guntur","Gunupur","Gurgaon","Gurmatkāl","Gursahāiganj","Gursarāi","Guru Har Sahāi","Guruvāyūr","Guskhara","Guwahati","Gwalior","Gyānpur","Gādarwāra","Gāndarbal","Gāndhīdhām","Gīrīdīh","Gūduvāncheri","Gūdūr","Hadagalli","Hadgāon","Hailākāndi","Haldaur","Haldia","Haldwani","Haliyal","Halvad","Hamīrpur","Hamīrpur","Handiā","Hanumāngarh","Harda Khās","Hardoī","Haridwar","Harihar","Harpanahalli","Harpālpur","Harsūd","Harūr","Hasanpur","Hassan","Hastināpur","Hatta","Hazāribāgh","Hilsa","Himatnagar","Hindaun","Hindoria","Hindupur","Hinganghāt","Hingoli","Hinjilikatu","Hirekerūr","Hiriyūr","Hisar","Hisuā","Hodal","Hojāi","Holalkere","Hole Narsipur","Homnābād","Honavar","Honnāli","Hosdurga","Hoshangābād","Hoshiārpur","Hoskote","Hospet","Hosūr","Howli","Hubli","Hugli","Hukeri","Hungund","Hunsūr","Husainābād","Hyderābād","Hābra","Hāflong","Hājo","Hājīpur","Hālol","Hālīsahar","Hāngal","Hānsi","Hāora","Hāpur","Hārij","Hāsimāra","Hāthras","Hāveri","Hīrākud","Ichalkaranji","Ichchāpuram","Idappadi","Igatpuri","Ilkal","Imphāl","Indergarh","Indi","Indore","Indri","Indāpur","Ingrāj Bāzār","Injambakkam","Iringal","Irinjālakuda","Irugūr","Islāmnagar","Islāmpur","Islāmpur","Itimādpur","Itānagar","Itārsi","Jabalpur","Jagalūr","Jagatsinghapur","Jagdalpur","Jagdīshpur","Jagdīspur","Jaggayyapeta","Jagraon","Jagtiāl","Jagādhri","Jahāngīrābād","Jahānābād","Jahāzpur","Jaigaon","Jainagar","Jaipur","Jais","Jaisalmer","Jaisingpur","Jaito","Jaitāran","Jalalpore","Jalandhar","Jalesar","Jaleshwar","Jalgaon","Jalgaon Jamod","Jalor","Jalpāiguri","Jalālpur","Jalālābad","Jalālābād","Jalālābād","Jalālī","Jalārpet","Jambusar","Jamkhandi","Jammalamadugu","Jammu","Jamnagar","Jamshedpur","Jamālpur","Jamūī","Jandiāla","Jangaon","Jangipur","Jaorā","Jarwal","Jasdan","Jashpurnagar","Jasidih","Jaspur","Jaswantnagar","Jatani","Jatāra","Jaunpur","Jayamkondacholapuram","Jaynagar Majilpur","Jetpur","Jevargi","Jewar","Jeypore","Jhajjar","Jhalidā","Jhanjhārpur","Jharia","Jharsuguda","Jhinjhāna","Jhumri Telaiya","Jhunjhunūn","Jhā-Jhā","Jhābua","Jhālrapātan","Jhālu","Jhālāwār","Jhānsi","Jhārgrām","Jhīnjhak","Jhūsi","Jintūr","Jodhpur","Jodhpur","Jodiya Bandar","Jogbani","Jora","Jorhāt","Jugsālai","Junnar","Jājpur","Jālaun","Jālna","Jāmadoba","Jāmai","Jāmtāra","Jāmuria","Jānjgīr","Jānsath","Jāwad","Jīnd","Jūnāgadh","Jūnāgarh","Kabrāi","Kachhwa","Kadakkavoor","Kadayanallur","Kadi","Kadiri","Kadod","Kadūr","Kaikalūr","Kailāras","Kailāshahar","Kaimganj","Kaimori","Kairāna","Kaithal","Kakching","Kakdwip","Kakrāla","Kalakkādu","Kalamassery","Kalamb","Kalamnūri","Kalavoor","Kalghatgi","Kallakkurichchi","Kallakurichi","Kallidaikurichi","Kalmeshwar","Kalpatta","Kalugumalai","Kalyandurg","Kalyani","Kalyān","Kalānaur","Kamalganj","Kampli","Kanchipuram","Kandukūr","Kangayam","Kanigiri","Kankauli","Kannad","Kannauj","Kanniyākumāri","Kannod","Kannur","Kanpur","Kantābānji","Kanuru","Kapadvanj","Kapūrthala","Karamsad","Karanpur","Karauli","Kareli","Karera","Karhal","Karjat","Karmāla","Karnāl","Karol Bāgh","Kartārpur","Karur","Karwar","Karād","Karīmganj","Karīmnagar","Kasba","Kashipur","Kasrāwad","Katangi","Katangi","Katghora","Kathua","Katihar","Kattanam","Kattivākkam","Kawardha","Kayalpattinam","Keelakarai","Kekri","Kemrī","Kenda","Kendrāparha","Kerūr","Keshod","Keshorai Pātan","Kesinga","Khada","Khadki","Khagaria","Khagaul","Khair","Khairābād","Khairāgarh","Khairāgarh","Khajuraho Group of Monuments","Khalīlābād","Khamaria","Khambhāliya","Khambhāt","Khammam","Khandela","Khandwa","Khanna","Kharagpur","Kharagpur","Kharakvasla","Kharar","Khardah","Khargone","Kharkhauda","Kharsia","Khatauli","Khatīma","Kheda","Khedbrahma","Khekra","Kheri","Kherālu","Khetia","Khetri","Khilchipur","Khirkiyān","Khopoli","Khowai","Khuldābād","Khunti","Khurai","Khurda","Khurja","Khāchrod","Khāmgaon","Khānāpur","Khāpa","Khārupatia","Khātegaon","Khātra","Khūtār","Kichha","Kinwat","Kirandul","Kiraoli","Kishanganj","Kishangarh","Kishtwār","Kithor","Kizhake Chālakudi","Koch Bihār","Kodaikānāl","Kodarmā","Kodoli","Kodungallūr","Kodār","Kodīnar","Koelwār","Kohīma","Kokrajhar","Kolasib","Kolhāpur","Kolkata","Kollam","Kollegāl","Kolār","Kolāras","Konch","Kondagaon","Kondapalle","Konnagar","Konnūr","Konārka","Koothanallur","Kopargaon","Koppal","Kopāganj","Koratla","Korba","Koregaon","Korwai","Korāput","Kosamba","Kosi","Kosigi","Kota","Kotagiri","Kotamangalam","Kotapārh","Kotdwāra","Kotkapura","Kotma","Kotputli","Kottagūdem","Kottayam","Kottūru","Kotā","Kovilpatti","Kovvūr","Kovūr","Koynanagar","Kozhikode","Koāth","Krishnagiri","Krishnanagar","Krishnarājpet","Kuchaiburi","Kuchera","Kuchāman","Kudachi","Kuju","Kukshi","Kulgam","Kulittalai","Kulpahār","Kulti","Kulu","Kumarapalayam","Kumbakonam","Kumbalam","Kumbhrāj","Kumhāri","Kumta","Kunda","Kundarkhi","Kundgol","Kundla","Kunigal","Kunnamangalam","Kunnamkulam","Kuppam","Kurandvād","Kurduvādi","Kurinjippādi","Kurnool","Kushtagi","Kutiatodu","Kutiyāna","Kuttampuzha","Kuzhithurai","Kyathampalle","Kāgal","Kākināda","Kākori","Kālimpong","Kāliyāganj","Kālka","Kālna","Kālol","Kālpi","Kālānwāli","Kālāvad","Kāman","Kāmthi","Kāmākhyānagar","Kāmāreddi","Kāmārhāti","Kānchrāpāra","Kāndhla","Kāndi","Kāndla","Kānke","Kānker","Kānkānhalli","Kānnangād","Kānt","Kānth","Kāpren","Kāraikkudi","Kāraikāl","Kāramadai","Kāranja","Kārkala","Kārsiyāng","Kāsaragod","Kāsganj","Kāthor","Kātol","Kātoya","Kātpādi","Kātrās","Kāvali","Kāyankulam","Kīl Bhuvanagiri","Kīratpur","Kūdligi","Kūkatpalli","Kūmher","Lachhmangarh Sīkar","Lahār","Lakhnādon","Lakhyabad","Lakhīmpur","Laksar","Lakshmeshwar","Lal Bahadur Nagar","Lalgudi","Lalitpur","Lar","Latur","Laungowāl","Leh","Leteri","Limbdi","Lingsugūr","Lohārdagā","Lonar","Lonavla","Loni","Losal","Luckeesarai","Lucknow","Ludhiāna","Lumding Railway Colony","Lunglei","Lādnūn","Lādwa","Lāharpur","Lākheri","Lālganj","Lālganj","Lālgola","Lālpur","Lālsot","Lātehār","Lāthi","Lāwar Khās","Lūnāvāda","Machhlīshahr","Machilīpatnam","Madambakkam","Madanapalle","Maddūr","Madgaon","Madhipura","Madhubani","Madhugiri","Madhupur","Madhyamgram","Madikeri","Madipakkam","Madukkarai","Madukkūr","Madurai","Madurāntakam","Maghar","Maham","Mahbūbnagar","Mahbūbābād","Mahemdāvād","Mahendragarh","Maheshtala","Maheshwar","Mahgawān","Mahiari","Mahmudābād","Mahobā","Maholi","Mahudha","Mahwah","Mahād","Mahālingpur","Mahārāganj","Mahārājgani","Mahāsamund","Mahē","Mahīshādal","Maihar","Mainpuri","Maināguri","Mairwa","Majalgaon","Makrāna","Maksi","Malakanagiri","Malappuram","Malaut","Malavalli","Malkajgiri","Malkapur","Malkāpur","Mallasamudram","Malpe","Malīhābād","Manali","Manamadurai","Manapparai","Mancherāl","Mandamarri","Mandapam","Mandapeta","Mandi","Mandideep","Mandlā","Mandsaur","Mandya","Mandāwar","Maner","Mangalagiri","Mangaldai","Mangalore","Manglaur","Mangrūl Pīr","Maniar","Manihāri","Manipal","Manjeri","Manjhanpur","Mankāchar","Manmād","Mannargudi","Mannārakkāt","Manoharpur","Manthani","Manuguru","Manāsa","Manāwar","Marakkanam","Marayur","Margherita","Marhaura","Mariāhu","Mariāni","Masaurhi Buzurg","Mathura","Mattanur","Mau","Mau","Mau Aimma","Maudaha","Mauganj","Maur","Mavoor","Mawāna","Mayiladuthurai","Mayāng Imphāl","Medak","Medinīpur","Meerut","Mehkar","Mehndāwal","Melur","Memāri","Mendarda","Merta","Mettupalayam","Mettur","Mhāsvād","Mihona","Milak","Miriālgūda","Mirzāpur","Misrikh","Modāsa","Moga","Mohali","Moirāng","Mokameh","Mokokchūng","Mon","Monghyr","Monoharpur","Moram","Morbi","Morena","Morigaon","Morinda","Mormugao","Morsi","Morwa","Morādābād","Morār","Mothīhāri","Mubarakpur","Muddebihāl","Mudgal","Mudhol","Mudkhed","Mughal Sarāi","Muhammadābād","Muhammadābād","Muhammadābād","Mukeriān","Mukher","Muktsar","Mulbāgal","Mulgund","Multai","Mulugu","Muluppilagadu","Mumbai","Mundargi","Mundgod","Mundra","Mungaoli","Mungeli","Munnar","Murbād","Murlīganj","Murshidābād","Murtajāpur","Murudeshwara","Murwāra","Murādnagar","Mushābani","Musiri","Mussoorie","Muttupet","Muvattupuzha","Muzaffarnagar","Muzaffarpur","Mysore","Mācherla","Māchhīwāra","Māgadi","Mākum","Mālegaon","Māler Kotla","Mālpura","Mālvan","Mālūr","Māndal","Māndalgarh","Māndvi","Māndvi","Māngrol","Māngrol","Mānsa","Mānsa","Mānvi","Mānwat","Mānāvadar","Māpuca","Mārahra","Mārkāpur","Mātābhānga","Māvelikara","Mīnjūr","Mīrganj","Mīrānpur","Mīrānpur Katra","Mūdbidri","Mūl","Mūlki","Mūndwa","Mūvattupula","Nabīnagar","Nadiād","Naduvannūr","Nagar","Nagari","Nagda","Nagpur","Nagīna","Naharlagun","Nahorkatiya","Naihāti","Naini Tāl","Nainpur","Nainwa","Najafgarh","Najībābād","Nakodar","Naksalbāri","Nakūr","Naldurg","Nalgonda","Nalhāti","Nambiyūr","Nanauta","Nanded","Nandigāma","Nandikotkūr","Nandurbar","Nandyāl","Nangal","Nanjangūd","Napāsar","Naraina","Naraini","Narasannapeta","Narasapur","Narasaraopet","Narauli","Naraura","Naregal","Narela","Nargund","Narsimhapur","Narsinghgarh","Narsīpatnam","Narwar","Narwāna","Narāyangarh","Nashik","Nasrullāhganj","Nasīrābād","Nattam","Naugachhia","Nautanwa","Navadwīp","Navalgund","Navi Mumbai","Navsari","Nawalgarh","Nawanshahr","Nawābganj","Nawābganj","Nawābganj","Nawāda","Nayāgarh","Nedumangād","Neelankarai","Neem ka Thana","Negapatam","Nelamangala","Nellikkuppam","Nellore","Nepānagar","Neral","New Delhi","Neyveli","Neyyāttinkara","Nichlaul","Nidadavole","Nihtaur","Nilakottai","Nilanga","Nimāparha","Nipāni","Nirmal","Nirmāli","Niwai","Nizāmābād","Nohar","Noida","Nokha","Nongstoin","North Guwāhāti","North Lakhimpur","Nowrangapur","Noāmundi","Nābha","Nādbai","Nādāpuram","Nāgamangala","Nāgar Karnūl","Nāgaur","Nāgercoil","Nāgod","Nāhan","Nāmagiripettai","Nāmakkal","Nāmrup","Nāndgaon","Nāndūra Buzurg","Nāngloi Jāt","Nānpāra","Nāravārikuppam","Nārnaul","Nārnaund","Nārāyanpet","Nāspur","Nāsriganj","Nāthdwāra","Nāwa","Nāyudupet","Nīlgiri","Nīlokheri","Nīlēshwar","Nīmbāhera","Nīmāj","Nūrpur","Nūzvīd","Obra","Okha","Ongole","Ooty","Orai","Osmanabad","Ottappālam","Ozar","Pachperwa","Padam","Padampur","Padampur","Padmanābhapuram","Padra","Padrauna","Pahāsu","Paithan","Pakur","Palani","Palera","Paliā Kalān","Palladam","Pallappatti","Pallikondai","Pallippatti","Pallāvaram","Palmaner","Palwal","Palwancha","Palāsa","Panaji","Panchkula","Pandharpur","Pandua","Panna","Panruti","Panvel","Panāgar","Papanasam","Paramagudi","Paravūr Tekkumbhāgam","Parbhani","Pariyāpuram","Parli Vaijnāth","Parlākimidi","Parola","Partūr","Parvatsar","Parādīp Garh","Parāsia","Parīchhatgarh","Pasān","Patancheru","Pataudi","Pathalgaon","Pathanāmthitta","Patharia","Pathānkot","Patiāla","Patna","Patnāgarh","Patti","Pattukkottai","Patāmundai","Pauri","Pawni","Pawāyan","Payyannūr","Pedana","Peddapalli","Peddāpuram","Pehowa","Pen","Pennādam","Pennāgaram","Penugonda","Penukonda","Perambalur","Peranāmpattu","Peravurani","Periyakulam","Periyanayakkanpalaiyam","Perumbavoor","Perumpāvūr","Perundurai","Perungudi","Petlād","Phagwāra","Phalauda","Phalodi","Phaltan","Phaphūnd","Phek","Phillaur","Phirangipuram","Phulbāni","Phulera","Phulpur","Phusro","Pihānī","Pilibangan","Pilkhua","Pilāni","Pimpri","Pindwāra","Pinjaur","Pināhat","Pipili","Pipraich","Piravam","Piriyāpatna","Piro","Pithampur","Pithorāgarh","Pithāpuram","Pokaran","Polasara","Polavaram","Pollachi","Polūr","Ponda","Ponmana","Ponneri","Ponnur","Ponnāni","Ponnūru","Poonamalle","Porbandar","Porsa","Port Blair","Porur","Powai","Pratāpgarh","Proddatūr","Puducherry","Pudukkottai","Pujali","Pukhrāyān","Pulgaon","Pulivendla","Puliyangudi","Pulwama","Punalūr","Pune","Punganūru","Punjai Puliyampatti","Punāsa","Pupri","Puri","Purnia","Puruliya","Purwā","Pusad","Pushkar","Puttūr","Puttūr","Pāchora","Pākāla","Pālakkodu","Pālakollu","Pālanpur","Pālghar","Pālghāt","Pāli","Pāli","Pālitāna","Pālkonda","Pāloncha","Pānchla","Pāndhurnā","Pānihāti","Pānīpat","Pāonta Sāhib","Pāppinisshēri","Pārdi","Pārvatipuram","Pāsighāt","Pātan","Pāthardi","Pāthardih","Pāthri","Pātūr","Pāvugada","Pīlibhīt","Pīpri","Pīpār","Pūnch","Pūndri","Pūnāhāna","Pūranpur","Pūrna","Quthbullapur","Qādiān","Rabkavi","Raebareli","Rafiganj","Raghunathpur","Rahimatpur","Raigarh","Raipur","Raipur","Raisen","Rajaori","Rajapalaiyam","Rajpur","Rajpur","Rajpur Sonarpur","Ramagundam","Ramanathapuram","Ramanayyapeta","Rameswaram","Rampachodavaram","Rampur Hat","Ranchi","Rangia","Rangāpāra","Rasipuram","Rasrā","Ratangarh","Ratanpur","Ratia","Ratlām","Ratnagiri","Raurkela","Raxaul","Raybag","Rehli","Remuna","Renigunta","Renukūt","Reoti","Repalle","Revelganj","Rewa","Rewāri","Richha","Rishra","Rishīkesh","Risod","Robertsganj","Robertsonpet","Roha","Rohini","Rohtak","Ron","Roorkee","Ropar","Rura","Rusera","Rādhanpur","Rāghogarh","Rāhatgarh","Rāhuri","Rāichūr","Rāiganj","Rāikot","Rāipur","Rāisinghnagar","Rāj-Nāndgaon","Rājahmundry","Rājaldesar","Rājgarh","Rājgarh","Rājgarh","Rājgarh","Rājgurunagar","Rājgīr","Rājkot","Rājmahal","Rājpura","Rājpīpla","Rājsamand","Rājula","Rājākhera","Rājūra","Rāmachandrapuram","Rāmganj Mandi","Rāmgarh","Rāmgarh","Rāmgundam","Rāmjībanpur","Rāmnagar","Rāmnagar","Rāmnagar","Rāmpur","Rāmpur","Rāmpura","Rāmpura","Rāmtek","Rāmāpuram","Rānia","Rānikhet","Rānipet","Rānāghāt","Rānāvāv","Rānībennur","Rānīganj","Rānīpur","Rāpar","Rāth","Rāver","Rāwatbhāta","Rāwatsār","Rāya","Rāyachoti","Rāyadrug","Rāzampeta","Rāzām","Rīngas","Rūdarpur","Sabalgarh","Sadalgi","Sadashivpet","Sadābād","Safidon","Safīpur","Sagauli","Saharsa","Sahaspur","Sahaswān","Sahāranpur","Sahāwar","Saidpur","Saiha","Saint Thomas Mount","Sainthia","Sakleshpur","Saktī","Salem","Salāya","Sambalpur","Sambhal","Samdari","Samrāla","Samthar","Samālkha","Samāstipur","Sanaur","Sancoale","Sandīla","Sandūr","Sangamner","Sangariā","Sangod","Sangrūr","Sangāreddi","Sankeshwar","Sanāwad","Saoner","Saraipali","Sarauli","Sardhana","Sardulgarh","Sardārshahr","Sarkhej","Sarwār","Sarāi Mīr","Sarāi Ākil","Satara","Sathupalli","Sathyamangalam","Satna","Sattenapalle","Sattur","Satānā","Saugor","Saundatti","Sausar","Savanūr","Savarkundla","Sawāi Mādhopur","Secunderabad","Sehore","Selu","Sendhwa","Seohāra","Seondha","Seoni","Seoni Mālwa","Seram","Serchhīp","Serilingampalle","Shahbazpur","Shahdol","Shamsābād","Shamsābād","Shegaon","Sheikhpura","Sheoganj","Sheohar","Sheopur","Sherghāti","Sherkot","Shertallai","Shiggaon","Shikohābād","Shikārpur","Shikārpūr","Shillong","Shimla","Shimoga","Shiraguppi","Shirdi","Shirhatti","Shirpur","Shivaji Nagar","Shivpuri","Sholinghur","Shorāpur","Shrīgonda","Shrīrangapattana","Shrīrāmpur","Shujālpur","Shyamnagar","Shāhganj","Shāhi","Shāhjānpur","Shāhpur","Shāhpur","Shāhpur","Shāhpur","Shāhpura","Shāhpura","Shāhābād","Shāhābād","Shāhābād","Shāhābād","Shāhāda","Shājāpur","Shāmgarh","Shāmli","Shāntipur","Shīshgarh","Shōranūr","Sibsāgar","Siddhapur","Siddipet","Sidhaulī","Sidhi","Sidlaghatta","Sihor","Sihorā","Sijua","Sikandarpur","Sikandarābād","Sikandra Rao","Sikka","Silao","Silapathar","Silchar","Siliguri","Sillod","Silvassa","Simdega","Sindgi","Sindhnūr","Singarāyakonda","Singrauli","Singur","Singānallūr","Singāpur","Sinnar","Sirhind","Sirohi","Sironj","Sirsa","Sirsi","Sirsi","Sirsilla","Sirsāganj","Siruguppa","Sirumugai","Sirūr","Sisauli","Siswā Bāzār","Sitārganj","Siuri","Sivaganga","Sivagiri","Sivagiri","Sivakasi","Siwān","Siwāna","Sohna","Sohāgpur","Sojat","Sojītra","Solan","Solāpur","Someshwar","Sompeta","Sonepur","Songadh","Sonāmukhi","Sonāri","Sonīpat","Sopur","Sorada","Soro","Soron","Soygaon","Soyībug","Sri Dūngargarh","Sri Mādhopur","Srikakulam","Srinagar","Srirāmpur","Srivaikuntam","Srivilliputhur","Srīnagar","Srīnivāspur","Srīperumbūdūr","Srīrāmnagar","Srīsailain","Srīvardhan","Suket","Sultanpur","Sultānpur","Sulur","Sulya","Sundargarh","Sundarnagar","Sunel","Sunām","Supaul","Surat","Surendranagar","Suriānwān","Suriāpet","Suār","Sādri","Sāgar","Sāhibganj","Sālūmbar","Sālūr","Sāmalkot","Sāmba","Sāmbhar","Sānand","Sānchor","Sāndi","Sāngli","Sāngola","Sānkrāil","Sārangpur","Sāsvad","Sāvantvādi","Sāvda","Sāyla","Sīkar","Sīra","Sīrkāzhi","Sītāmarhi","Sītāpur","Sūjāngarh","Sūlūru","Sūrajgarh","Sūrandai","Sūratgarh","Takhatgarh","Takhatpur","Talegaon Dābhāde","Taleigao","Talipparamba","Taloda","Talwandi Bhai","Talwāra","Talāja","Tambaram","Tamlūk","Tanakpur","Tanjore","Tanuku","Tarakeswar","Tarikere","Tarn Tāran","Tarāna","Teghra","Tehri","Tekkalakote","Tekkali","Tekāri","Telhāra","Tellicherry","Teni","Teonthar","Terdāl","Tezpur","Thakurdwara","Tharangambadi","Tharād","Thenkasi","Thiruthani","Thiruvananthapuram","Thiruvarur","Thoothukudi","Thoubāl","Thrissur","Thākurganj","Thān","Thāna Bhawan","Thāne","Thānesar","Thāsra","Tijāra","Tilhar","Tindivanam","Tinnanūr","Tinsukia","Tiptūr","Tiruchchendur","Tiruchengode","Tiruchirappalli","Tirukkoyilur","Tirumala","Tirunelveli","Tirupati","Tirupparangunram","Tiruppur","Tiruppuvanam","Tirur","Tiruttangal","Tiruvalla","Tiruvallur","Tiruvannāmalai","Tiruvottiyūr","Tisaiyanvilai","Titlāgarh","Titāgarh","Todabhim","Todaraisingh","Tohāna","Tondi","Tonk","Tuensang","Tufānganj","Tuljāpur","Tulsīpur","Tumkūr","Tumsar","Tuni","Tura","Turaiyūr","Tādepalle","Tādepallegūdem","Tādpatri","Tājpur","Tāki","Tālcher","Tālīkota","Tānda","Tāndā","Tāndūr","Tāoru","Tāramangalam","Tārānagar","Tāsgaon","Tīkamgarh","Tīrthahalli","Tūndla","Udaipur","Udaipur","Udaipur","Udaipura","Udalguri","Udangudi","Udgīr","Udhampur","Udumalaippettai","Udupi","Ujhāni","Ujjain","Ulhasnagar","Ullal","Umarga","Umaria","Umarkhed","Umarkot","Umred","Umreth","Un","Una","Una","Unhel","Unjha","Unnāo","Upleta","Uppal Kalan","Uran","Uravakonda","Usehat","Usilampatti","Utraula","Uttamapālaiyam","Uttarkāshi","Uttiramerūr","V.S.K.Valasai (Dindigul-Dist.)","Vadakku Valliyūr","Vadakku Viravanallur","Vadamadurai","Vadigenhalli","Vadlapūdi","Vadnagar","Vadodara","Vaijāpur","Vaikam","Valabhīpur","Vallabh Vidyanagar","Valparai","Valsād","Vandavāsi","Vaniyambadi","Vapi","Varanasi","Varangaon","Varkala","Vasa","Vasco da Gama","Vasind","Vattalkundu","Vayalār","Vedaraniyam","Vejalpur","Vellore","Velur","Vemalwāda","Venkatagiri","Vepagunta","Verāval","Vetapālem","Vettaikkaranpudur","Vettūr","Vidisha","Vijayawada","Vijāpur","Vikārābād","Villupuram","Vinukonda","Virajpet","Virudunagar","Virār","Visakhapatnam","Visnagar","Vite","Vizianagaram","Vriddhāchalam","Vrindāvan","Vuyyūru","Vyāra","Vāda","Vādippatti","Vāsudevanallūr","Vīsāvadar","Wai","Walajapet","Wani","Wanparti","Warangal","Wardha","Warora","Warud","Wazīrganj","Wellington","Wer","Wokha","Wādi","Wānkāner","Wāris Alīganj","Wārāseonī","Wāshīm","Yamunānagar","Yanam","Yanamalakuduru","Yavatmāl","Yelahanka","Yellandu","Yellāpur","Yeola","Yādgīr","Yāval","Zahirābād","Zaidpur","Zamānia","Zira","Zunheboto","Ābu","Ābu Road","Ādilābād","Ādoni","Ālangulam","Āmli","Āmlāgora","Āmta","Āndippatti","Ārangaon","Āron","Ārvi","Āsandh","Āsansol","Āsika","Āsind","Āthagarh","Āvadi","Ūn"]}] [{"timezone":"America/New_York","cities":["Aberdeen","Abington","Abington","Acton","Acworth","Adams Morgan","Adelphi","Agawam","Aiken","Akron","Alafaya","Albany","Albany","Albemarle","Alexandria","Allapattah","Allentown","Alliance","Allison Park","Alpharetta","Altamonte Springs","Altoona","Americus","Amesbury","Amherst","Amherst","Amherst Center","Amsterdam","Anderson","Annandale","Annapolis","Ansonia","Apex","Apopka","Arbutus","Arlington","Arlington","Arnold","Asbury Park","Ashburn","Asheboro","Asheville","Ashland","Ashland","Ashland","Ashtabula","Aspen Hill","Astoria","Athens","Athens","Atlanta","Atlantic City","Attleboro","Auburn","Auburn","Auburn","Auburndale","Augusta","Augusta","Aurora","Austintown","Avenel","Aventura","Avon","Avon Center","Avon Lake","Back Mountain","Baileys Crossroads","Baldwin","Baldwin","Ballenger Creek","Baltimore","Bangor","Barberton","Barnstable","Barrington","Bartow","Basking Ridge","Batavia","Bath Beach","Bay Shore","Bay Village","Baychester","Bayonet Point","Bayonne","Bayshore Gardens","Bayside","Bayville","Bear","Beavercreek","Beckley","Bedford","Bel Air North","Bel Air South","Belle Glade","Belleville","Bellmore","Belmont","Beltsville","Belvedere Park","Bensalem","Bensonhurst","Berea","Bergenfield","Bethel Park","Bethesda","Bethlehem","Bethpage","Beverly","Beverly Cove","Biddeford","Billerica","Binghamton","Blacksburg","Bloomfield","Bloomingdale","Bluffton","Boardman","Boca Del Mar","Boca Raton","Bon Air","Bonita Springs","Boone","Borough Park","Boston","Bowie","Bowling Green","Boynton Beach","Bradenton","Braintree","Brandon","Branford","Brentwood","Briarwood","Bridgeport","Bridgeton","Bridgewater","Brighton","Brighton Beach","Bristol","Bristol","Bristol","Bristol","Broadview Heights","Brockton","Brook Park","Brookhaven","Brookline","Brooklyn","Brooklyn Heights","Brownsville","Brownsville","Brunswick","Brunswick","Brunswick","Buckhall","Buenaventura Lakes","Buffalo","Burke","Burlington","Burlington","Burlington","Burlington","Bushwick","Calhoun","Calverton","Cambria Heights","Cambridge","Camden","Camp Springs","Canarsie","Candler-McAfee","Canton","Canton","Canton","Cape Coral","Carlisle","Carney","Carol City","Carrboro","Carrollton","Carrollwood","Carrollwood Village","Carteret","Cartersville","Cary","Casselberry","Catonsville","Cave Spring","Center City","Centereach","Centerville","Central Falls","Central Islip","Centreville","Chambersburg","Chamblee","Chantilly","Chapel Hill","Charleston","Charleston","Charlotte","Charlottesville","Chattanooga","Cheektowaga","Chelmsford","Chelsea","Cherry Hill","Cherry Hill","Chesapeake","Cheshire","Chester","Chester","Chicopee","Chillicothe","Chillum","Chinatown","Christiansburg","Cicero","Cincinnati","Citrus Park","City of Milford (balance)","Clark-Fulton","Clarksburg","Clay","Clayton","Clearwater","Clemmons","Clemson","Clermont","Cleveland","Cleveland","Cleveland Heights","Cliffside Park","Clifton","Clifton Park","Clinton","Cloverly","Cockeysville","Cocoa","Coconut Creek","Coconut Grove","Cohoes","Colchester","College Park","College Point","Collinwood","Colonia","Colonial Heights","Columbia","Columbia","Columbus","Columbus","Commack","Concord","Concord","Concord","Coney Island","Conway","Conyers","Cooper City","Copiague","Coral Gables","Coral Springs","Coral Terrace","Coram","Cornelius","Corona","Cortland","Cortlandt Manor","Country Club","Country Walk","Coventry","Covington","Cranberry Township","Cranford","Cranston","Crofton","Culpeper","Cumberland","Cumberland","Cutler","Cutler Bay","Cutler Ridge","Cuyahoga Falls","Cypress Hills","Dale City","Dalton","Damascus","Danbury","Dania Beach","Danvers","Danville","Danville","Darien","Davie","Dayton","Daytona Beach","DeBary","DeLand","Decatur","Dedham","Deer Park","Deerfield Beach","Defiance","Delaware","Delray Beach","Deltona","Denville","Depew","Derry","Derry Village","Detroit-Shoreway","Dix Hills","Doral","Douglasville","Dover","Dover","Dover","Dracut","Drexel Hill","Dublin","Dublin","Duluth","Dumont","Dundalk","Dunedin","Dunwoody","Durham","Duxbury","Dyker Heights","Easley","East Amherst","East Brainerd","East Brunswick","East Chattanooga","East Cleveland","East Concord","East Elmhurst","East Flatbush","East Hampton","East Harlem","East Hartford","East Haven","East Lake","East Lake-Orient Park","East Longmeadow","East Massapequa","East Meadow","East Naples","East New York","East Northport","East Norwalk","East Orange","East Patchogue","East Point","East Providence","East Ridge","East Riverdale","East Setauket","East Tremont","East Village","Eastchester","Easthampton","Eastlake","Easton","Easton","Easton","Eden","Edgewater","Edgewood","Edison","Eggertsville","Egypt Lake-Leto","Eldersburg","Elizabeth","Elizabeth City","Elizabethtown","Elkridge","Elkton","Ellicott City","Elmhurst","Elmira","Elmont","Elmwood Park","Elyria","Emerson Hill","Enfield","Englewood","Erie","Erlanger","Essex","Estero","Euclid","Eustis","Evans","Everett","Ewing","Fair Lawn","Fairborn","Fairfax","Fairfield","Fairfield","Fairhaven","Fairland","Fairmont","Fairview Park","Fall River","Far Rockaway","Farmington","Farmingville","Farragut","Fayetteville","Fayetteville","Ferndale","Financial District","Findlay","Fitchburg","Flagami","Flatbush","Flatlands","Fleming Island","Floral Park","Florence","Florence","Florida Ridge","Fordham","Fords","Forest Hills","Forest Park","Forest Park","Fort Bragg","Fort Hamilton","Fort Hunt","Fort Lauderdale","Fort Lee","Fort Myers","Fort Pierce","Fort Thomas","Fort Washington","Fountainebleau","Four Corners","Framingham","Framingham Center","Franconia","Frankfort","Franklin","Franklin Square","Frederick","Fredericksburg","Freeport","Fremont","Fresh Meadows","Front Royal","Fruit Cove","Fuquay-Varina","Gahanna","Gainesville","Gainesville","Gaithersburg","Garden City","Gardner","Garfield","Garfield Heights","Garner","Gastonia","Gates-North Gates","Georgetown","Germantown","Glassboro","Glassmanor","Glastonbury","Glen Burnie","Glen Cove","Glendale","Glenvar Heights","Glenville","Gloucester","Gloversville","Golden Gate","Golden Glades","Goldsboro","Goose Creek","Grafton","Gramercy Park","Grand Island","Graniteville","Gravesend","Great Falls","Great Kills","Greater Northdale","Greater Upper Marlboro","Green","Green Haven","Greenacres City","Greenbelt","Greenburgh","Greeneville","Greenfield","Greenpoint","Greensboro","Greenville","Greenville","Greenwood","Greer","Griffin","Grove City","Guilford","Gwynn Oak","Hackensack","Hagerstown","Haines City","Hallandale Beach","Hamden","Hamilton","Hampton","Hanahan","Hanover","Hanover","Hanover","Harlem","Harrisburg","Harrison","Harrison","Harrisonburg","Hartford","Hauppauge","Havelock","Haverhill","Hawthorne","Hazleton","Hell's Kitchen","Hempstead","Henderson","Henrietta","Hermitage","Herndon","Hialeah","Hialeah Gardens","Hickory","Hicksville","High Point","Highland Springs","Hillcrest Heights","Hilliard","Hillsborough","Hillside","Hillside","Hilton Head","Hilton Head Island","Hinesville","Hoboken","Holbrook","Holden","Holiday","Hollis","Holly Springs","Hollywood","Holtsville","Holyoke","Homestead","Hopatcong Hills","Hope Mills","Hopewell","Hough","Howard Beach","Huber Heights","Hudson","Hunt Valley","Huntersville","Huntington","Huntington","Huntington Station","Hunts Point","Hyattsville","Hybla Valley","Idylwood","Ilchester","Immokalee","Independence","Indian Trail","Iona","Irondequoit","Ironville","Irvington","Iselin","Islip","Ithaca","Ives Estates","Jackson","Jackson Heights","Jacksonville","Jacksonville","Jacksonville Beach","Jamaica","Jamaica Plain","Jamestown","Jasmine Estates","Jersey City","Johns Creek","Johnson City","Johnston","Johnstown","Jupiter","Kannapolis","Kearny","Keene","Kendale Lakes","Kendall","Kendall West","Kenmore","Kennesaw","Kensington","Kent","Kernersville","Kettering","Kew Gardens","Kew Gardens Hills","Key West","Keystone","Killingly Center","King of Prussia","Kings Bridge","Kings Park","Kingsland","Kingsport","Kingston","Kinston","Kiryas Joel","Kissimmee","Knoxville","LaGrange","Lackawanna","Laconia","Lake Butler","Lake Magdalene","Lake Mary","Lake Ridge","Lake Ronkonkoma","Lake Shore","Lake Wales","Lake Worth","Lake Worth Corridor","Lakeland","Lakeside","Lakewood","Lakewood","Lancaster","Lancaster","Land O' Lakes","Landover","Langley Park","Lanham-Seabrook","Lansdale","Largo","Latham","Lauderdale Lakes","Lauderhill","Laurel","Laurel","Laurelton","Laurinburg","Lawrence","Lawrenceville","Lealman","Lebanon","Lebanon","Ledyard","Leesburg","Leesburg","Lehigh Acres","Leisure City","Leland","Lenoir","Leominster","Levittown","Levittown","Lewiston","Lexington","Lexington","Lexington","Lexington","Lexington-Fayette","Lima","Limerick","Lincoln","Lincolnia","Linden","Lindenhurst","Lindenwold","Linton Hall","Lithia Springs","Livingston","Lochearn","Lockport","Lodi","Long Beach","Long Branch","Long Island City","Longmeadow","Lorain","Lorton","Lowell","Ludlow","Lumberton","Lutherville-Timonium","Lutz","Lynbrook","Lynchburg","Lyndhurst","Lynn","Mableton","Macon","Madison","Madison","Mahwah","Maitland","Malden","Mamaroneck","Manassas","Manassas Park","Manchester","Manchester","Manhattan","Mansfield","Mansfield","Mansfield City","Maple Heights","Maple Shade","Maplewood","Marblehead","Marco Island","Margate","Marietta","Mariners Harbor","Marion","Marlboro","Marlborough","Martinez","Martinsburg","Maryland City","Marysville","Maryville","Mason","Maspeth","Massapequa","Massapequa Park","Massillon","Mastic","Matthews","Mauldin","Mayfield Heights","McDonough","McKeesport","McLean","Meadow Woods","Meadowbrook","Meads","Mechanicsville","Medford","Medford","Medina","Melbourne","Melrose","Melrose","Melville","Mentor","Mercerville-Hamilton Square","Meriden","Merrick","Merrifield","Merrimack","Merritt Island","Methuen","Miami","Miami Beach","Miami Gardens","Miami Lakes","Miamisburg","Middle River","Middle Village","Middleborough","Middleburg Heights","Middletown","Middletown","Middletown","Middletown","Middletown","Middletown","Milford","Milford","Milford Mill","Milledgeville","Millville","Milton","Milton","Mineola","Mint Hill","Miramar","Monroe","Monroeville","Monsey","Montclair","Montclair","Montgomery Village","Montville Center","Mooresville","Morganton","Morgantown","Morningside Heights","Morris Heights","Morrisania","Morristown","Morristown","Morrisville","Mott Haven","Mount Laurel","Mount Lebanon","Mount Pleasant","Mount Vernon","Mount Vernon","Murrysville","Myrtle Beach","Nanuet","Naples","Narragansett","Nashua","Natick","Naugatuck","Needham","New Bedford","New Bern","New Britain","New Brunswick","New Canaan","New Castle","New City","New Haven","New London","New Milford","New Philadelphia","New Port Richey","New Rochelle","New Smyrna Beach","New Springville","New York City","Newark","Newark","Newark","Newburgh","Newburyport","Newington","Newnan","Newport","Newport","Newport News","Newton","Niagara Falls","Nicetown-Tioga","Nicholasville","Niles","Norcross","Norfolk","Norland","Norristown","North Amityville","North Andover","North Arlington","North Atlanta","North Attleborough Center","North Augusta","North Babylon","North Bay Shore","North Bel Air","North Bellmore","North Bergen","North Bethesda","North Canton","North Charleston","North Chicopee","North Decatur","North Druid Hills","North Fort Myers","North Haven","North Kingstown","North Lauderdale","North Massapequa","North Miami","North Miami Beach","North Myrtle Beach","North Olmsted","North Plainfield","North Port","North Potomac","North Providence","North Ridgeville","North Royalton","North Stamford","North Tonawanda","North Valley Stream","Northampton","Northdale","Norton","Norwalk","Norwalk","Norwich","Norwood","Norwood","Nutley","Oak Hill","Oak Ridge","Oak Ridge","Oakland Park","Oakleaf Plantation","Oakton","Ocala","Ocean Acres","Oceanside","Ocoee","Odenton","Ojus","Old Bridge","Olney","Opa-locka","Orange","Oregon","Orlando","Ormond Beach","Ossining","Oswego","Oviedo","Owings Mills","Oxford","Oxon Hill","Oxon Hill-Glassmanor","Ozone Park","Painesville","Palisades Park","Palm Bay","Palm Beach Gardens","Palm City","Palm Coast","Palm Harbor","Palm River-Clair Mel","Palm Springs","Palm Valley","Palmer","Palmetto Bay","Paramus","Park Slope","Parkchester","Parkersburg","Parkland","Parkville","Parma","Parma Heights","Parole","Parsippany","Pasadena","Passaic","Pataskala","Paterson","Pawtucket","Peabody","Peachtree City","Peachtree Corners","Pearl River","Peekskill","Pembroke Pines","Penn Hills","Pennsauken","Pennsport","Perry","Perry Hall","Perrysburg","Perth Amboy","Petersburg","Phenix City","Philadelphia","Phoenixville","Pickerington","Pikesville","Pine Hills","Pinecrest","Pinehurst","Pinellas Park","Pinewood","Piqua","Piscataway","Pittsburgh","Pittsfield","Plainfield","Plainfield","Plainview","Plainville","Plant City","Plantation","Plattsburgh","Pleasantville","Plum","Poinciana","Point Pleasant","Pompano Beach","Ponte Vedra Beach","Pooler","Port Charlotte","Port Chester","Port Orange","Port Richmond","Port Saint Lucie","Port Washington","Portland","Portsmouth","Portsmouth","Portsmouth","Portsmouth","Portsmouth Heights","Potomac","Pottstown","Poughkeepsie","Princeton","Princeton","Providence","Punta Gorda","Punta Gorda Isles","Queens","Queens Village","Queensbury","Quincy","Radcliff","Radford","Radnor","Rahway","Raleigh","Ramsey","Randallstown","Randolph","Randolph","Reading","Reading","Redan","Redland","Rego Park","Reisterstown","Reston","Revere","Reynoldsburg","Richmond","Richmond","Richmond Hill","Richmond West","Ridgewood","Ridgewood","Riverdale","Riverside","Riverview","Riviera Beach","Roanoke","Roanoke Rapids","Rochester","Rochester","Rock Hill","Rockland","Rockledge","Rockville","Rockville Centre","Rocky Mount","Rocky River","Rome","Rome","Ronkonkoma","Roosevelt","Rose Hill","Rosedale","Rosedale","Roselle","Rossville","Rossville","Roswell","Rotterdam","Royal Palm Beach","Ruskin","Rutherford","Rutland","Rye","Saco","Safety Harbor","Saint Andrews","Saint Charles","Saint Cloud","Salem","Salem","Salem","Salisbury","Salisbury","San Carlos Park","Sandalfoot Cove","Sandusky","Sandy Springs","Sanford","Sanford","Sanford","Sarasota","Saratoga Springs","Saugus","Savannah","Sayreville","Sayreville Junction","Sayville","Scaggsville","Scarsdale","Schenectady","Scotch Plains","Scranton","Seabrook","Seaford","Sebastian","Secaucus","Selden","Seminole","Setauket-East Setauket","Seven Oaks","Severn","Severna Park","Sevierville","Sewell","Seymour","Shaker Heights","Shaw","Sheepshead Bay","Shelby","Shelbyville","Shelton","Shirley","Short Pump","Shrewsbury","Sicklerville","Sidney","Silver Spring","Simpsonville","Smithfield","Smithtown","Smyrna","Snellville","Socastee","Solon","Somerset","Somerset","Somerville","South Bel Air","South Boston","South Bradenton","South Burlington","South Euclid","South Gate","South Hadley","South Kingstown","South Laurel","South Miami Heights","South Old Bridge","South Orange","South Ozone Park","South Peabody","South Plainfield","South Portland","South Portland Gardens","South Riding","South River","South Suffolk","South Vineland","South Windsor","Southbridge","Southbury","Southchase","Sparta","Spartanburg","Spring Hill","Spring Valley","Springboro","Springfield","Springfield","Springfield","Springfield","Springfield Gardens","St. Charles","St. Johns","St. Marys","St. Petersburg","Stallings","Stamford","State College","Staten Island","Statesboro","Statesville","Staunton","Sterling","Steubenville","Stockbridge","Stonecrest","Stoneham","Storrs","Stoughton","Stow","Stratford","Streetsboro","Strongsville","Stuart","Sudbury","Sudley","Suffolk","Sugar Hill","Suitland","Suitland-Silver Hill","Summerville","Summit","Sumter","Sun City Center","Sunny Isles Beach","Sunnyside","Sunrise","Sunset","Sunset Park","Suwanee","Swansea","Sweetwater","Sylvania","Syosset","Syracuse","Takoma Park","Tallahassee","Tallmadge","Tamarac","Tamiami","Tampa","Tarpon Springs","Taunton","Tavares","Taylors","Teaneck","Temple Terrace","Terrace Heights","Tewksbury","The Acreage","The Bronx","The Crossings","The Hammocks","The Villages","Thomasville","Thomasville","Three Lakes","Throgs Neck","Tiffin","Tifton","Tinton Falls","Titusville","Toledo","Toms River","Torrington","Town 'n' Country","Towson","Tremont","Trenton","Trotwood","Troy","Troy","Trumbull","Tuckahoe","Tucker","Twinsburg","Tysons Corner","Union","Union City","Union City","Uniondale","Unionport","University","University Heights","University Park","Upper Arlington","Upper Saint Clair","Utica","Valdosta","Valley Stream","Valrico","Van Nest","Vandalia","Venice","Vero Beach","Vero Beach South","Vestal","Vienna","Vincentown","Vineland","Virginia Beach","Voorhees","Wade Hampton","Wadsworth","Wake Forest","Wakefield","Wakefield","Waldorf","Wallingford","Wallingford Center","Waltham","Wantagh","Warner Robins","Warren","Warren Township","Warwick","Washington","Washington Heights","Waterbury","Waterford","Watertown","Watertown","Waterville","Wayne","Wayne","Waynesboro","Weirton","Weirton Heights","Wekiwa Springs","Wellesley","Wellington","Wesley Chapel","West Albany","West Babylon","West Chester","West Columbia","West Elkridge","West Falls Church","West Hartford","West Haven","West Hempstead","West Hollywood","West Islip","West Little River","West Lynchburg","West Melbourne","West Mifflin","West Milford","West New York","West Orange","West Palm Beach","West Park","West Raleigh","West Scarborough","West Seneca","West Springfield","West Springfield","West Torrington","West Warwick","West and East Lealman","Westbrook","Westbury","Westchase","Westchester","Westerly","Westerville","Westfield","Westfield","Westford","Westlake","Westminster","Weston","Westport","Wethersfield","Weymouth","Wharton","Wheaton","Wheeling","White Oak","White Oak","White Plains","Whitehall","Whitehall Township","Whitestone","Whitman","Wilkes-Barre","Wilkinsburg","Williamsburg","Williamsburg","Williamsport","Williamstown","Willimantic","Willingboro","Willoughby","Willow Grove","Wilmington","Wilmington","Wilmington","Wilmington Island","Wilson","Wilton","Winchester","Winchester","Winchester","Winder","Windham","Windsor","Winston-Salem","Winter Garden","Winter Haven","Winter Park","Winter Springs","Winthrop","Woburn","Wolcott","Wolf Trap","Woodbridge","Woodhaven","Woodlawn","Woodlawn","Woodmere","Woodrow","Woodside","Woodstock","Woonsocket","Wooster","Worcester","Wyckoff","Xenia","Yarmouth","Yonkers","York","Youngstown","Zanesville"]},{"timezone":"Asia/Kolkata","cities":["Abhayāpuri","Abohar","Abrama","Achalpur","Achhnera","Adampur","Addanki","Adirampattinam","Adra","Adūr","Afzalgarh","Afzalpur","Agar","Agartala","Agra","Ahmadnagar","Ahmadpur","Ahmedabad","Ahraura","Airoli","Aistala","Aizawl","Ajmer","Ajnāla","Ajodhya","Ajra","Akalkot","Akaltara","Akbarpur","Akbarpur","Akivīdu","Akkarampalle","Aklera","Akola","Akot","Aland","Alandi","Alandur","Alangad","Alangāyam","Alappuzha","Allahābād","Almora","Alnāvar","Along","Alot","Aluva","Alwar","Alwaye","Alībāg","Alīganj","Alīgarh","Alīpur","Alīpur Duār","Amalner","Amalāpuram","Amaravati","Amarnāth","Amarpur","Amarpātan","Ambad","Ambasamudram","Ambattūr","Ambikāpur","Ambur","Ambāh","Ambājogāi","Ambāla","Amet","Amla","Amod","Amreli","Amritsar","Amroha","Amroli","Amrāvati","Amudālavalasa","Anakāpalle","Anamalais","Anand","Anandpur","Anantapur","Anantnag","Andol","Anekal","Angamāli","Angul","Anjad","Anjangaon","Anjār","Ankleshwar","Annigeri","Annur","Anshing","Anta","Anthiyur","Anūpgarh","Anūppur","Anūpshahr","Aonla","Arakkonam","Arang","Arantāngi","Arcot","Ariyalūr","Arkalgūd","Arni","Aroor","Arrah","Arsikere","Arukutti","Arumuganeri","Aruppukkottai","Arāmbāgh","Arāria","Ashoknagar","Ashoknagar Kalyangarh","Ashta","Ashta","Asifābād","Atarra","Athni","Atmakūr","Atraulī","Attili","Attingal","Attur","Auraiya","Aurangabad","Aurangābād","Aurād","Ausa","Avanigadda","Avinashi","Ayakudi","Azamgarh","Azhiyūr","Baberu","Babrāla","Babīna","Bachhraon","Bada Barabīl","Badagara","Baddi","Badlapur","Badnāwar","Badvel","Badūria","Bagaha","Bagaha","Bagalkot","Bagar","Bagasra","Bagdogra","Bagulā","Baharampur","Baheri","Bahjoi","Bahraigh","Bahula","Bahādurganj","Bahādurgarh","Baidyabāti","Baihar","Bail-Hongal","Bairāgnia","Bakhtiyārpur","Balarāmpur","Balasore","Ballālpur","Balod","Baloda Bāzār","Balrāmpur","Balāngīr","Banat","Banda","Bandipura","Banga","Banganapalle","Bangaon","Bangaon","Bangarapet","Bankra","Banmankhi","Bannūr","Bantvāl","Banūr","Bar Bigha","Bara Uchāna","Baranagar","Barauli","Baraut","Barbil","Barddhamān","Bareilly","Bargarh","Bargi","Barhiya","Bari Sādri","Barjala","Barki Saria","Barkā Kānā","Barnāla","Barpeta","Barpeta Road","Barpāli","Baruipur","Barwāla","Barwāni","Basavakalyān","Basavana Bāgevādi","Basi","Basi","Basi","Basmat","Basni","Bastī","Baswa","Bathinda","Batāla","Baud","Bawāna","Bayāna","Bedi","Begamganj","Begusarai","Begūn","Behat","Behror","Bela","Beldānga","Belgaum","Bellampalli","Bellary","Belonia","Belsand","Belūr","Bemetāra","Bengaluru","Beohāri","Berasia","Beri Khās","Betamcherla","Bettiah","Betūl","Bewar","Beypore","Beāwar","Bhabhua","Bhachāu","Bhadaur","Bhadohi","Bhadrakh","Bhadreswar","Bhadrāchalam","Bhadrāvati","Bhainsdehi","Bhaisa","Bhandāra","Bhanjanagar","Bharatpur","Bharthana","Bharwāri","Bharūch","Bhasāwar","Bhatkal","Bhattiprolu","Bhavnagar","Bhavāni","Bhawanipur","Bhawāni Mandi","Bhawānipatna","Bhawānīgarh","Bhayandar","Bhikangaon","Bhilai","Bhind","Bhindār","Bhinga","Bhitarwār","Bhiwadi","Bhiwandi","Bhiwāni","Bhogpur","Bhongaon","Bhongīr","Bhopāl","Bhor","Bhuban","Bhubaneshwar","Bhudgaon","Bhuj","Bhusāval","Bhādra","Bhādāsar","Bhāgalpur","Bhālki","Bhānder","Bhānpura","Bhānpurī","Bhānvad","Bhātpāra","Bhātāpāra","Bhāyāvadar","Bhīkhi","Bhīlwāra","Bhīmavaram","Bhīmunipatnam","Bhīnmāl","Bhūm","Biaora","Bidhūna","Bihār Sharīf","Bihārīganj","Bijapur","Bijbehara","Bijnor","Bijāwar","Bikramganj","Bilgi","Bilgrām","Bilhaur","Bilimora","Bilsanda","Bilsi","Bilthra","Bilāra","Bilāri","Bilāsipāra","Bilāspur","Bindki","Binka","Birmitrapur","Birpara","Birūr","Bisauli","Bishnupur","Bissāu","Biswān","Bobbili","Bodhan","Bodināyakkanūr","Boisar","Bokajān","Bokāro","Bolpur","Bongaigaon","Borivli","Borsad","Botād","Brahmapur","Brājarājnagar","Budaun","Buddh Gaya","Budge Budge","Budhlāda","Budhāna","Bulandshahr","Buldāna","Burhar","Burhānpur","Burla","Buxar","Byndoor","Byādgi","Bābai","Bābra","Bādāmi","Bāgepalli","Bāgha Purāna","Bāghpat","Bāh","Bāli","Bāli","Bālotra","Bālugaon","Bālurghāt","Bālāchor","Bālāghāt","Bālāpur","Bāmor Kalān","Bānapur","Bānda","Bāndīkūi","Bāngarmau","Bānka","Bānki","Bānkura","Bānsbāria","Bānsdīh","Bānsi","Bānswāda","Bānswāra","Bāntva","Bāpatla","Bāramūla","Bārdoli","Bārh","Bāri","Bārmer","Bārsi","Bāruni","Bārākpur","Bārāmati","Bārān","Bārāsat","Bāsoda","Bāsudebpur","Bāzpur","Bīdar","Bīkaner","Bīlāspur","Bīrpur","Bīsalpur","Būndi","Būndu","Calangute","Canning","Chakapara","Chaklāsi","Chakradharpur","Chaksu","Challakere","Challapalle","Chalāla","Chamba","Chamrajnagar","Chandannagar","Chandauli","Chanderi","Chandrakona","Chanduasi","Chandīgarh","Changanācheri","Channagiri","Channapatna","Channarāyapatna","Charkhi Dādri","Charkhāri","Charthāwal","Chatrapur","Chatrā","Chemmumiahpet","Chengalpattu","Chengam","Chengannūr","Chennai","Chennimalai","Cherpulassery","Cherthala","Chetput","Chettipālaiyam","Chetwayi","Cheyyar","Chhabra","Chhala","Chhaprauli","Chharra","Chhatarpur","Chhibrāmau","Chhindwāra","Chhota Udepur","Chhoti Sādri","Chhāpar","Chhāta","Chhātāpur","Chicholi","Chidambaram","Chidawa","Chik Ballāpur","Chikhli","Chikmagalūr","Chiknāyakanhalli","Chikodi","Chilakalūrupet","Chillupār","Chincholi","Chinna Salem","Chinnachowk","Chinnamanūr","Chintāmani","Chiplūn","Chirmiri","Chitradurga","Chittaranjan","Chittaurgarh","Chittūr","Chodavaram","Chopda","Chotila","Chunār","Churāchāndpur","Chākan","Chākia","Chālisgaon","Chāmpa","Chānasma","Chānda","Chāndor","Chāndpur","Chāndur","Chāndūr","Chāndūr Bāzār","Chāpar","Chāpra","Chās","Chāībāsa","Chīpurupalle","Chīrāla","Chītāpur","Chūru","Clement Town","Closepet","Cochin","Coimbatore","Colachel","Colgong","Colonelganj","Contai","Coondapoor","Cuddalore","Cuddapah","Cumbum","Cumbum","Cuncolim","Curchorem","Cuttack","Dabhoi","Daboh","Dabra","Dabwāli","Dahegām","Dalkola","Dalsingh Sarai","Daltonganj","Dam Dam","Damoh","Damān","Dandeli","Darbhanga","Darsi","Daryāpur","Dasnapur","Dasūya","Datia","Dattāpur","Daudnagar","Daund","Dausa","Davangere","Dehra Dūn","Dehri","Delhi","Denkanikota","Deoband","Deogarh","Deoghar","Deoli","Deoli","Deoli","Deolāli","Deoraniān","Deori Khās","Deoria","Depālpur","Deshnoke","Devakottai","Devanhalli","Devarkonda","Devgadh Bāriya","Devgarh","Dewas","Deūlgaon Rāja","Dhamtari","Dhanaula","Dhanaura","Dhanbad","Dhandhuka","Dhanera","Dharampur","Dharamsala","Dharangaon","Dharapuram","Dharmadam","Dharmanagar","Dharmapuri","Dharmavaram","Dharmābād","Dhaulpur","Dhaurahra","Dhekiajuli","Dhenkānāl","Dhing","Dholka","Dhone","Dhorāji","Dhrol","Dhrāngadhra","Dhuburi","Dhulagari","Dhuliān","Dhupgāri","Dhāka","Dhāmnod","Dhāmpur","Dhār","Dhāri","Dhāriwāl","Dhāruhera","Dhārūr","Dhūlia","Dhūri","Diamond Harbour","Dibai","Dibrugarh","Dicholi","Digboi","Dighwāra","Digras","Dimāpur","Dinapore","Dindigul","Dindori","Diphu","Diu","Doda","Doddaballapura","Dohad","Dombivli","Dondaicha","Dongargarh","Dornakal","Dorāha","Dubrājpur","Dugda","Duliāgaon","Dum Duma","Dumjor","Dumka","Dumra","Dumraon","Durg","Durgapur","Durgāpur","Dwārka","Dādri","Dāhānu","Dākor","Dāmnagar","Dārjiling","Dārwha","Dāsna","Dātāganj","Dīdwāna","Dīg","Dīglūr","Dīnhāta","Dīnānagar","Dīsa","Dūngarpur","Egra","Elamanchili","Ellenabad","Ellore","Elūr","Emmiganūr","Erandol","Erode","Erraguntla","Erāttupetta","Etāwa","Etāwah","Faizpur","Farakka","Faridabad","Farrukhnagar","Farrukhābād","Farīdkot","Farīdpur","Fatehganj West","Fatehgarh Chūriān","Fatehpur","Fatehpur","Fatehpur","Fatehpur Sīkri","Fatehābād","Fatehābād","Fatwa","Ferokh","Ferozepore","Forbesganj","Fort Gloster","French Rocks","Fyzābād","Fālākāta","Fāzilka","Fīrozpur Jhirka","Fīrozābād","Gadag","Gadag-Betageri","Gaddi Annaram","Gadhada","Gadhinglaj","Gadwāl","Gajendragarh","Gajraula","Gajuwaka","Gandevi","Gandhinagar","Gangoh","Gangolli","Gangtok","Gangākher","Gangānagar","Gangāpur","Gangāpur","Gangāpur","Gangārāmpur","Gangāwati","Ganj Dundwāra","Gannavaram","Garhmuktesar","Garhshankar","Garhwa","Garhākota","Gariadhar","Garui","Gauribidanur","Gauripur","Gaya","Gevrai","Gharaunda","Ghatkesar","Ghazīpur","Ghosī","Ghoti Budrukh","Ghugus","Ghātampur","Ghātanji","Ghātsīla","Ghātāl","Ghāziābād","Giddalūr","Giddarbāha","Gingee","Gobichettipalayam","Gobindpur","Gobārdānga","Godda","Godhra","Gohadi","Gohāna","Gokak","Gokarna","Gokavaram","Gola Gokarannāth","Golāghāt","Gomoh","Gondal","Gondiā","Gondā City","Gopālganj","Gorakhpur","Gorakhpur","Gorantla","Gosāba","Govardhan","Goyerkāta","Goālpāra","Greater Noida","Gubbi","Gudalur","Gudivāda","Gudiyatham","Gulbarga","Guledagudda","Gulābpura","Gulāothi","Gumia","Gumlā","Gummidipundi","Guna","Gundlupēt","Gunnaur","Guntakal Junction","Guntur","Gunupur","Gurgaon","Gurmatkāl","Gursahāiganj","Gursarāi","Guru Har Sahāi","Guruvāyūr","Guskhara","Guwahati","Gwalior","Gyānpur","Gādarwāra","Gāndarbal","Gāndhīdhām","Gīrīdīh","Gūduvāncheri","Gūdūr","Hadagalli","Hadgāon","Hailākāndi","Haldaur","Haldia","Haldwani","Haliyal","Halvad","Hamīrpur","Hamīrpur","Handiā","Hanumāngarh","Harda Khās","Hardoī","Haridwar","Harihar","Harpanahalli","Harpālpur","Harsūd","Harūr","Hasanpur","Hassan","Hastināpur","Hatta","Hazāribāgh","Hilsa","Himatnagar","Hindaun","Hindoria","Hindupur","Hinganghāt","Hingoli","Hinjilikatu","Hirekerūr","Hiriyūr","Hisar","Hisuā","Hodal","Hojāi","Holalkere","Hole Narsipur","Homnābād","Honavar","Honnāli","Hosdurga","Hoshangābād","Hoshiārpur","Hoskote","Hospet","Hosūr","Howli","Hubli","Hugli","Hukeri","Hungund","Hunsūr","Husainābād","Hyderābād","Hābra","Hāflong","Hājo","Hājīpur","Hālol","Hālīsahar","Hāngal","Hānsi","Hāora","Hāpur","Hārij","Hāsimāra","Hāthras","Hāveri","Hīrākud","Ichalkaranji","Ichchāpuram","Idappadi","Igatpuri","Ilkal","Imphāl","Indergarh","Indi","Indore","Indri","Indāpur","Ingrāj Bāzār","Injambakkam","Iringal","Irinjālakuda","Irugūr","Islāmnagar","Islāmpur","Islāmpur","Itimādpur","Itānagar","Itārsi","Jabalpur","Jagalūr","Jagatsinghapur","Jagdalpur","Jagdīshpur","Jagdīspur","Jaggayyapeta","Jagraon","Jagtiāl","Jagādhri","Jahāngīrābād","Jahānābād","Jahāzpur","Jaigaon","Jainagar","Jaipur","Jais","Jaisalmer","Jaisingpur","Jaito","Jaitāran","Jalalpore","Jalandhar","Jalesar","Jaleshwar","Jalgaon","Jalgaon Jamod","Jalor","Jalpāiguri","Jalālpur","Jalālābad","Jalālābād","Jalālābād","Jalālī","Jalārpet","Jambusar","Jamkhandi","Jammalamadugu","Jammu","Jamnagar","Jamshedpur","Jamālpur","Jamūī","Jandiāla","Jangaon","Jangipur","Jaorā","Jarwal","Jasdan","Jashpurnagar","Jasidih","Jaspur","Jaswantnagar","Jatani","Jatāra","Jaunpur","Jayamkondacholapuram","Jaynagar Majilpur","Jetpur","Jevargi","Jewar","Jeypore","Jhajjar","Jhalidā","Jhanjhārpur","Jharia","Jharsuguda","Jhinjhāna","Jhumri Telaiya","Jhunjhunūn","Jhā-Jhā","Jhābua","Jhālrapātan","Jhālu","Jhālāwār","Jhānsi","Jhārgrām","Jhīnjhak","Jhūsi","Jintūr","Jodhpur","Jodhpur","Jodiya Bandar","Jogbani","Jora","Jorhāt","Jugsālai","Junnar","Jājpur","Jālaun","Jālna","Jāmadoba","Jāmai","Jāmtāra","Jāmuria","Jānjgīr","Jānsath","Jāwad","Jīnd","Jūnāgadh","Jūnāgarh","Kabrāi","Kachhwa","Kadakkavoor","Kadayanallur","Kadi","Kadiri","Kadod","Kadūr","Kaikalūr","Kailāras","Kailāshahar","Kaimganj","Kaimori","Kairāna","Kaithal","Kakching","Kakdwip","Kakrāla","Kalakkādu","Kalamassery","Kalamb","Kalamnūri","Kalavoor","Kalghatgi","Kallakkurichchi","Kallakurichi","Kallidaikurichi","Kalmeshwar","Kalpatta","Kalugumalai","Kalyandurg","Kalyani","Kalyān","Kalānaur","Kamalganj","Kampli","Kanchipuram","Kandukūr","Kangayam","Kanigiri","Kankauli","Kannad","Kannauj","Kanniyākumāri","Kannod","Kannur","Kanpur","Kantābānji","Kanuru","Kapadvanj","Kapūrthala","Karamsad","Karanpur","Karauli","Kareli","Karera","Karhal","Karjat","Karmāla","Karnāl","Karol Bāgh","Kartārpur","Karur","Karwar","Karād","Karīmganj","Karīmnagar","Kasba","Kashipur","Kasrāwad","Katangi","Katangi","Katghora","Kathua","Katihar","Kattanam","Kattivākkam","Kawardha","Kayalpattinam","Keelakarai","Kekri","Kemrī","Kenda","Kendrāparha","Kerūr","Keshod","Keshorai Pātan","Kesinga","Khada","Khadki","Khagaria","Khagaul","Khair","Khairābād","Khairāgarh","Khairāgarh","Khajuraho Group of Monuments","Khalīlābād","Khamaria","Khambhāliya","Khambhāt","Khammam","Khandela","Khandwa","Khanna","Kharagpur","Kharagpur","Kharakvasla","Kharar","Khardah","Khargone","Kharkhauda","Kharsia","Khatauli","Khatīma","Kheda","Khedbrahma","Khekra","Kheri","Kherālu","Khetia","Khetri","Khilchipur","Khirkiyān","Khopoli","Khowai","Khuldābād","Khunti","Khurai","Khurda","Khurja","Khāchrod","Khāmgaon","Khānāpur","Khāpa","Khārupatia","Khātegaon","Khātra","Khūtār","Kichha","Kinwat","Kirandul","Kiraoli","Kishanganj","Kishangarh","Kishtwār","Kithor","Kizhake Chālakudi","Koch Bihār","Kodaikānāl","Kodarmā","Kodoli","Kodungallūr","Kodār","Kodīnar","Koelwār","Kohīma","Kokrajhar","Kolasib","Kolhāpur","Kolkata","Kollam","Kollegāl","Kolār","Kolāras","Konch","Kondagaon","Kondapalle","Konnagar","Konnūr","Konārka","Koothanallur","Kopargaon","Koppal","Kopāganj","Koratla","Korba","Koregaon","Korwai","Korāput","Kosamba","Kosi","Kosigi","Kota","Kotagiri","Kotamangalam","Kotapārh","Kotdwāra","Kotkapura","Kotma","Kotputli","Kottagūdem","Kottayam","Kottūru","Kotā","Kovilpatti","Kovvūr","Kovūr","Koynanagar","Kozhikode","Koāth","Krishnagiri","Krishnanagar","Krishnarājpet","Kuchaiburi","Kuchera","Kuchāman","Kudachi","Kuju","Kukshi","Kulgam","Kulittalai","Kulpahār","Kulti","Kulu","Kumarapalayam","Kumbakonam","Kumbalam","Kumbhrāj","Kumhāri","Kumta","Kunda","Kundarkhi","Kundgol","Kundla","Kunigal","Kunnamangalam","Kunnamkulam","Kuppam","Kurandvād","Kurduvādi","Kurinjippādi","Kurnool","Kushtagi","Kutiatodu","Kutiyāna","Kuttampuzha","Kuzhithurai","Kyathampalle","Kāgal","Kākināda","Kākori","Kālimpong","Kāliyāganj","Kālka","Kālna","Kālol","Kālpi","Kālānwāli","Kālāvad","Kāman","Kāmthi","Kāmākhyānagar","Kāmāreddi","Kāmārhāti","Kānchrāpāra","Kāndhla","Kāndi","Kāndla","Kānke","Kānker","Kānkānhalli","Kānnangād","Kānt","Kānth","Kāpren","Kāraikkudi","Kāraikāl","Kāramadai","Kāranja","Kārkala","Kārsiyāng","Kāsaragod","Kāsganj","Kāthor","Kātol","Kātoya","Kātpādi","Kātrās","Kāvali","Kāyankulam","Kīl Bhuvanagiri","Kīratpur","Kūdligi","Kūkatpalli","Kūmher","Lachhmangarh Sīkar","Lahār","Lakhnādon","Lakhyabad","Lakhīmpur","Laksar","Lakshmeshwar","Lal Bahadur Nagar","Lalgudi","Lalitpur","Lar","Latur","Laungowāl","Leh","Leteri","Limbdi","Lingsugūr","Lohārdagā","Lonar","Lonavla","Loni","Losal","Luckeesarai","Lucknow","Ludhiāna","Lumding Railway Colony","Lunglei","Lādnūn","Lādwa","Lāharpur","Lākheri","Lālganj","Lālganj","Lālgola","Lālpur","Lālsot","Lātehār","Lāthi","Lāwar Khās","Lūnāvāda","Machhlīshahr","Machilīpatnam","Madambakkam","Madanapalle","Maddūr","Madgaon","Madhipura","Madhubani","Madhugiri","Madhupur","Madhyamgram","Madikeri","Madipakkam","Madukkarai","Madukkūr","Madurai","Madurāntakam","Maghar","Maham","Mahbūbnagar","Mahbūbābād","Mahemdāvād","Mahendragarh","Maheshtala","Maheshwar","Mahgawān","Mahiari","Mahmudābād","Mahobā","Maholi","Mahudha","Mahwah","Mahād","Mahālingpur","Mahārāganj","Mahārājgani","Mahāsamund","Mahē","Mahīshādal","Maihar","Mainpuri","Maināguri","Mairwa","Majalgaon","Makrāna","Maksi","Malakanagiri","Malappuram","Malaut","Malavalli","Malkajgiri","Malkapur","Malkāpur","Mallasamudram","Malpe","Malīhābād","Manali","Manamadurai","Manapparai","Mancherāl","Mandamarri","Mandapam","Mandapeta","Mandi","Mandideep","Mandlā","Mandsaur","Mandya","Mandāwar","Maner","Mangalagiri","Mangaldai","Mangalore","Manglaur","Mangrūl Pīr","Maniar","Manihāri","Manipal","Manjeri","Manjhanpur","Mankāchar","Manmād","Mannargudi","Mannārakkāt","Manoharpur","Manthani","Manuguru","Manāsa","Manāwar","Marakkanam","Marayur","Margherita","Marhaura","Mariāhu","Mariāni","Masaurhi Buzurg","Mathura","Mattanur","Mau","Mau","Mau Aimma","Maudaha","Mauganj","Maur","Mavoor","Mawāna","Mayiladuthurai","Mayāng Imphāl","Medak","Medinīpur","Meerut","Mehkar","Mehndāwal","Melur","Memāri","Mendarda","Merta","Mettupalayam","Mettur","Mhāsvād","Mihona","Milak","Miriālgūda","Mirzāpur","Misrikh","Modāsa","Moga","Mohali","Moirāng","Mokameh","Mokokchūng","Mon","Monghyr","Monoharpur","Moram","Morbi","Morena","Morigaon","Morinda","Mormugao","Morsi","Morwa","Morādābād","Morār","Mothīhāri","Mubarakpur","Muddebihāl","Mudgal","Mudhol","Mudkhed","Mughal Sarāi","Muhammadābād","Muhammadābād","Muhammadābād","Mukeriān","Mukher","Muktsar","Mulbāgal","Mulgund","Multai","Mulugu","Muluppilagadu","Mumbai","Mundargi","Mundgod","Mundra","Mungaoli","Mungeli","Munnar","Murbād","Murlīganj","Murshidābād","Murtajāpur","Murudeshwara","Murwāra","Murādnagar","Mushābani","Musiri","Mussoorie","Muttupet","Muvattupuzha","Muzaffarnagar","Muzaffarpur","Mysore","Mācherla","Māchhīwāra","Māgadi","Mākum","Mālegaon","Māler Kotla","Mālpura","Mālvan","Mālūr","Māndal","Māndalgarh","Māndvi","Māndvi","Māngrol","Māngrol","Mānsa","Mānsa","Mānvi","Mānwat","Mānāvadar","Māpuca","Mārahra","Mārkāpur","Mātābhānga","Māvelikara","Mīnjūr","Mīrganj","Mīrānpur","Mīrānpur Katra","Mūdbidri","Mūl","Mūlki","Mūndwa","Mūvattupula","Nabīnagar","Nadiād","Naduvannūr","Nagar","Nagari","Nagda","Nagpur","Nagīna","Naharlagun","Nahorkatiya","Naihāti","Naini Tāl","Nainpur","Nainwa","Najafgarh","Najībābād","Nakodar","Naksalbāri","Nakūr","Naldurg","Nalgonda","Nalhāti","Nambiyūr","Nanauta","Nanded","Nandigāma","Nandikotkūr","Nandurbar","Nandyāl","Nangal","Nanjangūd","Napāsar","Naraina","Naraini","Narasannapeta","Narasapur","Narasaraopet","Narauli","Naraura","Naregal","Narela","Nargund","Narsimhapur","Narsinghgarh","Narsīpatnam","Narwar","Narwāna","Narāyangarh","Nashik","Nasrullāhganj","Nasīrābād","Nattam","Naugachhia","Nautanwa","Navadwīp","Navalgund","Navi Mumbai","Navsari","Nawalgarh","Nawanshahr","Nawābganj","Nawābganj","Nawābganj","Nawāda","Nayāgarh","Nedumangād","Neelankarai","Neem ka Thana","Negapatam","Nelamangala","Nellikkuppam","Nellore","Nepānagar","Neral","New Delhi","Neyveli","Neyyāttinkara","Nichlaul","Nidadavole","Nihtaur","Nilakottai","Nilanga","Nimāparha","Nipāni","Nirmal","Nirmāli","Niwai","Nizāmābād","Nohar","Noida","Nokha","Nongstoin","North Guwāhāti","North Lakhimpur","Nowrangapur","Noāmundi","Nābha","Nādbai","Nādāpuram","Nāgamangala","Nāgar Karnūl","Nāgaur","Nāgercoil","Nāgod","Nāhan","Nāmagiripettai","Nāmakkal","Nāmrup","Nāndgaon","Nāndūra Buzurg","Nāngloi Jāt","Nānpāra","Nāravārikuppam","Nārnaul","Nārnaund","Nārāyanpet","Nāspur","Nāsriganj","Nāthdwāra","Nāwa","Nāyudupet","Nīlgiri","Nīlokheri","Nīlēshwar","Nīmbāhera","Nīmāj","Nūrpur","Nūzvīd","Obra","Okha","Ongole","Ooty","Orai","Osmanabad","Ottappālam","Ozar","Pachperwa","Padam","Padampur","Padampur","Padmanābhapuram","Padra","Padrauna","Pahāsu","Paithan","Pakur","Palani","Palera","Paliā Kalān","Palladam","Pallappatti","Pallikondai","Pallippatti","Pallāvaram","Palmaner","Palwal","Palwancha","Palāsa","Panaji","Panchkula","Pandharpur","Pandua","Panna","Panruti","Panvel","Panāgar","Papanasam","Paramagudi","Paravūr Tekkumbhāgam","Parbhani","Pariyāpuram","Parli Vaijnāth","Parlākimidi","Parola","Partūr","Parvatsar","Parādīp Garh","Parāsia","Parīchhatgarh","Pasān","Patancheru","Pataudi","Pathalgaon","Pathanāmthitta","Patharia","Pathānkot","Patiāla","Patna","Patnāgarh","Patti","Pattukkottai","Patāmundai","Pauri","Pawni","Pawāyan","Payyannūr","Pedana","Peddapalli","Peddāpuram","Pehowa","Pen","Pennādam","Pennāgaram","Penugonda","Penukonda","Perambalur","Peranāmpattu","Peravurani","Periyakulam","Periyanayakkanpalaiyam","Perumbavoor","Perumpāvūr","Perundurai","Perungudi","Petlād","Phagwāra","Phalauda","Phalodi","Phaltan","Phaphūnd","Phek","Phillaur","Phirangipuram","Phulbāni","Phulera","Phulpur","Phusro","Pihānī","Pilibangan","Pilkhua","Pilāni","Pimpri","Pindwāra","Pinjaur","Pināhat","Pipili","Pipraich","Piravam","Piriyāpatna","Piro","Pithampur","Pithorāgarh","Pithāpuram","Pokaran","Polasara","Polavaram","Pollachi","Polūr","Ponda","Ponmana","Ponneri","Ponnur","Ponnāni","Ponnūru","Poonamalle","Porbandar","Porsa","Port Blair","Porur","Powai","Pratāpgarh","Proddatūr","Puducherry","Pudukkottai","Pujali","Pukhrāyān","Pulgaon","Pulivendla","Puliyangudi","Pulwama","Punalūr","Pune","Punganūru","Punjai Puliyampatti","Punāsa","Pupri","Puri","Purnia","Puruliya","Purwā","Pusad","Pushkar","Puttūr","Puttūr","Pāchora","Pākāla","Pālakkodu","Pālakollu","Pālanpur","Pālghar","Pālghāt","Pāli","Pāli","Pālitāna","Pālkonda","Pāloncha","Pānchla","Pāndhurnā","Pānihāti","Pānīpat","Pāonta Sāhib","Pāppinisshēri","Pārdi","Pārvatipuram","Pāsighāt","Pātan","Pāthardi","Pāthardih","Pāthri","Pātūr","Pāvugada","Pīlibhīt","Pīpri","Pīpār","Pūnch","Pūndri","Pūnāhāna","Pūranpur","Pūrna","Quthbullapur","Qādiān","Rabkavi","Raebareli","Rafiganj","Raghunathpur","Rahimatpur","Raigarh","Raipur","Raipur","Raisen","Rajaori","Rajapalaiyam","Rajpur","Rajpur","Rajpur Sonarpur","Ramagundam","Ramanathapuram","Ramanayyapeta","Rameswaram","Rampachodavaram","Rampur Hat","Ranchi","Rangia","Rangāpāra","Rasipuram","Rasrā","Ratangarh","Ratanpur","Ratia","Ratlām","Ratnagiri","Raurkela","Raxaul","Raybag","Rehli","Remuna","Renigunta","Renukūt","Reoti","Repalle","Revelganj","Rewa","Rewāri","Richha","Rishra","Rishīkesh","Risod","Robertsganj","Robertsonpet","Roha","Rohini","Rohtak","Ron","Roorkee","Ropar","Rura","Rusera","Rādhanpur","Rāghogarh","Rāhatgarh","Rāhuri","Rāichūr","Rāiganj","Rāikot","Rāipur","Rāisinghnagar","Rāj-Nāndgaon","Rājahmundry","Rājaldesar","Rājgarh","Rājgarh","Rājgarh","Rājgarh","Rājgurunagar","Rājgīr","Rājkot","Rājmahal","Rājpura","Rājpīpla","Rājsamand","Rājula","Rājākhera","Rājūra","Rāmachandrapuram","Rāmganj Mandi","Rāmgarh","Rāmgarh","Rāmgundam","Rāmjībanpur","Rāmnagar","Rāmnagar","Rāmnagar","Rāmpur","Rāmpur","Rāmpura","Rāmpura","Rāmtek","Rāmāpuram","Rānia","Rānikhet","Rānipet","Rānāghāt","Rānāvāv","Rānībennur","Rānīganj","Rānīpur","Rāpar","Rāth","Rāver","Rāwatbhāta","Rāwatsār","Rāya","Rāyachoti","Rāyadrug","Rāzampeta","Rāzām","Rīngas","Rūdarpur","Sabalgarh","Sadalgi","Sadashivpet","Sadābād","Safidon","Safīpur","Sagauli","Saharsa","Sahaspur","Sahaswān","Sahāranpur","Sahāwar","Saidpur","Saiha","Saint Thomas Mount","Sainthia","Sakleshpur","Saktī","Salem","Salāya","Sambalpur","Sambhal","Samdari","Samrāla","Samthar","Samālkha","Samāstipur","Sanaur","Sancoale","Sandīla","Sandūr","Sangamner","Sangariā","Sangod","Sangrūr","Sangāreddi","Sankeshwar","Sanāwad","Saoner","Saraipali","Sarauli","Sardhana","Sardulgarh","Sardārshahr","Sarkhej","Sarwār","Sarāi Mīr","Sarāi Ākil","Satara","Sathupalli","Sathyamangalam","Satna","Sattenapalle","Sattur","Satānā","Saugor","Saundatti","Sausar","Savanūr","Savarkundla","Sawāi Mādhopur","Secunderabad","Sehore","Selu","Sendhwa","Seohāra","Seondha","Seoni","Seoni Mālwa","Seram","Serchhīp","Serilingampalle","Shahbazpur","Shahdol","Shamsābād","Shamsābād","Shegaon","Sheikhpura","Sheoganj","Sheohar","Sheopur","Sherghāti","Sherkot","Shertallai","Shiggaon","Shikohābād","Shikārpur","Shikārpūr","Shillong","Shimla","Shimoga","Shiraguppi","Shirdi","Shirhatti","Shirpur","Shivaji Nagar","Shivpuri","Sholinghur","Shorāpur","Shrīgonda","Shrīrangapattana","Shrīrāmpur","Shujālpur","Shyamnagar","Shāhganj","Shāhi","Shāhjānpur","Shāhpur","Shāhpur","Shāhpur","Shāhpur","Shāhpura","Shāhpura","Shāhābād","Shāhābād","Shāhābād","Shāhābād","Shāhāda","Shājāpur","Shāmgarh","Shāmli","Shāntipur","Shīshgarh","Shōranūr","Sibsāgar","Siddhapur","Siddipet","Sidhaulī","Sidhi","Sidlaghatta","Sihor","Sihorā","Sijua","Sikandarpur","Sikandarābād","Sikandra Rao","Sikka","Silao","Silapathar","Silchar","Siliguri","Sillod","Silvassa","Simdega","Sindgi","Sindhnūr","Singarāyakonda","Singrauli","Singur","Singānallūr","Singāpur","Sinnar","Sirhind","Sirohi","Sironj","Sirsa","Sirsi","Sirsi","Sirsilla","Sirsāganj","Siruguppa","Sirumugai","Sirūr","Sisauli","Siswā Bāzār","Sitārganj","Siuri","Sivaganga","Sivagiri","Sivagiri","Sivakasi","Siwān","Siwāna","Sohna","Sohāgpur","Sojat","Sojītra","Solan","Solāpur","Someshwar","Sompeta","Sonepur","Songadh","Sonāmukhi","Sonāri","Sonīpat","Sopur","Sorada","Soro","Soron","Soygaon","Soyībug","Sri Dūngargarh","Sri Mādhopur","Srikakulam","Srinagar","Srirāmpur","Srivaikuntam","Srivilliputhur","Srīnagar","Srīnivāspur","Srīperumbūdūr","Srīrāmnagar","Srīsailain","Srīvardhan","Suket","Sultanpur","Sultānpur","Sulur","Sulya","Sundargarh","Sundarnagar","Sunel","Sunām","Supaul","Surat","Surendranagar","Suriānwān","Suriāpet","Suār","Sādri","Sāgar","Sāhibganj","Sālūmbar","Sālūr","Sāmalkot","Sāmba","Sāmbhar","Sānand","Sānchor","Sāndi","Sāngli","Sāngola","Sānkrāil","Sārangpur","Sāsvad","Sāvantvādi","Sāvda","Sāyla","Sīkar","Sīra","Sīrkāzhi","Sītāmarhi","Sītāpur","Sūjāngarh","Sūlūru","Sūrajgarh","Sūrandai","Sūratgarh","Takhatgarh","Takhatpur","Talegaon Dābhāde","Taleigao","Talipparamba","Taloda","Talwandi Bhai","Talwāra","Talāja","Tambaram","Tamlūk","Tanakpur","Tanjore","Tanuku","Tarakeswar","Tarikere","Tarn Tāran","Tarāna","Teghra","Tehri","Tekkalakote","Tekkali","Tekāri","Telhāra","Tellicherry","Teni","Teonthar","Terdāl","Tezpur","Thakurdwara","Tharangambadi","Tharād","Thenkasi","Thiruthani","Thiruvananthapuram","Thiruvarur","Thoothukudi","Thoubāl","Thrissur","Thākurganj","Thān","Thāna Bhawan","Thāne","Thānesar","Thāsra","Tijāra","Tilhar","Tindivanam","Tinnanūr","Tinsukia","Tiptūr","Tiruchchendur","Tiruchengode","Tiruchirappalli","Tirukkoyilur","Tirumala","Tirunelveli","Tirupati","Tirupparangunram","Tiruppur","Tiruppuvanam","Tirur","Tiruttangal","Tiruvalla","Tiruvallur","Tiruvannāmalai","Tiruvottiyūr","Tisaiyanvilai","Titlāgarh","Titāgarh","Todabhim","Todaraisingh","Tohāna","Tondi","Tonk","Tuensang","Tufānganj","Tuljāpur","Tulsīpur","Tumkūr","Tumsar","Tuni","Tura","Turaiyūr","Tādepalle","Tādepallegūdem","Tādpatri","Tājpur","Tāki","Tālcher","Tālīkota","Tānda","Tāndā","Tāndūr","Tāoru","Tāramangalam","Tārānagar","Tāsgaon","Tīkamgarh","Tīrthahalli","Tūndla","Udaipur","Udaipur","Udaipur","Udaipura","Udalguri","Udangudi","Udgīr","Udhampur","Udumalaippettai","Udupi","Ujhāni","Ujjain","Ulhasnagar","Ullal","Umarga","Umaria","Umarkhed","Umarkot","Umred","Umreth","Un","Una","Una","Unhel","Unjha","Unnāo","Upleta","Uppal Kalan","Uran","Uravakonda","Usehat","Usilampatti","Utraula","Uttamapālaiyam","Uttarkāshi","Uttiramerūr","V.S.K.Valasai (Dindigul-Dist.)","Vadakku Valliyūr","Vadakku Viravanallur","Vadamadurai","Vadigenhalli","Vadlapūdi","Vadnagar","Vadodara","Vaijāpur","Vaikam","Valabhīpur","Vallabh Vidyanagar","Valparai","Valsād","Vandavāsi","Vaniyambadi","Vapi","Varanasi","Varangaon","Varkala","Vasa","Vasco da Gama","Vasind","Vattalkundu","Vayalār","Vedaraniyam","Vejalpur","Vellore","Velur","Vemalwāda","Venkatagiri","Vepagunta","Verāval","Vetapālem","Vettaikkaranpudur","Vettūr","Vidisha","Vijayawada","Vijāpur","Vikārābād","Villupuram","Vinukonda","Virajpet","Virudunagar","Virār","Visakhapatnam","Visnagar","Vite","Vizianagaram","Vriddhāchalam","Vrindāvan","Vuyyūru","Vyāra","Vāda","Vādippatti","Vāsudevanallūr","Vīsāvadar","Wai","Walajapet","Wani","Wanparti","Warangal","Wardha","Warora","Warud","Wazīrganj","Wellington","Wer","Wokha","Wādi","Wānkāner","Wāris Alīganj","Wārāseonī","Wāshīm","Yamunānagar","Yanam","Yanamalakuduru","Yavatmāl","Yelahanka","Yellandu","Yellāpur","Yeola","Yādgīr","Yāval","Zahirābād","Zaidpur","Zamānia","Zira","Zunheboto","Ābu","Ābu Road","Ādilābād","Ādoni","Ālangulam","Āmli","Āmlāgora","Āmta","Āndippatti","Ārangaon","Āron","Ārvi","Āsandh","Āsansol","Āsika","Āsind","Āthagarh","Āvadi","Ūn"]}] jo-1.9/tests/jo.20.sh000066400000000000000000000004671433077231700143370ustar00rootroot00000000000000# read json array elements echo '{"a":1,"b":"val"}' > $$.1 echo '{"a":478,"b":"other"}' > $$.2 ${JO:-jo} -a :$$.1 :$$.2 rm -f $$.1 rm -f $$.2 # read large json array elements ${JO:-jo} -a < ${srcdir:=.}/tests/jo-large1.json ${JO:-jo} -a :${srcdir:=.}/tests/jo-large2.json :${srcdir:=.}/tests/jo-large1.json jo-1.9/tests/jo.21.exp000066400000000000000000000001311433077231700145060ustar00rootroot00000000000000{"nested":{"a":1,"b":"val"}} {"top":{"obj1":{"c":3,"d":"key"},"obj2":{"a":1,"b":"val"}}} jo-1.9/tests/jo.21.sh000066400000000000000000000002771433077231700143370ustar00rootroot00000000000000# read nested json elements echo '{"a":1,"b":"val"}' > $$.1 ${JO:-jo} nested=:$$.1 # nested json within object path ${JO:-jo} -d . top.obj1.c=3 top.obj1.d="key" top.obj2=:$$.1 rm -f $$.1 jo-1.9/tests/jo.22.exp000066400000000000000000000000251433077231700145110ustar00rootroot00000000000000{"key":"@timestamp"} jo-1.9/tests/jo.22.sh000066400000000000000000000001061433077231700143270ustar00rootroot00000000000000# avoid reading from file if escaped \@ ${JO:-jo} key="\@timestamp" jo-1.9/tests/jo.23.exp000066400000000000000000000001361433077231700145150ustar00rootroot00000000000000{"foo":null} {} {"foo":1,"bar":null,"baz":3} {"foo":1,"baz":3} {"list":[1,null]} {"list":[1]} jo-1.9/tests/jo.23.sh000066400000000000000000000003251433077231700143330ustar00rootroot00000000000000# disable creation of null-valued keys ${JO:-jo} foo= ${JO:-jo} -n foo= ${JO:-jo} foo=1 bar= baz=3 ${JO:-jo} -n foo=1 bar= baz=3 nothing= ${JO:-jo} list[]=1 list[]=$nothing ${JO:-jo} -n list[]=1 list[]=$nothing jo-1.9/tests/jo.24.exp000066400000000000000000000004421433077231700145160ustar00rootroot00000000000000[1,2,3,4,6,8] {"a":1,"b":2,"c":42,"d":3} {"a":1,"b":2,"c":42,"d":3,"stage":{"1":"a","2":"b"}} {"a":1,"b":2,"c":42,"d":3,"stage":{"1":"a","2":"b"}} {"a":1,"b":2,"c":42,"d":3,"stage":{"1":"a","2":"b"}} {"a":1,"b":2,"stage":{"1":"a","2":"b"}} {"people":"need people"} {"people":"need people"} jo-1.9/tests/jo.24.sh000066400000000000000000000014761433077231700143440ustar00rootroot00000000000000# jo as filter # filter array echo "[1,2,3,4]" | ${JO:-jo} -f - 6 8 # filter object ${JO:-jo} a=1 b=2 | ${JO:-jo} -f - c=42 d=3 # multi-stage pipeline ${JO:-jo} a=1 b=2 | ${JO:-jo} -f - c=42 d=3 | ${JO:-jo} -f - -d . stage.1=a stage.2=b # filter from file tmp=/tmp/jo.filter.$$ trap "rm -f $tmp; exit" 0 1 2 15 ${JO:-jo} a=1 b=2 > $tmp ${JO:-jo} -f $tmp c=42 d=3 | ${JO:-jo} -f - -d . stage.1=a stage.2=b # take initial object from file, and mods from stdin echo "c=42 d=3" | ${JO:-jo} -f $tmp | ${JO:-jo} -f - -d . stage.1=a stage.2=b # this command should NOT output keys "c" and "d" ${JO:-jo} a=1 b=2 | ${JO:-jo} -f - c=42 d=3 | ${JO:-jo} -f $tmp -d . stage.1=a stage.2=b # filter non-collections (input basically ignored) echo hi | tee $tmp | ${JO:-jo} -f - people="need people" ${JO:-jo} -f $tmp people="need people" jo-1.9/tests/jo.25.exp000066400000000000000000000003351433077231700145200ustar00rootroot00000000000000{"a":1,"b":2,"a":3} {"a":3,"b":2} {"stage":{"1":"a","2":"b","3":"c","2":"x","3":"y","4":"d","1":"h"},"down":"up"} {"stage":{"1":"h","2":"x","3":"y","4":"d"},"down":"up"} {"name":"aaa"} {"name":"cba","another_name":"abc"} jo-1.9/tests/jo.25.sh000066400000000000000000000007311433077231700143360ustar00rootroot00000000000000# overwrite values of existing object keys ${JO:-jo} a=1 b=2 a=3 ${JO:-jo} -D a=1 b=2 a=3 tmp=`${JO:-jo} 1=a 2=b 3=c` ${JO:-jo} -d . stage="$tmp" down=up stage.2=x stage\[3\]=y stage.4=d stage\[1\]=h ${JO:-jo} -D -d . stage="$tmp" down=up stage.2=x stage\[3\]=y stage.4=d stage\[1\]=h # dedup filter input too tmpf=$$.json trap 'rm -f "$tmpf"' 0 1 2 15 ${JO:-jo} name=aaa name=aaa | tee $tmpf | ${JO:-jo} -D -f - name=aaa ${JO:-jo} -D -f $tmpf another_name=abc name=cba jo-1.9/tests/jo.26.exp000066400000000000000000000000331433077231700145140ustar00rootroot00000000000000{"file":"stdin","jo":true} jo-1.9/tests/jo.26.sh000066400000000000000000000001071433077231700143340ustar00rootroot00000000000000# read from stdin echo '{"file":"stdin", "jo": true}' | ${JO:-jo} -f - jo-1.9/tests/jo.27.exp000066400000000000000000000002601433077231700145170ustar00rootroot00000000000000{"b":[1,2]} jo: JSON_ERR: Cannot add {"a":3} to non-object [1] Test 2 should fail {"d":{"m":10,"n":20}} jo: JSON_ERR: Cannot append 20 to non-array {"m":10} Test 4 should fail jo-1.9/tests/jo.27.sh000066400000000000000000000002741433077231700143420ustar00rootroot00000000000000# user-friendly errors ${JO:-jo} b[]=1 b[]=2 ${JO:-jo} b[]=1 b[a]=3 2>&1 || echo "Test 2 should fail" ${JO:-jo} d[m]=10 d[n]=20 ${JO:-jo} d[m]=10 d[]=20 2>&1 || echo "Test 4 should fail" jo-1.9/tests/jo.test000077500000000000000000000020061433077231700144560ustar00rootroot00000000000000#!/bin/sh # input file (jo.??.in) is a shell script; the first line must # be a comment and is used as the name of the test: # # # basic logo # $JO -a jo # # the expect file (jo.??.exp) is a file which will be diff'd # against the output of the corresponding .in file: # # ["jo"] # JO="$(pwd)/jo" TESTDIR="${0%/*}" NTESTS=$(expr $(ls $TESTDIR/jo.??.sh 2>/dev/null | wc -l)) export JO export TESTDIR echo "1..$NTESTS" # Number of tests to be executed. n=0 for t in $TESTDIR/jo.??.sh; do n=$(expr $n + 1) input=$t expected="$TESTDIR/$(basename $t .sh).exp" output=$(mktemp /tmp/jo.XXXXXX) title=$(head -1 $input | sed -e 's/^#//') sh $input > $output RC=$? if [ $RC -ne 0 ]; then echo "not ok $n - $title" rm -f $output continue fi # if self-contained test (i.e. no 'expected' file exists) # use the previous RC if test -f "$expected"; then diff "$output" "$expected" > /dev/null RC=$? fi status="ok" if [ $RC -ne 0 ]; then status="not ok" fi echo "$status $n - $title" rm -f $output done