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.
set(VERSION_MAJOR 0)
set(VERSION_MINOR 10)
set(VERSION_PATCH 0)
set(VERSION_PATCH 1)
configure_file(
"src/config.h.in"

40
INSTALL
View File

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

View File

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

View File

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

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 "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)) {
int count = 0;
count = scan_folder(arg, &result, result.recursive);
if (count == 0) {
//TODO Trigger a warning
if (!files_flag) {
folders_flag = true;
parameters.input_folder = strdup(arg);
int count = 0;
count = scan_folder(arg, &parameters, parameters.recursive);
if (count == 0) {
display_error(WARNING, 3);
}
} else {
display_error(WARNING, 9);
}
} 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++;
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]);

View File

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

View File

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

View File

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