All features available

This commit is contained in:
Matteo Paonessa 2017-03-10 11:46:41 +01:00
parent 854859bb8a
commit 88f80019ef
10 changed files with 164 additions and 83 deletions

View File

@ -4,7 +4,7 @@ project(caesiumclt)
# The version number. # The version number.
set(VERSION_MAJOR 0) set(VERSION_MAJOR 0)
set(VERSION_MINOR 10) set(VERSION_MINOR 10)
set(VERSION_PATCH 0) set(VERSION_PATCH 1)
configure_file( configure_file(
"src/config.h.in" "src/config.h.in"

40
INSTALL
View File

@ -1,35 +1,10 @@
Installation Instructions Compile instructions
************************* *************************
Requirements Requirements
================== ==================
You will need both mozjpeg (libjpeg-turbo will work too) and zopflipng compiled as libraries to be able to CaesiumCLT is based on libcaesium (https://github.com/Lymphatus/libcaesium) and requires it to be compiled.
compile CaesiumCLT. Refer to their own documentation for the detailed Please refer to its own documentation.
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
Basic Installation Basic Installation
================== ==================
@ -39,17 +14,16 @@ Unpack the archive (if not cloned from GIT), enter the directory
and compile the program. and compile the program.
The sequence should be something like: The sequence should be something like:
$ tar xfv caesiumclt-*.tar.gz $ tar xfv caesium-clt-*.tar.gz
$ cd caesiumclt-* $ cd caesium-clt-*
$ cmake . $ cmake .
$ make $ make
$ sudo make install $ sudo make install
Cloning from git Cloning from git
================== ==================
$ git clone https://github.com/Lymphatus/CaesiumCLT $ git clone https://github.com/Lymphatus/caesium-clt
$ cd CaesiumCLT $ cd caesium-clt
$ autoreconf -fiv
$ cmake . $ cmake .
$ make $ make
$ sudo make install $ sudo make install

View File

@ -1,5 +1,5 @@
## Caesium CommandLineTools ## 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 ###### TESTED PLATFORMS
* Mac OS X Sierra (v10.12.1) * Mac OS X Sierra (v10.12.3)
* Arch Linux * Ubuntu 16.04
* Windows 10 * Windows 10
---------- ----------
###### INSTALLATION ###### COMPILATION
See INSTALL for more details. 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 $ 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 ###### TODO
* Code cleaning * Code cleaning
* Keep folder structure * Deeper error handling
---------- ----------
###### CHANGELOG ###### CHANGELOG
* 0.10.1-beta - All features are available
* 0.10.0-beta - Switched to cmake build system and libcaesium * 0.10.0-beta - Switched to cmake build system and libcaesium
* 0.9.1-beta - Initial development stage * 0.9.1-beta - Initial development stage

View File

@ -3,7 +3,6 @@ FILE(GLOB CHeaders *.h)
add_executable(caesiumclt ${CSources} ${CHeaders}) add_executable(caesiumclt ${CSources} ${CHeaders})
find_library(caesium caesium /usr/local/lib)
target_link_libraries(caesiumclt LINK_PUBLIC caesium) target_link_libraries(caesiumclt LINK_PUBLIC caesium)
install(TARGETS caesiumclt DESTINATION bin) install(TARGETS caesiumclt DESTINATION bin)

45
src/error.c Normal file
View File

@ -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.";
}
}

8
src/error.h Normal file
View File

@ -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

View File

@ -5,12 +5,13 @@
#include "optparse.h" #include "optparse.h"
#include "utils.h" #include "utils.h"
#include "config.h" #include "config.h"
#include "error.h"
cclt_options parse_arguments(char **argv, cs_image_pars *options) cclt_options parse_arguments(char **argv, cs_image_pars *options)
{ {
struct optparse opts; struct optparse opts;
//Initialize application options //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 //Parse command line args
optparse_init(&opts, argv); optparse_init(&opts, argv);
@ -31,7 +32,7 @@ cclt_options parse_arguments(char **argv, cs_image_pars *options)
case 'q': case 'q':
options->jpeg.quality = atoi(opts.optarg); options->jpeg.quality = atoi(opts.optarg);
if (options->jpeg.quality < 0 || options->jpeg.quality > 100) { if (options->jpeg.quality < 0 || options->jpeg.quality > 100) {
//TODO Trigger a error display_error(ERROR, 1);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
break; break;
@ -40,18 +41,18 @@ cclt_options parse_arguments(char **argv, cs_image_pars *options)
break; break;
case 'o': case 'o':
if (opts.optarg[strlen(opts.optarg) - 1] == '/') { if (opts.optarg[strlen(opts.optarg) - 1] == '/') {
result.output_folder = malloc((strlen(opts.optarg) + 1) * sizeof(char)); parameters.output_folder = malloc((strlen(opts.optarg) + 1) * sizeof(char));
snprintf(result.output_folder, strlen(opts.optarg) + 1, "%s", opts.optarg); snprintf(parameters.output_folder, strlen(opts.optarg) + 1, "%s", opts.optarg);
} else { } else {
result.output_folder = malloc((strlen(opts.optarg) + 2) * sizeof(char)); parameters.output_folder = malloc((strlen(opts.optarg) + 2) * sizeof(char));
snprintf(result.output_folder, strlen(opts.optarg) + 2, "%s/", opts.optarg); snprintf(parameters.output_folder, strlen(opts.optarg) + 2, "%s/", opts.optarg);
} }
break; break;
case 'R': case 'R':
result.recursive = true; parameters.recursive = true;
break; break;
case 'S': case 'S':
result.keep_structure = true; parameters.keep_structure = true;
break; break;
case 'v': case 'v':
fprintf(stdout, "%d.%d.%d\n", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH); 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 '?': case '?':
default: default:
fprintf(stderr, "%s: %s\n", argv[0], opts.errmsg); fprintf(stderr, "%s: %s\n", argv[0], opts.errmsg);
display_error(ERROR, 2);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }
@ -70,27 +72,48 @@ cclt_options parse_arguments(char **argv, cs_image_pars *options)
char *arg; char *arg;
bool files_flag = false, folders_flag = false; bool files_flag = false, folders_flag = false;
while ((arg = optparse_arg(&opts))) { while ((arg = optparse_arg(&opts))) {
if (folders_flag) {
display_error(WARNING, 8);
continue;
}
//Check if it's a directory and add its content //Check if it's a directory and add its content
if (is_directory(arg)) { if (is_directory(arg)) {
int count = 0; if (!files_flag) {
count = scan_folder(arg, &result, result.recursive); folders_flag = true;
if (count == 0) { parameters.input_folder = strdup(arg);
//TODO Trigger a warning int count = 0;
count = scan_folder(arg, &parameters, parameters.recursive);
if (count == 0) {
display_error(WARNING, 3);
}
} else {
display_error(WARNING, 9);
} }
} else { } else {
result.input_files = realloc(result.input_files, (result.files_count + 1) * sizeof(char *)); files_flag = true;
result.input_files[result.files_count] = malloc((strlen(arg) + 1) * sizeof(char)); parameters.input_folder = NULL;
snprintf(result.input_files[result.files_count], strlen(arg) + 1, "%s", arg); parameters.input_files = realloc(parameters.input_files, (parameters.files_count + 1) * sizeof(char *));
result.files_count++; 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 //If there're files and folders, we cannot keep the structure
//TODO Trigger a warning if (parameters.recursive && !folders_flag) {
result.keep_structure = !(files_flag && 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) 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 input_file_size = 0;
off_t output_file_size = 0; off_t output_file_size = 0;
//TODO Support folder structure //TODO Support folder structure
//Create the output folder if does not exists //Create the output folder if does not exists
if (mkpath(options->output_folder, 0777) == -1) { if (mkpath(options->output_folder, 0777) == -1) {
//TODO Error display_error(ERROR, 5);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
for (int i = 0; i < options->files_count; i++) { for (int i = 0; i < options->files_count; i++) {
char *filename = get_filename(options->input_files[i]); char *filename = get_filename(options->input_files[i]);
char *output_full_path = malloc((strlen(filename) + strlen(options->output_folder) + 1) * sizeof(char)); char *output_full_path;
snprintf(output_full_path, (strlen(filename) + strlen(options->output_folder) + 1), "%s%s", options->output_folder, filename); //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", fprintf(stdout, "(%d/%d) %s -> %s\n",
i + 1, 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); output_file_size = get_file_size(output_full_path);
options->output_total_size += output_file_size; 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", fprintf(stdout, "%s -> %s [%.2f%%]\n",
get_human_size(input_file_size), human_input_size,
get_human_size(output_file_size), human_output_size,
((float) output_file_size - input_file_size) * 100 / input_file_size); ((float) output_file_size - input_file_size) * 100 / input_file_size);
} else { } else {
options->input_total_size -= get_file_size(options->input_files[i]); options->input_total_size -= get_file_size(options->input_files[i]);

View File

@ -6,6 +6,7 @@
typedef struct cclt_options typedef struct cclt_options
{ {
char **input_files; char **input_files;
char *input_folder;
char *output_folder; char *output_folder;
bool recursive; bool recursive;
bool keep_structure; bool keep_structure;

View File

@ -1,21 +1,21 @@
#include <stdlib.h> #include <stdlib.h>
#include <time.h> #include <time.h>
#include <stdio.h> #include <stdio.h>
#include <errno.h>
#include <caesium.h> #include <caesium.h>
#include "utils.h" #include "utils.h"
#include "helper.h"
int main(int argc, char* argv[]) { int main(int argc, char *argv[])
errno = 0; {
long execution_ms = 0; long compression_time = 0;
cs_image_pars compress_options; cs_image_pars compress_options;
cclt_options options; cclt_options options;
//Initialize the default parameters //Initialize the default parameters
compress_options = initialize_parameters(); compress_options = initialize_parameters();
//Set them according to command line parameters
options = parse_arguments(argv, &compress_options); options = parse_arguments(argv, &compress_options);
//Start a timer before calling the compression //Start a timer before calling the compression
clock_t start = clock(), diff; clock_t start = clock(), diff;
@ -24,18 +24,19 @@ int main(int argc, char* argv[]) {
//Cleanup the two memory allocated objects //Cleanup the two memory allocated objects
free(options.output_folder); free(options.output_folder);
free(options.input_files); free(options.input_files);
free(options.input_folder);
//Get the difference //Get the difference
diff = clock() - start; diff = clock() - start;
execution_ms = diff * 1000 / CLOCKS_PER_SEC; compression_time = diff * 1000 / CLOCKS_PER_SEC;
//Output the compression results //Output the compression results
fprintf(stdout, "-------------------------------\n"
fprintf(stdout, "-------------------------------\nCompression completed in " "Compression completed in "
"%lum%lus%lums\n%s -> %s [%.2f%% | %s]\n", "%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), 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))); get_human_size((options.output_total_size - options.input_total_size)));

View File

@ -6,6 +6,7 @@
#include <math.h> #include <math.h>
#include "utils.h" #include "utils.h"
#include "tinydir.h" #include "tinydir.h"
#include "error.h"
void print_help() void print_help()
@ -32,7 +33,7 @@ bool is_directory(const char *path)
tinydir_file file; tinydir_file file;
if (tinydir_file_open(&file, path) == -1) { if (tinydir_file_open(&file, path) == -1) {
//TODO Error display_error(ERROR, 6);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@ -99,7 +100,7 @@ char *get_filename(char *full_path)
tofree = strdup(full_path); tofree = strdup(full_path);
//TODO change to strncpy //TODO change to strncpy
strcpy(tofree, full_path); strcpy(tofree, full_path);
//TODO Change on Windows //TODO Windows?
while ((token = strsep(&tofree, "/")) != NULL) { while ((token = strsep(&tofree, "/")) != NULL) {
if (tofree == NULL) { if (tofree == NULL) {
break; break;
@ -116,20 +117,21 @@ off_t get_file_size(const char *path)
tinydir_file file; tinydir_file file;
if (tinydir_file_open(&file, path) == -1) { if (tinydir_file_open(&file, path) == -1) {
//TODO Error display_error(ERROR, 7);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
return file._s.st_size; 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 //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 //Index of the array containing the correct unit
double order = floor(log2(labs(size)) / 10); double order = floor(log2(labs(size)) / 10);
//Alloc enough size for the final string //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 the order exceeds 4, something is fishy
if (order > 4) { if (order > 4) {
@ -137,7 +139,7 @@ char* get_human_size(off_t size) {
} }
//Copy the formatted string into the buffer //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 //And return it
return final; return final;
} }