An open API service indexing awesome lists of open source software.

https://github.com/themactep/jct

JSON configuration CLI tool
https://github.com/themactep/jct

Last synced: 29 days ago
JSON representation

JSON configuration CLI tool

Awesome Lists containing this project

README

          

# JSON Configuration CLI Tool

A command-line tool for reading and writing JSON configuration files.
This tool is written in pure C with no external dependencies, making
it suitable for cross-compilation and embedded systems.

## Features

- Read values from JSON configuration files using dot notation
- Write values to JSON configuration files using dot notation
- Import values from one JSON file into another via deep merge
- Export differences between two JSON files (useful for OverlayFS systems)
- Create new JSON configuration files
- Print entire JSON configuration files
- Restore configuration files to original state (OverlayFS support)
- Support for nested objects and arrays
- Support for various data types (strings, numbers, booleans, null)
- Pretty-printing of JSON output

## Building

### Native Compilation

To build the project for your local machine, run:

```bash
make
```

This will compile the source files and create the `jct` executable.

### Cross-Compilation

#### Toolchain Selection

The Makefile supports multiple toolchains using the standard `CROSS_COMPILE` prefix:

```bash
# Use prefix for a toolchain found in PATH
make CROSS_COMPILE=mipsel-linux-gnu-

# Use any custom toolchain with a full path
make CROSS_COMPILE=/path/to/toolchain/bin/prefix-
```

You can also use these toolchain options with the release target:

```bash
make CROSS_COMPILE=/path/to/toolchain/bin/mipsel-linux-musl- release
make CROSS_COMPILE=mipsel-linux-gnu- release
```

### Optimized Builds

For optimized builds with smaller binary size:

```bash
make release # Optimized build (stripped of debug info)
```

The optimized builds use the following techniques to reduce binary size:
- `-Os` optimization flag for size
- Removal of unused code with `-ffunction-sections` and `-fdata-sections`
- Stripping of debug information

### Cleaning

To clean up the build artifacts:

```bash
make clean # Remove object files and executables
make distclean # Remove all generated files (complete cleanup)
```

## Usage

```
Usage: jct [--trace-resolve] [options]

Commands:
get Get a value from the config file
set Set a value in the config file
import Merge values from another JSON file
export []
Export differences to stdout
create Create a new empty config file
print Print the entire config file
restore Restore config file to original state (OverlayFS)

Options:
--trace-resolve Trace short-name resolution steps (get/set/import/print/restore)

Short-name resolution (when has no '/' and does not end with .json):
Tries, in order: ./, ./.json, /etc/.json (POSIX only)
- If none found: exit 2 with an error listing tried paths
- If a candidate exists but is not readable: exit 13 (do not try later candidates)
- Symlinks are followed; final target must be a regular file (directories are skipped)

Creation rules:
- create requires an explicit path
- set may create a new file only with an explicit path
- import follows the same rule as set for its destination file; use an explicit path to create a new config
- Using a short name that does not resolve will not create; the command exits 2 with guidance

Examples:
jct prudynt get server.host Resolve short name 'prudynt' and read
jct prudynt set app.name "My App" Resolve and update existing; to create, use explicit path
jct ./prudynt set app.name "My App" Explicit path; allowed to create
jct config.json print Print the entire config file
jct /etc/prudynt.json export > diff.json
Export differences (compares with /rom version)
jct modified.json export base.json > diff.json
Export differences between two files
```
### Exit codes

- 0: Success
- 2: Not found
- Short name did not resolve to any candidate (get/print/restore/set)
- Short name used with create (and with set when it would create) — use an explicit path instead
- 13: Permission denied
- A candidate file was found during short-name resolution but is not readable; later candidates are not tried

Note: The restore command defines additional exit codes specific to OverlayFS operations; see the Restore section below for details.

### Short-name resolution details

When is a short name (no path separators and no .json extension), jct searches for a JSON file deterministically:

1. ./
2. ./.json
3. /etc/.json (POSIX systems only)

Rules:
- If a candidate does not exist, jct continues to the next.
- If a candidate exists and is a directory, it is skipped.
- If a candidate exists, symlinks are followed and the final target must be a regular file.
- If a candidate is a regular file but not readable, jct exits immediately with code 13 (permission denied) and does not try later candidates.
- If no candidate is selected, jct exits with code 2 (not found) and prints:
jct: no JSON file found for ''; tried: ./, ./.json[, /etc/.json]

Creation behavior:
- create requires an explicit path; short names are not accepted for creation and will return code 2 with guidance.
- set may create a new file only when invoked with an explicit path; using a short name that does not resolve will return code 2 and advise supplying an explicit path (e.g., ./.json).

Tip:
- Use --trace-resolve to print the resolution steps and the final chosen path to stderr.

### Examples

#### Creating a new configuration file

```bash
./jct new_config.json create
```

#### Setting values in a configuration file

```bash
./jct config.json set server.host localhost
./jct config.json set server.port 8080
./jct config.json set server.ssl true
./jct config.json set app.name "My Application"
./jct config.json set app.version 1.0
```

#### Importing values from another JSON file

```bash
./jct base_config.json import overrides.json
```

The `import` command deep-merges the source file into the destination:

- Nested objects are merged recursively so you can override just a few keys
- Arrays or non-object values from the source replace the destination value entirely
- Any keys that exist only in the source are added to the destination

This makes it easy to check in small overlay files (for example, only `image.hflip`
and `image.vflip`) and import them into a larger device profile in one step.

#### Exporting differences between JSON files

```bash
./jct /etc/config.json export > differences.json
```

The `export` command compares two JSON files and outputs only the differences (keys and values that differ or exist only in the modified file):

- When called with an absolute path (e.g., `/etc/config.json`), it defaults to comparing with `/rom/` (for OverlayFS systems)
- You can specify a custom original file to compare against:
```bash
./jct modified.json export base.json > diff.json
```
- The output is a JSON object containing only the modified or added keys
- This is useful for creating minimal overlay configurations or tracking what has changed from a baseline

**Usage patterns:**

```bash
# Export differences from an OverlayFS system (compares /etc/config.json with /rom/etc/config.json)
./jct /etc/config.json export > changes.json

# Export differences between any two JSON files
./jct current.json export original.json > diff.json

# Use the output to create a minimal overlay file
./jct current.json export baseline.json > overlay.json
./jct new_device.json import overlay.json
```

#### JSONPath queries (new)

Query JSON data using Goessner JSONPath.

Examples:

- jct books.json path "$..author" --mode values
- jct books.json path "$.store.book[?(@.price < 10)].title"
- jct books.json path "$.store.book[0:3]" --mode pairs

Options:

- --mode values|paths|pairs (default: values)
- --limit N to stop after N matches
- --strict causes parse/eval errors to exit nonzero (2 parse, 3 eval); default lenient emits [] and warns to stderr
- --pretty pretty-prints JSON output
- --unwrap-single when mode=values, emit the lone value instead of [value]

#### Getting values from a configuration file

```bash
./jct config.json get server.host
# Output: localhost

./jct config.json get server.port
# Output: 8080

./jct config.json get app.name
# Output: My Application
```

#### Printing the entire configuration file

```bash
./jct config.json print
```

#### Restoring a configuration file to its original state (OverlayFS)

```bash
./jct /etc/config.json restore
```

This command is designed for embedded systems using OverlayFS. It:
1. **Requires an absolute path** (must start with '/') for the config file
2. Validates that the original file exists in `/rom/`
3. Checks that a modified version exists in `/overlay/`
4. Removes the overlay file to expose the original ROM version
5. Remounts the overlay filesystem to apply changes

**Important:** The config file path must be absolute. Relative paths are not accepted.

**Examples:**
```bash
./jct /etc/prudynt.json restore # ✓ Valid - absolute path
./jct /opt/app/config.json restore # ✓ Valid - absolute path
./jct prudynt.json restore # ✗ Invalid - relative path
./jct ./config.json restore # ✗ Invalid - relative path
```

**Exit codes:**
- 0: Success - file restored to original state
- 1: Original ROM file not found
- 2: File is already original (no overlay to remove)
- 3: Failed to remove overlay file
- 4: Failed to remount overlay filesystem
- 5: Invalid arguments or non-absolute path

## Project Structure

- `src/json_config.h` - Header file with type definitions and function declarations
- `src/json_value.c` - Implementation of JSON value handling functions
- `src/json_parse.c` - Implementation of JSON parsing functions
- `src/json_serialize.c` - Implementation of JSON serialization functions
- `src/json_config.c` - Implementation of configuration manipulation functions
- `src/json_config_cli.c` - Main file with CLI interface
- `Makefile` - Build configuration

## License

This project is open source and available under the MIT License.