Merge pull request #24 from Lymphatus/0.11.0

0.11.0
This commit is contained in:
Matteo Paonessa 2017-12-29 23:22:13 +01:00 committed by GitHub
commit ce0f68f790
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 577 additions and 146 deletions

161
.gitignore vendored
View File

@ -10,6 +10,163 @@ Thumbs.db
.Spotlight-V100
.Trashes
.idea
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff:
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/dictionaries
# Sensitive or high-churn files:
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.xml
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
# Gradle:
.idea/**/gradle.xml
.idea/**/libraries
# CMake
cmake-build-*
build
# Mongo Explorer plugin:
.idea/**/mongoSettings.xml
## File-based project format:
*.iws
## Plugin-specific files:
# IntelliJ
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
### macOS template
# General
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.TemporaryItems
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### Linux template
*~
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
### Windows template
# Windows thumbnail cache files
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msm
*.msp
# Windows shortcuts
*.lnk
### C template
# Prerequisites
*.d
# Object files
*.o
*.ko
*.obj
*.elf
# Linker output
*.ilk
*.map
*.exp
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf

1
.idea/.name Normal file
View File

@ -0,0 +1 @@
caesiumclt

2
.idea/caesium-clt.iml Normal file
View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<module classpath="CMake" type="CPP_MODULE" version="4" />

View File

@ -0,0 +1,29 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<Objective-C-extensions>
<file>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
</file>
<class>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
</class>
<extensions>
<pair source="cpp" header="h" fileNamingConvention="NONE" />
<pair source="c" header="h" fileNamingConvention="NONE" />
</extensions>
</Objective-C-extensions>
</code_scheme>
</component>

View File

@ -0,0 +1,125 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="ClangTidyInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Inspection" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="DeprecatedAPI" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Inspection" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="DuplicateSwitchCase" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Inspection" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="EndlessLoop" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Inspection" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="EqualityInConditionalOperator" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Inspection" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="FormatSpecifiers" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Inspection" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="FunctionImplicitDeclarationInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Inspection" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="HidesUpperScope" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Inspection" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="HidingNonVirtualFunction" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Inspection" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="ImplicitIntegerAndEnumConversion" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Inspection" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="ImplicitPointerAndIntegerConversion" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Inspection" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="IncompatibleEnums" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Inspection" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="IncompatibleInitializers" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Inspection" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="IncompatiblePointers" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Inspection" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="InfiniteRecursion" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Inspection" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="KRUnspecifiedParameters" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Inspection" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="LocalValueEscapesScope" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Inspection" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="MissingReturn" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Inspection" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="MissingSwitchCase" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Inspection" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="NotImplementedFunctions" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Inspection" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="NotInitializedVariable" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Inspection" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="NotSuperclass" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Inspection" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="OCDFAInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Inspection" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="OCLoopDoesntUseConditionVariableInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Inspection" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="OCSimplifyInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Inspection" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="OCUnusedGlobalDeclarationInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Inspection" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="OCUnusedMacroInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Inspection" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="OCUnusedStructInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Inspection" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="OCUnusedTemplateParameterInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Inspection" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="RedundantCast" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Inspection" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="ResourceNotFoundInspection" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Inspection" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="SignednessMismatch" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Inspection" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="UnreachableCode" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Inspection" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="UnusedExpressionResult" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Inspection" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="UnusedImportStatement" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Inspection" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="UnusedLocalVariable" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Inspection" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="UnusedLocalization" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Inspection" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="UnusedParameter" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Inspection" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="UnusedValue" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Inspection" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="ValueMayNotFitIntoReceiver" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Inspection" level="WARNING" enabled="true" />
</inspection_tool>
</profile>
</component>

12
.idea/misc.xml Normal file
View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
<component name="CidrRootsConfiguration">
<sourceRoots>
<file path="$PROJECT_DIR$/src" />
</sourceRoots>
<excludeRoots>
<file path="$PROJECT_DIR$/cmake-build-debug/CMakeFiles" />
</excludeRoots>
</component>
</project>

8
.idea/modules.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/caesium-clt.iml" filepath="$PROJECT_DIR$/.idea/caesium-clt.iml" />
</modules>
</component>
</project>

View File

@ -0,0 +1,3 @@
<component name="DependencyValidationManager">
<scope name="Inspection" pattern="!file:src/optparse.c&amp;&amp;!file:src/optparse.h&amp;&amp;!file:src/tinydir.h&amp;&amp;!file:.gitignore" />
</component>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

@ -3,8 +3,8 @@ project(caesiumclt)
# The version number.
set(VERSION_MAJOR 0)
set(VERSION_MINOR 10)
set(VERSION_PATCH 2)
set(VERSION_MINOR 11)
set(VERSION_PATCH 0)
configure_file(
"src/config.h.in"

View File

@ -1,5 +1,5 @@
## Caesium CommandLineTools
##### caesium-clt - v0.10.2-beta (build 20170318) - Copyright &copy; Matteo Paonessa, 2017. All Rights Reserved.
##### caesium-clt - v0.11.0-beta (build 20171109) - Copyright &copy; Matteo Paonessa, 2017. All Rights Reserved.
[![Build Status](https://travis-ci.org/Lymphatus/caesium-clt.svg?branch=master)](https://travis-ci.org/Lymphatus/caesium-clt)
----------
@ -13,7 +13,7 @@
----------
###### TESTED PLATFORMS
* Mac OS X Sierra (v10.12.3)
* Mac OS X High Sierra (v10.13.1)
* Ubuntu 16.04
* Windows 10
@ -60,6 +60,7 @@ $ caesiumclt -q 0 -RS -o ~/output/ ~/Pictures
----------
###### CHANGELOG
* 0.11.0-beta - Fixing paths issues and dry-run option
* 0.10.2-beta - Bugfixes & full Windows support
* 0.10.1-beta - All features are available
* 0.10.0-beta - Switched to cmake build system and libcaesium

View File

@ -39,9 +39,9 @@ const char *get_error_message(int code)
case 9:
return "Input files provided. Cannot mix them with a folder.";
case 10:
return "-R is useless on files.";
return "-R has no effects on files.";
case 11:
return "-S is useless without -R.";
return "-S has no effect without -R.";
case 12:
return "Cannot set output folder inside the input one";

View File

@ -1,6 +1,11 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifdef _WIN32
#include <direct.h>
#endif
#include "helper.h"
#include "optparse.h"
#include "utils.h"
@ -11,7 +16,7 @@ cclt_options parse_arguments(char **argv, cs_image_pars *options)
{
struct optparse opts;
//Initialize application options
cclt_options parameters = {NULL, NULL, false, false, 0, 0, 0};
cclt_options parameters = {NULL, "", "", false, false, 0, 0, 0, false};
//Parse command line args
optparse_init(&opts, argv);
@ -21,33 +26,36 @@ cclt_options parse_arguments(char **argv, cs_image_pars *options)
{"output", 'o', OPTPARSE_REQUIRED},
{"recursive", 'R', OPTPARSE_NONE},
{"keep-structure", 'S', OPTPARSE_NONE},
{"dry-run", 'd', OPTPARSE_NONE},
{"version", 'v', OPTPARSE_NONE},
{"help", 'h', OPTPARSE_NONE},
{0}
};
int option;
int option;
while ((option = optparse_long(&opts, longopts, NULL)) != -1) {
switch (option) {
case 'q':
options->jpeg.quality = atoi(opts.optarg);
options->jpeg.quality = (int) strtol(opts.optarg, (char **) NULL, 10);;
if (options->jpeg.quality < 0 || options->jpeg.quality > 100) {
display_error(ERROR, 1);
}
break;
case 'e':
options->jpeg.exif_copy = true;
break;
case 'o':
if (opts.optarg[strlen(opts.optarg) - 1] == '/' || opts.optarg[strlen(opts.optarg) - 1] == '\\') {
parameters.output_folder = malloc((strlen(opts.optarg) + 1) * sizeof(char));
if (opts.optarg[0] == '~') {
snprintf(parameters.output_folder, strlen(opts.optarg) + 1, "%s", opts.optarg);
} else {
parameters.output_folder = malloc((strlen(opts.optarg) + 2) * sizeof(char));
realpath(opts.optarg, parameters.output_folder);
}
if (parameters.output_folder[strlen(opts.optarg) - 1] != '/' &&
parameters.output_folder[strlen(opts.optarg) - 1] != '\\') {
#ifdef _WIN32
snprintf(parameters.output_folder, strlen(opts.optarg) + 2, "%s\\", opts.optarg);
snprintf(parameters.output_folder, strlen(parameters.output_folder) + 2, "%s\\", parameters.output_folder);
#else
snprintf(parameters.output_folder, strlen(opts.optarg) + 2, "%s/", opts.optarg);
snprintf(parameters.output_folder, strlen(parameters.output_folder) + 2, "%s/",
parameters.output_folder);
#endif
}
break;
@ -57,6 +65,9 @@ cclt_options parse_arguments(char **argv, cs_image_pars *options)
case 'S':
parameters.keep_structure = true;
break;
case 'd':
parameters.dry_run = true;
break;
case 'v':
fprintf(stdout, "%d.%d.%d\n", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH);
exit(EXIT_SUCCESS);
@ -69,22 +80,46 @@ cclt_options parse_arguments(char **argv, cs_image_pars *options)
display_error(ERROR, 2);
}
}
//Remaining arguments
char *arg;
bool files_flag = false, folders_flag = false;
char resolved_path[MAX_PATH_SIZE];
while ((arg = optparse_arg(&opts))) {
if (folders_flag) {
display_error(WARNING, 8);
continue;
break;
}
//Check if it's a directory and add its content
if (is_directory(arg)) {
if (arg[0] == '~') {
if (arg[strlen(arg) - 1] == '/' || arg[strlen(arg) - 1] == '\\') {
snprintf(resolved_path, strlen(arg), "%s", arg);
} else {
#ifdef _WIN32
snprintf(resolved_path, strlen(arg) + 1, "%s\\", arg);
#else
snprintf(resolved_path, strlen(arg) + 1, "%s/", arg);
#endif
}
} else {
realpath(arg, resolved_path);
}
if (is_directory(resolved_path)) {
if (!files_flag) {
folders_flag = true;
parameters.input_folder = strdup(arg);
if (resolved_path[strlen(resolved_path) - 1] != '/' && resolved_path[strlen(resolved_path) - 1] != '\\') {
#ifdef _WIN32
resolved_path[strlen(resolved_path)] = '\\';
#else
resolved_path[strlen(resolved_path)] = '/';
#endif
resolved_path[strlen(resolved_path)] = '\0';
}
snprintf(parameters.input_folder, strlen(resolved_path) + 1, "%s", resolved_path);
int count = 0;
count = scan_folder(arg, &parameters, parameters.recursive);
count = scan_folder(resolved_path, &parameters, parameters.recursive);
if (count == 0) {
display_error(WARNING, 3);
}
@ -93,7 +128,6 @@ cclt_options parse_arguments(char **argv, cs_image_pars *options)
}
} 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);
@ -102,8 +136,12 @@ cclt_options parse_arguments(char **argv, cs_image_pars *options)
}
//Check if the output folder is a subfolder of the input to avoid infinite loops
//but just if the -R option is set
//However, if the folders are the same, we can let it go as it will overwrite the files
if (folders_flag) {
if (strstr(parameters.output_folder, parameters.input_folder) != NULL) {
if (strstr(parameters.output_folder, parameters.input_folder) != NULL
&& strcmp(parameters.output_folder, parameters.input_folder) != 0
&& parameters.recursive) {
display_error(ERROR, 12);
}
}
@ -138,7 +176,10 @@ int start_compression(cclt_options *options, cs_image_pars *parameters)
for (int i = 0; i < options->files_count; i++) {
char *filename = get_filename(options->input_files[i]);
char *output_full_path;
char *output_full_path = NULL;
char *original_output_full_path = NULL;
bool overwriting = false;
off_t file_size = 0;
//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));
@ -146,18 +187,23 @@ int start_compression(cclt_options *options, cs_image_pars *parameters)
options->output_folder, filename);
} else {
/*
* Otherwise, we nee to compute the whole directory structure
* Otherwise, we need 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 index = strspn(options->input_folder, options->input_files[i]);
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]);
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);
snprintf(output_full_path, strlen(output_full_folder) + strlen(filename) + 1, "%s%s",
output_full_folder,
filename);
mkpath(output_full_folder);
}
@ -167,24 +213,47 @@ int start_compression(cclt_options *options, cs_image_pars *parameters)
filename,
output_full_path);
input_file_size = get_file_size(options->input_files[i]);
options->input_total_size += input_file_size;
if (cs_compress(options->input_files[i], output_full_path, parameters)) {
compressed_files++;
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",
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]);
//If the file already exist, create a temporary file
if (file_exists(output_full_path)) {
original_output_full_path = strdup(output_full_path);
output_full_path = realloc(output_full_path, (strlen(output_full_path) + 4) * sizeof(char));
snprintf(output_full_path, (strlen(original_output_full_path) + 4), "%s.cs", original_output_full_path);
overwriting = true;
}
file_size = get_file_size(options->input_files[i]);
if (file_size == 0) {
//We could not open the file
continue;
}
input_file_size = file_size;
options->input_total_size += input_file_size;
//Prevent compression if running in dry mode
if (!options->dry_run) {
if (cs_compress(options->input_files[i], output_full_path, parameters)) {
compressed_files++;
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",
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]);
}
}
//Rename if we were overwriting
if (overwriting && !options->dry_run) {
rename(output_full_path, original_output_full_path);
}
free(original_output_full_path);
free(output_full_path);
}

View File

@ -3,16 +3,24 @@
#include <caesium.h>
#ifdef _WIN32
#define MAX_PATH_SIZE _MAX_PATH
#else
#include <limits.h>
#define MAX_PATH_SIZE PATH_MAX
#endif
typedef struct cclt_options
{
char **input_files;
char *input_folder;
char *output_folder;
char input_folder[MAX_PATH_SIZE];
char output_folder[MAX_PATH_SIZE];
bool recursive;
bool keep_structure;
int files_count;
off_t input_total_size;
off_t output_total_size;
bool dry_run;
} cclt_options;
cclt_options parse_arguments(char *argv[], cs_image_pars *options);

View File

@ -7,7 +7,7 @@
int main(int argc, char *argv[])
{
//Exit if no arguments
//Exit if less than 2 arguments
if (argc < 2) {
print_help();
exit(EXIT_FAILURE);
@ -27,10 +27,8 @@ int main(int argc, char *argv[])
start_compression(&options, &compress_options);
//Cleanup the two memory allocated objects
free(options.output_folder);
//Cleanup the memory allocated objects
free(options.input_files);
free(options.input_folder);
//Get the difference
diff = clock() - start;

View File

@ -99,11 +99,6 @@ int optparse(struct optparse *options, const char *optstring)
int type = argtype(optstring, option[0]);
char *next = options->argv[options->optind + 1];
switch (type) {
case -1: {
options->optind++;
char str[2] = {option[0]};
return opterror(options, MSG_INVALID, str);
}
case OPTPARSE_NONE:
if (option[1]) {
options->subopt++;
@ -134,8 +129,13 @@ int optparse(struct optparse *options, const char *optstring)
else
options->optarg = 0;
return option[0];
default:
case -1: {
options->optind++;
char str[2] = {option[0]};
return opterror(options, MSG_INVALID, str);
}
}
return 0;
}
char *optparse_arg(struct optparse *options)

View File

@ -4,6 +4,7 @@
#include <caesium.h>
#include <limits.h>
#include <math.h>
#include "utils.h"
#include "tinydir.h"
#include "error.h"
@ -22,6 +23,7 @@ void print_help()
"\t-o, --output\t\toutput folder\n"
"\t-R, --recursive\t\tif input is a folder, scan subfolders too\n"
"\t-S, --keep-structure\tkeep the folder structure, use with -R\n"
"\t-d, --dry-run\t\tdo not really compress files but just show output paths\n"
"\t-h, --help\t\tdisplay this help and exit\n"
"\t-v, --version\t\toutput version information and exit\n\n");
exit(EXIT_SUCCESS);
@ -30,18 +32,18 @@ void print_help()
bool is_directory(const char *path)
{
#ifdef _WIN32
tinydir_dir dir;
tinydir_dir dir;
return tinydir_open(&dir, path) != -1;
return tinydir_open(&dir, path) != -1;
#else
tinydir_file file;
tinydir_file file;
if (tinydir_file_open(&file, path) == -1) {
display_error(ERROR, 6);
}
if (tinydir_file_open(&file, path) == -1) {
display_error(ERROR, 6);
}
return (bool) file.is_dir;
return (bool) file.is_dir;
#endif
}
@ -62,10 +64,10 @@ int scan_folder(const char *directory, cclt_options *options, bool recursive)
} else {
options->input_files = realloc(options->input_files, (options->files_count + 1) * sizeof(char *));
options->input_files[options->files_count] = malloc((strlen(file.path) + 1) * sizeof(char));
snprintf(options->input_files[options->files_count],
strlen(file.path) + 1, "%s", file.path);
snprintf(options->input_files[options->files_count],
strlen(file.path) + 1, "%s", file.path);
#ifdef _WIN32
options->input_files[options->files_count] = str_replace(options->input_files[options->files_count], "/", "\\");
options->input_files[options->files_count] = str_replace(options->input_files[options->files_count], "/", "\\");
#endif
options->files_count++;
n++;
@ -107,13 +109,10 @@ char *get_filename(char *full_path)
//Get just the filename
tofree = strdup(full_path);
//TODO change to strncpy
strcpy(tofree, full_path);
//TODO Windows?
#ifdef _WIN32
while ((token = strsep(&tofree, "\\")) != NULL) {
while ((token = strsep(&tofree, "\\")) != NULL) {
#else
while ((token = strsep(&tofree, "/")) != NULL) {
while ((token = strsep(&tofree, "/")) != NULL) {
#endif
if (tofree == NULL) {
break;
@ -127,19 +126,24 @@ char *get_filename(char *full_path)
off_t get_file_size(const char *path)
{
FILE *f = fopen(path, "rb");
if (f == NULL) {
display_error(ERROR, 7);
}
fseek(f, 0, SEEK_END);
unsigned long len = (unsigned long)ftell(f);
fclose(f);
FILE *f = fopen(path, "rb");
if (f == NULL) {
display_error(WARNING, 7);
return 0;
}
fseek(f, 0, SEEK_END);
off_t len = ftell(f);
fclose(f);
return len;
return len;
}
char *get_human_size(off_t size)
{
if (size == 0) {
return "0.00 B";
}
//We should not get more than TB images
char *unit[5] = {"B", "KB", "MB", "GB", "TB"};
//Index of the array containing the correct unit
@ -158,88 +162,94 @@ char *get_human_size(off_t size)
return final;
}
bool file_exists(const char *file_path)
{
struct stat buffer;
return (stat(file_path, &buffer) == 0);
}
#ifdef _WIN32
char *str_replace(char *orig, char *rep, char *with) {
char *result; // the return string
char *ins; // the next insert point
char *tmp; // varies
int len_rep; // length of rep (the string to remove)
int len_with; // length of with (the string to replace rep with)
int len_front; // distance between rep and end of last rep
int count; // number of replacements
char *result; // the return string
char *ins; // the next insert point
char *tmp; // varies
int len_rep; // length of rep (the string to remove)
int len_with; // length of with (the string to replace rep with)
int len_front; // distance between rep and end of last rep
int count; // number of replacements
if (!orig || !rep)
return NULL;
len_rep = strlen(rep);
if (len_rep == 0)
return NULL;
if (!with)
with = "";
len_with = strlen(with);
if (!orig || !rep)
return NULL;
len_rep = strlen(rep);
if (len_rep == 0)
return NULL;
if (!with)
with = "";
len_with = strlen(with);
ins = orig;
for (count = 0; tmp = strstr(ins, rep); ++count) {
ins = tmp + len_rep;
}
ins = orig;
for (count = 0; tmp = strstr(ins, rep); ++count) {
ins = tmp + len_rep;
}
tmp = result = malloc(strlen(orig) + (len_with - len_rep) * count + 1);
tmp = result = malloc(strlen(orig) + (len_with - len_rep) * count + 1);
if (!result)
return NULL;
if (!result)
return NULL;
while (count--) {
ins = strstr(orig, rep);
len_front = ins - orig;
tmp = strncpy(tmp, orig, len_front) + len_front;
tmp = strcpy(tmp, with) + len_with;
orig += len_front + len_rep;
}
strcpy(tmp, orig);
return result;
while (count--) {
ins = strstr(orig, rep);
len_front = ins - orig;
tmp = strncpy(tmp, orig, len_front) + len_front;
tmp = strcpy(tmp, with) + len_with;
orig += len_front + len_rep;
}
strcpy(tmp, orig);
return result;
}
char *strsep (char **stringp, const char *delim)
{
char *begin, *end;
char *begin, *end;
begin = *stringp;
if (begin == NULL)
return NULL;
begin = *stringp;
if (begin == NULL)
return NULL;
/* A frequent case is when the delimiter string contains only one
character. Here we don't need to call the expensive `strpbrk'
function and instead work using `strchr'. */
if (delim[0] == '\0' || delim[1] == '\0')
{
char ch = delim[0];
/* A frequent case is when the delimiter string contains only one
character. Here we don't need to call the expensive `strpbrk'
function and instead work using `strchr'. */
if (delim[0] == '\0' || delim[1] == '\0')
{
char ch = delim[0];
if (ch == '\0')
end = NULL;
else
{
if (*begin == ch)
end = begin;
else if (*begin == '\0')
end = NULL;
else
end = strchr (begin + 1, ch);
}
}
else
/* Find the end of the token. */
end = strpbrk (begin, delim);
if (ch == '\0')
end = NULL;
else
{
if (*begin == ch)
end = begin;
else if (*begin == '\0')
end = NULL;
else
end = strchr (begin + 1, ch);
}
}
else
/* Find the end of the token. */
end = strpbrk (begin, delim);
if (end)
{
/* Terminate the token and set *STRINGP past NUL character. */
*end++ = '\0';
*stringp = end;
}
else
/* No more delimiters; this is the last token. */
*stringp = NULL;
if (end)
{
/* Terminate the token and set *STRINGP past NUL character. */
*end++ = '\0';
*stringp = end;
}
else
/* No more delimiters; this is the last token. */
*stringp = NULL;
return begin;
return begin;
}
#endif

View File

@ -21,6 +21,8 @@ char* get_human_size(off_t size);
int mkpath(const char *pathname);
bool file_exists(const char* file_path);
#ifdef _WIN32
char *str_replace(char *orig, char *rep, char *with);
char *strsep(char **stringp, const char *delim);