blob: 9c4784599e617e3a7c65062ff043a6f755cf9c9a [file] [log] [blame]
Yann Collet4856a002015-01-24 01:58:16 +01001/*
2 zstdcli - Command Line Interface (cli) for zstd
3 Copyright (C) Yann Collet 2014-2015
4
5 GPL v2 License
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License along
18 with this program; if not, write to the Free Software Foundation, Inc.,
19 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
21 You can contact the author at :
22 - zstd source repository : https://github.com/Cyan4973/zstd
23 - ztsd public forum : https://groups.google.com/forum/#!forum/lz4c
24*/
25/*
26 Note : this is user program.
27 It is not part of zstd compression library.
28 The license of this compression CLI program is GPLv2.
29 The license of zstd library is BSD.
30*/
31
32
33/**************************************
34* Compiler Options
35**************************************/
36#define _CRT_SECURE_NO_WARNINGS /* Visual : removes warning from strcpy */
37#define _POSIX_SOURCE 1 /* triggers fileno() within <stdio.h> on unix */
38
39
40/**************************************
41* Includes
42**************************************/
43#include <stdio.h> /* fprintf, getchar */
44#include <stdlib.h> /* exit, calloc, free */
45#include <string.h> /* strcmp, strlen */
46#include "bench.h" /* BMK_benchFiles, BMK_SetNbIterations */
47#include "fileio.h"
48
49
50/**************************************
51* OS-specific Includes
52**************************************/
53#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__)
54# include <fcntl.h> // _O_BINARY
55# include <io.h> // _setmode, _isatty
56# ifdef __MINGW32__
57 int _fileno(FILE *stream); // MINGW somehow forgets to include this windows declaration into <stdio.h>
58# endif
59# define SET_BINARY_MODE(file) _setmode(_fileno(file), _O_BINARY)
60# define IS_CONSOLE(stdStream) _isatty(_fileno(stdStream))
61#else
62# include <unistd.h> // isatty
63# define SET_BINARY_MODE(file)
64# define IS_CONSOLE(stdStream) isatty(fileno(stdStream))
65#endif
66
67
68//****************************
69// Constants
70//****************************
71#define COMPRESSOR_NAME "zstd command line interface"
72#ifndef ZSTD_VERSION
73# define ZSTD_VERSION "v0.0.1"
74#endif
75#define AUTHOR "Yann Collet"
76#define WELCOME_MESSAGE "*** %s %i-bits %s, by %s (%s) ***\n", COMPRESSOR_NAME, (int)(sizeof(void*)*8), ZSTD_VERSION, AUTHOR, __DATE__
77#define ZSTD_EXTENSION ".zst"
78#define ZSTD_CAT "zstdcat"
79
80#define KB *(1 <<10)
81#define MB *(1 <<20)
82#define GB *(1U<<30)
83
84
85/**************************************
86* Display Macros
87**************************************/
88#define DISPLAY(...) fprintf(stderr, __VA_ARGS__)
89#define DISPLAYLEVEL(l, ...) if (displayLevel>=l) { DISPLAY(__VA_ARGS__); }
90static unsigned displayLevel = 2; // 0 : no display // 1: errors // 2 : + result + interaction + warnings ; // 3 : + progression; // 4 : + information
91
92
93/**************************************
94* Exceptions
95**************************************/
96#define DEBUG 0
97#define DEBUGOUTPUT(...) if (DEBUG) DISPLAY(__VA_ARGS__);
98#define EXM_THROW(error, ...) \
99{ \
100 DEBUGOUTPUT("Error defined at %s, line %i : \n", __FILE__, __LINE__); \
101 DISPLAYLEVEL(1, "Error %i : ", error); \
102 DISPLAYLEVEL(1, __VA_ARGS__); \
103 DISPLAYLEVEL(1, "\n"); \
104 exit(error); \
105}
106
107
108/**************************************
109* Command Line
110**************************************/
111static int usage(const char* programName)
112{
113 DISPLAY( "Usage :\n");
114 DISPLAY( " %s [arg] [input] [output]\n", programName);
115 DISPLAY( "\n");
116 DISPLAY( "input : a filename\n");
117 DISPLAY( " with no FILE, or when FILE is - , read standard input\n");
118 DISPLAY( "Arguments :\n");
119 DISPLAY( " -d : decompression (default for %s extension)\n", ZSTD_EXTENSION);
120 //DISPLAY( " -z : force compression\n");
121 DISPLAY( " -f : overwrite output without prompting \n");
122 DISPLAY( " -h/-H : display help/long help and exit\n");
123 return 0;
124}
125
126static int usage_advanced(const char* programName)
127{
128 DISPLAY(WELCOME_MESSAGE);
129 usage(programName);
130 DISPLAY( "\n");
131 DISPLAY( "Advanced arguments :\n");
132 DISPLAY( " -V : display Version number and exit\n");
133 DISPLAY( " -v : verbose mode\n");
134 DISPLAY( " -q : suppress warnings; specify twice to suppress errors too\n");
135 DISPLAY( " -c : force write to standard output, even if it is the console\n");
136 //DISPLAY( " -t : test compressed file integrity\n");
137 DISPLAY( "Benchmark arguments :\n");
138 DISPLAY( " -b : benchmark file(s)\n");
139 DISPLAY( " -i# : iteration loops [1-9](default : 3), benchmark mode only\n");
140 return 0;
141}
142
143static int badusage(const char* programName)
144{
145 DISPLAYLEVEL(1, "Incorrect parameters\n");
146 if (displayLevel >= 1) usage(programName);
147 return 1;
148}
149
150
151static void waitEnter(void)
152{
153 DISPLAY("Press enter to continue...\n");
154 getchar();
155}
156
157
158int main(int argc, char** argv)
159{
160 int i,
161 bench=0,
162 decode=0,
163 forceStdout=0,
164 main_pause=0;
165 unsigned fileNameStart = 0;
166 unsigned nbFiles = 0;
167 const char* programName = argv[0];
168 const char* inFileName = NULL;
169 const char* outFileName = NULL;
170 char* dynNameSpace = NULL;
171 char extension[] = ZSTD_EXTENSION;
172
173 /* zstdcat behavior */
174 if (!strcmp(programName, ZSTD_CAT)) { decode=1; forceStdout=1; displayLevel=1; outFileName=stdoutmark; }
175
176 // command switches
177 for(i=1; i<argc; i++)
178 {
179 char* argument = argv[i];
180
181 if(!argument) continue; // Protection if argument empty
182
183 /* Decode commands (note : aggregated commands are allowed) */
184 if (argument[0]=='-')
185 {
186 /* '-' means stdin/stdout */
187 if (argument[1]==0)
188 {
189 if (!inFileName) inFileName=stdinmark;
190 else outFileName=stdoutmark;
191 }
192
193 argument++;
194
195 while (argument[0]!=0)
196 {
197 switch(argument[0])
198 {
199 /* Display help */
200 case 'V': DISPLAY(WELCOME_MESSAGE); return 0; /* Version Only */
201 case 'H':
202 case 'h': return usage_advanced(programName);
203
204 // Compression (default)
205 //case 'z': forceCompress = 1; break;
206
207 // Decoding
208 case 'd': decode=1; argument++; break;
209
210 // Force stdout, even if stdout==console
211 case 'c': forceStdout=1; outFileName=stdoutmark; displayLevel=1; argument++; break;
212
213 // Test
214 //case 't': decode=1; LZ4IO_setOverwrite(1); output_filename=nulmark; break;
215
216 /* Overwrite */
217 case 'f': FIO_overwriteMode(); argument++; break;
218
219 /* Verbose mode */
220 case 'v': displayLevel=4; argument++; break;
221
222 /* Quiet mode */
223 case 'q': displayLevel--; argument++; break;
224
225 /* keep source file (default anyway, so useless; only for xz/lzma compatibility) */
226 case 'k': argument++; break;
227
228 /* Benchmark */
229 case 'b': bench=1; argument++; break;
230
231 /* Modify Nb Iterations (benchmark only) */
232 case 'i':
233 {
234 int iters= 0;
235 argument++;
236 while ((*argument >='0') && (*argument <='9'))
237 iters *= 10, iters += *argument++ - '0';
238 BMK_SetNbIterations(iters);
239 }
240 break;
241
242 /* Pause at the end (hidden option) */
243 case 'p': main_pause=1; argument++; break;
244
245 /* unknown command */
246 default : return badusage(programName);
247 }
248 }
249 continue;
250 }
251
252 /* first provided filename is input */
253 if (!inFileName) { inFileName = argument; fileNameStart = i; nbFiles = argc-i; continue; }
254
255 /* second provided filename is output */
256 if (!outFileName)
257 {
258 outFileName = argument;
259 if (!strcmp (outFileName, nullString)) outFileName = nulmark;
260 continue;
261 }
262 }
263
264 /* Welcome message (if verbose) */
265 DISPLAYLEVEL(3, WELCOME_MESSAGE);
266
267 /* No input filename ==> use stdin */
268 if(!inFileName) { inFileName=stdinmark; }
269
270 /* Check if input defined as console; trigger an error in this case */
271 if (!strcmp(inFileName, stdinmark) && IS_CONSOLE(stdin) ) return badusage(programName);
272
273 /* Check if benchmark is selected */
274 if (bench) { BMK_bench(argv+fileNameStart, nbFiles, 0); goto _end; }
275
276 /* No output filename ==> try to select one automatically (when possible) */
277 while (!outFileName)
278 {
279 if (!IS_CONSOLE(stdout)) { outFileName=stdoutmark; break; } /* Default to stdout whenever possible (i.e. not a console) */
280 if (!decode) /* compression to file */
281 {
282 size_t l = strlen(inFileName);
283 dynNameSpace = (char*)calloc(1,l+5);
284 strcpy(dynNameSpace, inFileName);
285 strcpy(dynNameSpace+l, ZSTD_EXTENSION);
286 outFileName = dynNameSpace;
287 DISPLAYLEVEL(2, "Compressed filename will be : %s \n", outFileName);
288 break;
289 }
290 /* decompression to file (automatic name will work only if input filename has correct format extension) */
291 {
292 size_t outl;
293 size_t inl = strlen(inFileName);
294 dynNameSpace = (char*)calloc(1,inl+1);
295 outFileName = dynNameSpace;
296 strcpy(dynNameSpace, inFileName);
297 outl = inl;
298 if (inl>4)
299 while ((outl >= inl-4) && (inFileName[outl] == extension[outl-inl+4])) dynNameSpace[outl--]=0;
300 if (outl != inl-5) { DISPLAYLEVEL(1, "Cannot determine an output filename\n"); return badusage(programName); }
301 DISPLAYLEVEL(2, "Decoding file %s \n", outFileName);
302 }
303 }
304
305 /* Check if output is defined as console; trigger an error in this case */
306 if (!strcmp(outFileName,stdoutmark) && IS_CONSOLE(stdout) && !forceStdout) return badusage(programName);
307
308 /* No warning message in pure pipe mode (stdin + stdout) */
309 if (!strcmp(inFileName, stdinmark) && !strcmp(outFileName,stdoutmark) && (displayLevel==2)) displayLevel=1;
310
311 /* IO Stream/File */
312 FIO_setNotificationLevel(displayLevel);
313 if (decode)
314 FIO_decompressFilename(outFileName, inFileName);
315 else
316 FIO_compressFilename(outFileName, inFileName);
317
318_end:
319 if (main_pause) waitEnter();
320 free(dynNameSpace);
321 return 0;
322}