All features available
This commit is contained in:
parent
854859bb8a
commit
88f80019ef
|
@ -4,7 +4,7 @@ project(caesiumclt)
|
|||
# The version number.
|
||||
set(VERSION_MAJOR 0)
|
||||
set(VERSION_MINOR 10)
|
||||
set(VERSION_PATCH 0)
|
||||
set(VERSION_PATCH 1)
|
||||
|
||||
configure_file(
|
||||
"src/config.h.in"
|
||||
|
|
40
INSTALL
40
INSTALL
|
@ -1,35 +1,10 @@
|
|||
Installation Instructions
|
||||
Compile instructions
|
||||
*************************
|
||||
|
||||
Requirements
|
||||
==================
|
||||
You will need both mozjpeg (libjpeg-turbo will work too) and zopflipng compiled as libraries to be able to
|
||||
compile CaesiumCLT. Refer to their own documentation for the detailed
|
||||
instructions.
|
||||
|
||||
This is the step-by-step sequence tested on Ubuntu 16.04 LTS:
|
||||
|
||||
mozjpeg
|
||||
|
||||
$ git clone https://github.com/mozilla/mozjpeg
|
||||
$ cd mozjpeg/
|
||||
$ autoreconf -fiv
|
||||
$ mkdir build && cd build
|
||||
$ ../configure
|
||||
$ make && sudo make install
|
||||
|
||||
Note that on some systems you have to run $ autoreconf -fiv twice.
|
||||
|
||||
zopflipng
|
||||
|
||||
$ git clone https://github.com/google/zopfli.git
|
||||
$ cd zopfli
|
||||
$ make libzopflipng
|
||||
$ sudo cp libzopflipng.so.1.0.0 /usr/lib
|
||||
$ sudo ln -s libzopflipng.so.1.0.0 /usr/lib/libzopflipng.so
|
||||
$ sudo ln -s libzopflipng.so.1.0.0 /usr/lib/libzopflipng.so.1
|
||||
$ sudo mkdir /usr/include/zopflipng
|
||||
$ sudo cp src/zopflipng/zopflipng_lib.h /usr/include/zopflipng
|
||||
CaesiumCLT is based on libcaesium (https://github.com/Lymphatus/libcaesium) and requires it to be compiled.
|
||||
Please refer to its own documentation.
|
||||
|
||||
Basic Installation
|
||||
==================
|
||||
|
@ -39,17 +14,16 @@ Unpack the archive (if not cloned from GIT), enter the directory
|
|||
and compile the program.
|
||||
The sequence should be something like:
|
||||
|
||||
$ tar xfv caesiumclt-*.tar.gz
|
||||
$ cd caesiumclt-*
|
||||
$ tar xfv caesium-clt-*.tar.gz
|
||||
$ cd caesium-clt-*
|
||||
$ cmake .
|
||||
$ make
|
||||
$ sudo make install
|
||||
|
||||
Cloning from git
|
||||
==================
|
||||
$ git clone https://github.com/Lymphatus/CaesiumCLT
|
||||
$ cd CaesiumCLT
|
||||
$ autoreconf -fiv
|
||||
$ git clone https://github.com/Lymphatus/caesium-clt
|
||||
$ cd caesium-clt
|
||||
$ cmake .
|
||||
$ make
|
||||
$ sudo make install
|
||||
|
|
16
README.md
16
README.md
|
@ -1,5 +1,5 @@
|
|||
## Caesium CommandLineTools
|
||||
##### caesium-clt - v0.10.0-beta (build 20170307) - Copyright © Matteo Paonessa, 2017. All Rights Reserved.
|
||||
##### caesium-clt - v0.10.1-beta (build 20170310) - Copyright © Matteo Paonessa, 2017. All Rights Reserved.
|
||||
|
||||
----------
|
||||
|
||||
|
@ -13,13 +13,13 @@
|
|||
----------
|
||||
|
||||
###### TESTED PLATFORMS
|
||||
* Mac OS X Sierra (v10.12.1)
|
||||
* Arch Linux
|
||||
* Mac OS X Sierra (v10.12.3)
|
||||
* Ubuntu 16.04
|
||||
* Windows 10
|
||||
|
||||
----------
|
||||
|
||||
###### INSTALLATION
|
||||
###### COMPILATION
|
||||
See INSTALL for more details.
|
||||
|
||||
----------
|
||||
|
@ -46,15 +46,21 @@ Losslessly compress ```Pictures``` folder and subfolders, located in the ```home
|
|||
$ caesiumclt -q 0 -R -o ~/output/ ~/Pictures
|
||||
```
|
||||
|
||||
Losslessly compress ```Pictures``` folder and subfolders, located in the ```home``` directory, into a folder called ```output``` retaining the input folder structure
|
||||
```
|
||||
$ caesiumclt -q 0 -RS-o ~/output/ ~/Pictures
|
||||
```
|
||||
|
||||
----------
|
||||
|
||||
###### TODO
|
||||
* Code cleaning
|
||||
* Keep folder structure
|
||||
* Deeper error handling
|
||||
|
||||
----------
|
||||
|
||||
###### CHANGELOG
|
||||
* 0.10.1-beta - All features are available
|
||||
* 0.10.0-beta - Switched to cmake build system and libcaesium
|
||||
* 0.9.1-beta - Initial development stage
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ FILE(GLOB CHeaders *.h)
|
|||
|
||||
add_executable(caesiumclt ${CSources} ${CHeaders})
|
||||
|
||||
find_library(caesium caesium /usr/local/lib)
|
||||
target_link_libraries(caesiumclt LINK_PUBLIC caesium)
|
||||
|
||||
install(TARGETS caesiumclt DESTINATION bin)
|
|
@ -0,0 +1,45 @@
|
|||
#include <stdio.h>
|
||||
#include <caesium.h>
|
||||
|
||||
#include "error.h"
|
||||
|
||||
void display_error(error_level level, int code)
|
||||
{
|
||||
char *error_level = ((level) ? "[WARNING]" : "[ERROR]");
|
||||
fprintf(stderr, "%s %d: %s\n",
|
||||
error_level,
|
||||
code,
|
||||
get_error_message(code));
|
||||
}
|
||||
|
||||
const char *get_error_message(int code)
|
||||
{
|
||||
switch (code) {
|
||||
//Generic errors
|
||||
case 1:
|
||||
return "Invalid quality value. Must be between [0-100].";
|
||||
case 2:
|
||||
return "Unrecognized option.";
|
||||
case 3:
|
||||
return "Empty input folder.";
|
||||
case 4:
|
||||
return "Cannot keep folder structure providing multiple input files.";
|
||||
case 5:
|
||||
return "Cannot create output folder.";
|
||||
case 6:
|
||||
return "Cannot check if is a directory.";
|
||||
case 7:
|
||||
return "Cannot calculate file size";
|
||||
case 8:
|
||||
return "Input folder provided. Skipping all other inputs.";
|
||||
case 9:
|
||||
return "Input files provided. Cannot mix them with a folder.";
|
||||
case 10:
|
||||
return "-R is useless on files.";
|
||||
case 11:
|
||||
return "-S is useless without -R.";
|
||||
|
||||
default:
|
||||
return "Unrecognized error.";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#ifndef CAESIUMCLT_ERROR_H
|
||||
#define CAESIUMCLT_ERROR_H
|
||||
|
||||
void display_error(error_level level, int code);
|
||||
|
||||
const char *get_error_message(int code);
|
||||
|
||||
#endif //CAESIUMCLT_ERROR_H
|
93
src/helper.c
93
src/helper.c
|
@ -5,12 +5,13 @@
|
|||
#include "optparse.h"
|
||||
#include "utils.h"
|
||||
#include "config.h"
|
||||
#include "error.h"
|
||||
|
||||
cclt_options parse_arguments(char **argv, cs_image_pars *options)
|
||||
{
|
||||
struct optparse opts;
|
||||
//Initialize application options
|
||||
cclt_options result = {NULL, NULL, false, false, 0, 0, 0};
|
||||
cclt_options parameters = {NULL, NULL, false, false, 0, 0, 0};
|
||||
|
||||
//Parse command line args
|
||||
optparse_init(&opts, argv);
|
||||
|
@ -31,7 +32,7 @@ cclt_options parse_arguments(char **argv, cs_image_pars *options)
|
|||
case 'q':
|
||||
options->jpeg.quality = atoi(opts.optarg);
|
||||
if (options->jpeg.quality < 0 || options->jpeg.quality > 100) {
|
||||
//TODO Trigger a error
|
||||
display_error(ERROR, 1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
|
@ -40,18 +41,18 @@ cclt_options parse_arguments(char **argv, cs_image_pars *options)
|
|||
break;
|
||||
case 'o':
|
||||
if (opts.optarg[strlen(opts.optarg) - 1] == '/') {
|
||||
result.output_folder = malloc((strlen(opts.optarg) + 1) * sizeof(char));
|
||||
snprintf(result.output_folder, strlen(opts.optarg) + 1, "%s", opts.optarg);
|
||||
parameters.output_folder = malloc((strlen(opts.optarg) + 1) * sizeof(char));
|
||||
snprintf(parameters.output_folder, strlen(opts.optarg) + 1, "%s", opts.optarg);
|
||||
} else {
|
||||
result.output_folder = malloc((strlen(opts.optarg) + 2) * sizeof(char));
|
||||
snprintf(result.output_folder, strlen(opts.optarg) + 2, "%s/", opts.optarg);
|
||||
parameters.output_folder = malloc((strlen(opts.optarg) + 2) * sizeof(char));
|
||||
snprintf(parameters.output_folder, strlen(opts.optarg) + 2, "%s/", opts.optarg);
|
||||
}
|
||||
break;
|
||||
case 'R':
|
||||
result.recursive = true;
|
||||
parameters.recursive = true;
|
||||
break;
|
||||
case 'S':
|
||||
result.keep_structure = true;
|
||||
parameters.keep_structure = true;
|
||||
break;
|
||||
case 'v':
|
||||
fprintf(stdout, "%d.%d.%d\n", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH);
|
||||
|
@ -62,6 +63,7 @@ cclt_options parse_arguments(char **argv, cs_image_pars *options)
|
|||
case '?':
|
||||
default:
|
||||
fprintf(stderr, "%s: %s\n", argv[0], opts.errmsg);
|
||||
display_error(ERROR, 2);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
@ -70,27 +72,48 @@ cclt_options parse_arguments(char **argv, cs_image_pars *options)
|
|||
char *arg;
|
||||
bool files_flag = false, folders_flag = false;
|
||||
while ((arg = optparse_arg(&opts))) {
|
||||
if (folders_flag) {
|
||||
display_error(WARNING, 8);
|
||||
continue;
|
||||
}
|
||||
//Check if it's a directory and add its content
|
||||
if (is_directory(arg)) {
|
||||
if (!files_flag) {
|
||||
folders_flag = true;
|
||||
parameters.input_folder = strdup(arg);
|
||||
int count = 0;
|
||||
count = scan_folder(arg, &result, result.recursive);
|
||||
count = scan_folder(arg, ¶meters, parameters.recursive);
|
||||
if (count == 0) {
|
||||
//TODO Trigger a warning
|
||||
display_error(WARNING, 3);
|
||||
}
|
||||
|
||||
} else {
|
||||
result.input_files = realloc(result.input_files, (result.files_count + 1) * sizeof(char *));
|
||||
result.input_files[result.files_count] = malloc((strlen(arg) + 1) * sizeof(char));
|
||||
snprintf(result.input_files[result.files_count], strlen(arg) + 1, "%s", arg);
|
||||
result.files_count++;
|
||||
display_error(WARNING, 9);
|
||||
}
|
||||
} else {
|
||||
files_flag = true;
|
||||
parameters.input_folder = NULL;
|
||||
parameters.input_files = realloc(parameters.input_files, (parameters.files_count + 1) * sizeof(char *));
|
||||
parameters.input_files[parameters.files_count] = malloc((strlen(arg) + 1) * sizeof(char));
|
||||
snprintf(parameters.input_files[parameters.files_count], strlen(arg) + 1, "%s", arg);
|
||||
parameters.files_count++;
|
||||
}
|
||||
}
|
||||
|
||||
//If there're files and folders, we cannot keep the structure
|
||||
//TODO Trigger a warning
|
||||
result.keep_structure = !(files_flag && folders_flag);
|
||||
if (parameters.recursive && !folders_flag) {
|
||||
display_error(WARNING, 10);
|
||||
parameters.recursive = false;
|
||||
}
|
||||
if (!parameters.recursive && parameters.keep_structure) {
|
||||
display_error(WARNING, 11);
|
||||
parameters.keep_structure = false;
|
||||
}
|
||||
if (parameters.keep_structure && (!folders_flag && parameters.files_count > 1)) {
|
||||
display_error(WARNING, 4);
|
||||
parameters.keep_structure = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
return parameters;
|
||||
}
|
||||
|
||||
int start_compression(cclt_options *options, cs_image_pars *parameters)
|
||||
|
@ -99,17 +122,36 @@ int start_compression(cclt_options *options, cs_image_pars *parameters)
|
|||
off_t input_file_size = 0;
|
||||
off_t output_file_size = 0;
|
||||
//TODO Support folder structure
|
||||
|
||||
//Create the output folder if does not exists
|
||||
if (mkpath(options->output_folder, 0777) == -1) {
|
||||
//TODO Error
|
||||
display_error(ERROR, 5);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
for (int i = 0; i < options->files_count; i++) {
|
||||
char *filename = get_filename(options->input_files[i]);
|
||||
char *output_full_path = malloc((strlen(filename) + strlen(options->output_folder) + 1) * sizeof(char));
|
||||
snprintf(output_full_path, (strlen(filename) + strlen(options->output_folder) + 1), "%s%s", options->output_folder, filename);
|
||||
char *output_full_path;
|
||||
//If we don't need to keep the structure, we put all the files in one folder by just the filename
|
||||
if (!options->keep_structure) {
|
||||
output_full_path = malloc((strlen(filename) + strlen(options->output_folder) + 1) * sizeof(char));
|
||||
snprintf(output_full_path, (strlen(filename) + strlen(options->output_folder) + 1), "%s%s",
|
||||
options->output_folder, filename);
|
||||
} else {
|
||||
/*
|
||||
* Otherwise, we nee to compute the whole directory structure
|
||||
* We are sure we have a folder only as input, so that's the root
|
||||
* Just compute the subfolders without the filename, make them and append the filename
|
||||
* A piece of cake <3
|
||||
*/
|
||||
|
||||
size_t index = strspn(options->input_folder, options->input_files[i]) + 1;
|
||||
size_t size = strlen(options->input_files[i]) - index - strlen(filename);
|
||||
char output_full_folder[strlen(options->output_folder) + size + 1];
|
||||
snprintf(output_full_folder, strlen(options->output_folder) + size + 1, "%s%s", options->output_folder, &options->input_files[i][index]);
|
||||
output_full_path = malloc((strlen(output_full_folder) + strlen(filename) + 1) * sizeof(char));
|
||||
snprintf(output_full_path, strlen(output_full_folder) + strlen(filename) + 1, "%s%s", output_full_folder, filename);
|
||||
mkpath(output_full_folder, 0777);
|
||||
}
|
||||
|
||||
fprintf(stdout, "(%d/%d) %s -> %s\n",
|
||||
i + 1,
|
||||
|
@ -124,9 +166,12 @@ int start_compression(cclt_options *options, cs_image_pars *parameters)
|
|||
output_file_size = get_file_size(output_full_path);
|
||||
options->output_total_size += output_file_size;
|
||||
|
||||
char *human_input_size = get_human_size(input_file_size);
|
||||
char *human_output_size = get_human_size(output_file_size);
|
||||
|
||||
fprintf(stdout, "%s -> %s [%.2f%%]\n",
|
||||
get_human_size(input_file_size),
|
||||
get_human_size(output_file_size),
|
||||
human_input_size,
|
||||
human_output_size,
|
||||
((float) output_file_size - input_file_size) * 100 / input_file_size);
|
||||
} else {
|
||||
options->input_total_size -= get_file_size(options->input_files[i]);
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
typedef struct cclt_options
|
||||
{
|
||||
char **input_files;
|
||||
char *input_folder;
|
||||
char *output_folder;
|
||||
bool recursive;
|
||||
bool keep_structure;
|
||||
|
|
21
src/main.c
21
src/main.c
|
@ -1,21 +1,21 @@
|
|||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <caesium.h>
|
||||
#include "utils.h"
|
||||
#include "helper.h"
|
||||
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
errno = 0;
|
||||
long execution_ms = 0;
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
long compression_time = 0;
|
||||
cs_image_pars compress_options;
|
||||
cclt_options options;
|
||||
|
||||
//Initialize the default parameters
|
||||
compress_options = initialize_parameters();
|
||||
//Set them according to command line parameters
|
||||
options = parse_arguments(argv, &compress_options);
|
||||
|
||||
//Start a timer before calling the compression
|
||||
clock_t start = clock(), diff;
|
||||
|
||||
|
@ -24,18 +24,19 @@ int main(int argc, char* argv[]) {
|
|||
//Cleanup the two memory allocated objects
|
||||
free(options.output_folder);
|
||||
free(options.input_files);
|
||||
free(options.input_folder);
|
||||
|
||||
//Get the difference
|
||||
diff = clock() - start;
|
||||
execution_ms = diff * 1000 / CLOCKS_PER_SEC;
|
||||
compression_time = diff * 1000 / CLOCKS_PER_SEC;
|
||||
|
||||
//Output the compression results
|
||||
|
||||
fprintf(stdout, "-------------------------------\nCompression completed in "
|
||||
fprintf(stdout, "-------------------------------\n"
|
||||
"Compression completed in "
|
||||
"%lum%lus%lums\n%s -> %s [%.2f%% | %s]\n",
|
||||
execution_ms / 1000 / 60, execution_ms / 1000 % 60, execution_ms % 1000,
|
||||
compression_time / 1000 / 60, compression_time / 1000 % 60, compression_time % 1000,
|
||||
get_human_size(options.input_total_size), get_human_size(options.output_total_size),
|
||||
((float)options.output_total_size - options.input_total_size) * 100 / options.input_total_size,
|
||||
((float) options.output_total_size - options.input_total_size) * 100 / options.input_total_size,
|
||||
get_human_size((options.output_total_size - options.input_total_size)));
|
||||
|
||||
|
||||
|
|
16
src/utils.c
16
src/utils.c
|
@ -6,6 +6,7 @@
|
|||
#include <math.h>
|
||||
#include "utils.h"
|
||||
#include "tinydir.h"
|
||||
#include "error.h"
|
||||
|
||||
|
||||
void print_help()
|
||||
|
@ -32,7 +33,7 @@ bool is_directory(const char *path)
|
|||
tinydir_file file;
|
||||
|
||||
if (tinydir_file_open(&file, path) == -1) {
|
||||
//TODO Error
|
||||
display_error(ERROR, 6);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
@ -99,7 +100,7 @@ char *get_filename(char *full_path)
|
|||
tofree = strdup(full_path);
|
||||
//TODO change to strncpy
|
||||
strcpy(tofree, full_path);
|
||||
//TODO Change on Windows
|
||||
//TODO Windows?
|
||||
while ((token = strsep(&tofree, "/")) != NULL) {
|
||||
if (tofree == NULL) {
|
||||
break;
|
||||
|
@ -116,20 +117,21 @@ off_t get_file_size(const char *path)
|
|||
tinydir_file file;
|
||||
|
||||
if (tinydir_file_open(&file, path) == -1) {
|
||||
//TODO Error
|
||||
display_error(ERROR, 7);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return file._s.st_size;
|
||||
}
|
||||
|
||||
char* get_human_size(off_t size) {
|
||||
char *get_human_size(off_t size)
|
||||
{
|
||||
//We should not get more than TB images
|
||||
char* unit[5] = {"B", "KB", "MB", "GB", "TB"};
|
||||
char *unit[5] = {"B", "KB", "MB", "GB", "TB"};
|
||||
//Index of the array containing the correct unit
|
||||
double order = floor(log2(labs(size)) / 10);
|
||||
//Alloc enough size for the final string
|
||||
char* final = (char*) malloc(((int) (floor(log10(labs(size))) + 4)) * sizeof(char));
|
||||
char *final = (char *) malloc(((int) (floor(log10(labs(size))) + 4)) * sizeof(char));
|
||||
|
||||
//If the order exceeds 4, something is fishy
|
||||
if (order > 4) {
|
||||
|
@ -137,7 +139,7 @@ char* get_human_size(off_t size) {
|
|||
}
|
||||
|
||||
//Copy the formatted string into the buffer
|
||||
sprintf(final, "%.2f %s", size / (pow(1024, order)), unit[(int)order]);
|
||||
sprintf(final, "%.2f %s", size / (pow(1024, order)), unit[(int) order]);
|
||||
//And return it
|
||||
return final;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue