1 /*-
2 * Copyright (c) 2011-2021 Ganael LAPLANCHE <ganael.laplanche@martymac.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27 #include "fpart.h"
28 #include "types.h"
29 #include "utils.h"
30 #include "options.h"
31 #include "partition.h"
32 #include "file_entry.h"
33 #include "dispatch.h"
34
35 /* NULL, exit(3) */
36 #include <stdlib.h>
37
38 /* strtoumax(3) */
39 #include <limits.h>
40 #include <inttypes.h>
41
42 /* fprintf(3), fopen(3), fclose(3), fgets(3), foef(3) */
43 #include <stdio.h>
44
45 /* getopt(3) */
46 #include <unistd.h>
47 #if !defined(__SunOS_5_9)
48 #include <getopt.h>
49 #endif
50
51 /* strlen(3) */
52 #include <string.h>
53
54 /* bzero(3) */
55 #include <strings.h>
56
57 /* errno */
58 #include <errno.h>
59
60 /* assert(3) */
61 #include <assert.h>
62
63 /* Print version */
64 static void
version(void)65 version(void)
66 {
67 fprintf(stderr, "fpart v" FPART_VERSION "\n"
68 "Copyright (c) 2011-2021 "
69 "Ganael LAPLANCHE <ganael.laplanche@martymac.org>\n"
70 "WWW: http://contribs.martymac.org\n");
71 fprintf(stderr, "Build options: debug=");
72 #if defined(DEBUG)
73 fprintf(stderr, "yes, fts=");
74 #else
75 fprintf(stderr, "no, fts=");
76 #endif
77 #if defined(EMBED_FTS)
78 fprintf(stderr, "embedded\n");
79 #else
80 fprintf(stderr, "system\n");
81 #endif
82 }
83
84 /* Print usage */
85 static void
usage(void)86 usage(void)
87 {
88 fprintf(stderr, "Usage: fpart [OPTIONS] -n num | -f files | -s size "
89 "[FILE or DIR...]\n");
90 fprintf(stderr, "Sort and pack files into partitions.\n");
91 fprintf(stderr, "\n");
92 fprintf(stderr, "General options:\n");
93 fprintf(stderr, " -h\tthis help\n");
94 fprintf(stderr, " -V\tprint version\n");
95 fprintf(stderr, "\n");
96 fprintf(stderr, "Partition control:\n");
97 fprintf(stderr, " -n\tpack files into <num> partitions\n");
98 fprintf(stderr, " -f\tlimit partitions to <files> files or directories\n");
99 fprintf(stderr, " -s\tlimit partitions to <size> bytes\n");
100 fprintf(stderr, "\n");
101 fprintf(stderr, "Input control:\n");
102 fprintf(stderr, " -i\tread file list from <infile> "
103 "(stdin if '-' is specified)\n");
104 fprintf(stderr, " -a\tinput contains arbitrary values "
105 "(do not crawl filesystem)\n");
106 fprintf(stderr, "\n");
107 fprintf(stderr, "Output control:\n");
108 fprintf(stderr, " -o\toutput partitions to <outfile> template "
109 "(stdout if '-' is specified)\n");
110 fprintf(stderr, " -0\tend filenames with a null (\\0) character when "
111 "using option -o\n");
112 fprintf(stderr, " -e\tadd ending slash to directories\n");
113 fprintf(stderr, " -v\tverbose mode (may be specified more than once to "
114 "increase verbosity)\n");
115 fprintf(stderr, "\n");
116 fprintf(stderr, "Filesystem crawling control:\n");
117 fprintf(stderr, " -l\tfollow symbolic links\n");
118 fprintf(stderr, " -b\tdo not cross filesystem boundaries\n");
119 fprintf(stderr, " -y\tinclude files matching <pattern> only (may be "
120 "specified more than once)\n");
121 #if defined(_HAS_FNM_CASEFOLD)
122 fprintf(stderr, " -Y\tsame as -y, but ignore case\n");
123 #endif
124 fprintf(stderr, " -x\texclude files matching <pattern> (may be specified "
125 "more than once)\n");
126 #if defined(_HAS_FNM_CASEFOLD)
127 fprintf(stderr, " -X\tsame as -x, but ignore case\n");
128 #endif
129 fprintf(stderr, "\n");
130 fprintf(stderr, "Directory handling:\n");
131 fprintf(stderr, " -z\tpack empty directories too "
132 "(default: pack files only)\n");
133 fprintf(stderr, " -zz\ttreat un-readable directories as empty\n");
134 fprintf(stderr, " -zzz\tpack all directories (as empty)\n");
135 fprintf(stderr, " -d\tpack directories instead of files after a certain "
136 "<depth>\n");
137 fprintf(stderr, " -D\tpack leaf directories (i.e. containing files only, "
138 "implies -z)\n");
139 fprintf(stderr, " -E\tpack directories instead of files (implies -D)\n");
140 fprintf(stderr, "\n");
141 fprintf(stderr, "Live mode:\n");
142 fprintf(stderr, " -L\tlive mode: generate partitions during filesystem "
143 "crawling\n");
144 fprintf(stderr, " -w\tpre-partition hook: execute <cmd> at partition "
145 "start\n");
146 fprintf(stderr, " -W\tpost-partition hook: execute <cmd> at partition "
147 "end\n");
148 fprintf(stderr, "\n");
149 fprintf(stderr, "Size handling:\n");
150 fprintf(stderr, " -p\tpreload each partition with <num> bytes\n");
151 fprintf(stderr, " -q\toverload each file with <num> bytes\n");
152 fprintf(stderr, " -r\tround each file size up to next <num> bytes "
153 "multiple\n");
154 fprintf(stderr, "\n");
155 fprintf(stderr, "Example: fpart -n 3 -o var-parts /var\n");
156 fprintf(stderr, "\n");
157 fprintf(stderr, "Please report bugs to Ganael LAPLANCHE "
158 "<ganael.laplanche@martymac.org>\n");
159 return;
160 }
161
162 /* Handle one argument (either a path to crawl or an arbitrary
163 value) and update file entries (head)
164 - returns != 0 if a critical error occurred
165 - returns with head set to the last element added
166 - updates totalfiles with the number of elements added */
167 static int
handle_argument(char * argument,fnum_t * totalfiles,struct file_entry ** head,struct program_options * options)168 handle_argument(char *argument, fnum_t *totalfiles, struct file_entry **head,
169 struct program_options *options)
170 {
171 assert(argument != NULL);
172 assert(totalfiles != NULL);
173 assert(head != NULL);
174 assert(options != NULL);
175
176 if(options->arbitrary_values == OPT_ARBITRARYVALUES) {
177 /* handle arbitrary values */
178 fsize_t input_size = 0;
179 char *input_path = NULL;
180 if_not_malloc(input_path, strlen(argument) + 1,
181 return (1);
182 )
183
184 if(sscanf(argument, "%ju %[^\n]", &input_size, input_path) == 2) {
185 if(handle_file_entry(head, input_path, input_size, options) == 0)
186 (*totalfiles)++;
187 else {
188 fprintf(stderr, "%s(): cannot add file entry\n", __func__);
189 free(input_path);
190 return (1);
191 }
192 }
193 else
194 fprintf(stderr, "error parsing input values: %s\n", argument);
195
196 /* cleanup */
197 free(input_path);
198 }
199 else {
200 /* handle paths, must examine filesystem */
201 char *input_path = NULL;
202 size_t input_path_len = strlen(argument);
203 size_t malloc_size = input_path_len + 1;
204 if_not_malloc(input_path, malloc_size,
205 return (1);
206 )
207 snprintf(input_path, malloc_size, "%s", argument);
208
209 /* remove multiple ending slashes */
210 while((input_path_len > 1) &&
211 (input_path[input_path_len - 1] == '/') &&
212 (input_path[input_path_len - 2] == '/')) {
213 input_path[input_path_len - 1] = '\0';
214 input_path_len--;
215 }
216
217 /* crawl path */
218 if(input_path[0] != '\0') {
219 #if defined(DEBUG)
220 fprintf(stderr, "init_file_entries(): examining %s\n",
221 input_path);
222 #endif
223 if(init_file_entries(input_path, head, totalfiles, options) != 0) {
224 fprintf(stderr, "%s(): cannot initialize file entries\n",
225 __func__);
226 free(input_path);
227 return (1);
228 }
229 }
230
231 /* cleanup */
232 free(input_path);
233 }
234
235 return (0);
236 }
237
238 /* Handle options parsing
239 - initializes options structure using argc and argv (through pointers)
240 - returns a value defined by the mask below */
241 static int
handle_options(struct program_options * options,int * argcp,char *** argvp)242 handle_options(struct program_options *options, int *argcp, char ***argvp)
243 {
244 /* Return code mask */
245 #define FPART_OPTS_OK 0 /* OK */
246 #define FPART_OPTS_NOK 1 /* Error */
247 #define FPART_OPTS_EXIT 2 /* exit(3) required */
248 #define FPART_OPTS_USAGE 4 /* usage() call required */
249 #define FPART_OPTS_VERSION 8 /* version() call required */
250
251 assert(options != NULL);
252 assert(argcp != NULL);
253 assert(*argcp > 0);
254 assert(argvp != NULL);
255 assert(*argvp != NULL);
256
257 /* Options handling */
258 extern char *optarg;
259 extern int optind;
260 int ch;
261 while((ch = getopt(*argcp, *argvp,
262 #if defined(_HAS_FNM_CASEFOLD)
263 "hVn:f:s:i:ao:0evlby:Y:x:X:zd:DELw:W:p:q:r:"
264 #else
265 "hVn:f:s:i:ao:0evlby:x:zd:DELw:W:p:q:r:"
266 #endif
267 )) != -1) {
268 switch(ch) {
269 case 'h':
270 return (FPART_OPTS_USAGE | FPART_OPTS_OK | FPART_OPTS_EXIT);
271 case 'V':
272 return (FPART_OPTS_VERSION | FPART_OPTS_OK | FPART_OPTS_EXIT);
273 case 'n':
274 {
275 uintmax_t num_parts = str_to_uintmax(optarg, 0);
276 if(num_parts == 0) {
277 fprintf(stderr,
278 "Option -n requires a value greater than 0.\n");
279 return (FPART_OPTS_USAGE |
280 FPART_OPTS_NOK | FPART_OPTS_EXIT);
281 }
282 options->num_parts = (pnum_t)num_parts;
283 break;
284 }
285 case 'f':
286 {
287 uintmax_t max_entries = str_to_uintmax(optarg, 0);
288 if(max_entries == 0) {
289 fprintf(stderr,
290 "Option -f requires a value greater than 0.\n");
291 return (FPART_OPTS_USAGE |
292 FPART_OPTS_NOK | FPART_OPTS_EXIT);
293 }
294 options->max_entries = (fnum_t)max_entries;
295 break;
296 }
297 case 's':
298 {
299 uintmax_t max_size = str_to_uintmax(optarg, 1);
300 if(max_size == 0) {
301 fprintf(stderr,
302 "Option -s requires a value greater than 0.\n");
303 return (FPART_OPTS_USAGE |
304 FPART_OPTS_NOK | FPART_OPTS_EXIT);
305 }
306 options->max_size = (fsize_t)max_size;
307 break;
308 }
309 case 'i':
310 {
311 /* check for empty argument */
312 if(strlen(optarg) == 0)
313 break;
314 /* replace previous filename if '-i' specified multiple times */
315 if(options->in_filename != NULL)
316 free(options->in_filename);
317 options->in_filename = abs_path(optarg);
318 if(options->in_filename == NULL) {
319 fprintf(stderr, "%s(): cannot determine absolute path for "
320 "file '%s'\n", __func__, optarg);
321 return (FPART_OPTS_NOK | FPART_OPTS_EXIT);
322 }
323 break;
324 }
325 case 'a':
326 options->arbitrary_values = OPT_ARBITRARYVALUES;
327 break;
328 case 'o':
329 {
330 /* check for empty argument */
331 if(strlen(optarg) == 0)
332 break;
333 /* replace previous filename if '-o' specified multiple times */
334 if(options->out_filename != NULL)
335 free(options->out_filename);
336 /* '-' goes to stdout */
337 if((optarg[0] == '-') && (optarg[1] == '\0')) {
338 options->out_filename = NULL;
339 } else {
340 options->out_filename = abs_path(optarg);
341 if(options->out_filename == NULL) {
342 fprintf(stderr, "%s(): cannot determine absolute path "
343 "for file '%s'\n", __func__, optarg);
344 return (FPART_OPTS_NOK | FPART_OPTS_EXIT);
345 }
346 }
347 break;
348 }
349 case '0':
350 options->out_zero = OPT_OUT0;
351 break;
352 case 'e':
353 options->add_slash = OPT_ADDSLASH;
354 break;
355 case 'v':
356 options->verbose++;
357 break;
358 case 'l':
359 options->follow_symbolic_links = OPT_FOLLOWSYMLINKS;
360 break;
361 case 'b':
362 options->cross_fs_boundaries = OPT_NOCROSSFSBOUNDARIES;
363 break;
364 case 'y':
365 case 'Y': /* needs _HAS_FNM_CASEFOLD */
366 case 'x':
367 case 'X': /* needs _HAS_FNM_CASEFOLD */
368 {
369 char ***dst_list = NULL;
370 unsigned int *dst_num = NULL;
371 switch(ch) {
372 case 'y':
373 dst_list = &(options->include_files);
374 dst_num = &(options->ninclude_files);
375 break;
376 case 'Y':
377 dst_list = &(options->include_files_ci);
378 dst_num = &(options->ninclude_files_ci);
379 break;
380 case 'x':
381 dst_list = &(options->exclude_files);
382 dst_num = &(options->nexclude_files);
383 break;
384 case 'X':
385 dst_list = &(options->exclude_files_ci);
386 dst_num = &(options->nexclude_files_ci);
387 break;
388 }
389 /* check for empty argument */
390 if(strlen(optarg) == 0)
391 break;
392 /* push string */
393 if(str_push(dst_list, dst_num, optarg) != 0)
394 return (FPART_OPTS_NOK | FPART_OPTS_EXIT);
395 break;
396 }
397 case 'z':
398 options->dirs_include++;
399 break;
400 case 'd':
401 {
402 char *endptr = NULL;
403 long dir_depth = strtol(optarg, &endptr, 10);
404 /* refuse values < 0 (-1 being used to disable this option) */
405 if((endptr == optarg) || (*endptr != '\0') || (dir_depth < 0))
406 return (FPART_OPTS_USAGE |
407 FPART_OPTS_NOK | FPART_OPTS_EXIT);
408 options->dir_depth = (int)dir_depth;
409 break;
410 }
411 case 'D':
412 options->leaf_dirs = OPT_LEAFDIRS;
413 break;
414 case 'E':
415 options->dirs_only = OPT_DIRSONLY;
416 options->leaf_dirs = OPT_LEAFDIRS;
417 break;
418 case 'L':
419 options->live_mode = OPT_LIVEMODE;
420 break;
421 case 'w':
422 {
423 /* check for empty argument */
424 size_t malloc_size = strlen(optarg) + 1;
425 if(malloc_size <= 1)
426 break;
427 /* replace previous hook if '-w' specified multiple times */
428 if(options->pre_part_hook != NULL)
429 free(options->pre_part_hook);
430 if_not_malloc(options->pre_part_hook, malloc_size,
431 return (FPART_OPTS_NOK | FPART_OPTS_EXIT);
432 )
433 snprintf(options->pre_part_hook, malloc_size, "%s", optarg);
434 break;
435 }
436 case 'W':
437 {
438 /* check for empty argument */
439 size_t malloc_size = strlen(optarg) + 1;
440 if(malloc_size <= 1)
441 break;
442 /* replace previous hook if '-W' specified multiple times */
443 if(options->post_part_hook != NULL)
444 free(options->post_part_hook);
445 if_not_malloc(options->post_part_hook, malloc_size,
446 return (FPART_OPTS_NOK | FPART_OPTS_EXIT);
447 )
448 snprintf(options->post_part_hook, malloc_size, "%s", optarg);
449 break;
450 }
451 case 'p':
452 {
453 uintmax_t preload_size = str_to_uintmax(optarg, 1);
454 if(preload_size == 0) {
455 fprintf(stderr,
456 "Option -p requires a value greater than 0.\n");
457 return (FPART_OPTS_USAGE |
458 FPART_OPTS_NOK | FPART_OPTS_EXIT);
459 }
460 options->preload_size = (fsize_t)preload_size;
461 break;
462 }
463 case 'q':
464 {
465 uintmax_t overload_size = str_to_uintmax(optarg, 1);
466 if(overload_size == 0) {
467 fprintf(stderr,
468 "Option -q requires a value greater than 0.\n");
469 return (FPART_OPTS_USAGE |
470 FPART_OPTS_NOK | FPART_OPTS_EXIT);
471 }
472 options->overload_size = (fsize_t)overload_size;
473 break;
474 }
475 case 'r':
476 {
477 uintmax_t round_size = str_to_uintmax(optarg, 1);
478 if(round_size <= 1) {
479 fprintf(stderr,
480 "Option -r requires a value greater than 1.\n");
481 return (FPART_OPTS_USAGE |
482 FPART_OPTS_NOK | FPART_OPTS_EXIT);
483 }
484 options->round_size = (fsize_t)round_size;
485 break;
486 }
487 case '?':
488 default:
489 return (FPART_OPTS_USAGE | FPART_OPTS_NOK | FPART_OPTS_EXIT);
490 }
491 }
492 *argcp -= optind;
493 *argvp += optind;
494
495 /* check for options consistency */
496 if((options->num_parts == DFLT_OPT_NUM_PARTS) &&
497 (options->max_entries == DFLT_OPT_MAX_ENTRIES) &&
498 (options->max_size == DFLT_OPT_MAX_SIZE)) {
499 fprintf(stderr, "Please specify either -n, -f or -s.\n");
500 return (FPART_OPTS_USAGE | FPART_OPTS_NOK | FPART_OPTS_EXIT);
501 }
502
503 if((options->num_parts != DFLT_OPT_NUM_PARTS) &&
504 ((options->max_entries != DFLT_OPT_MAX_ENTRIES) ||
505 (options->max_size != DFLT_OPT_MAX_SIZE) ||
506 (options->live_mode != DFLT_OPT_LIVEMODE))) {
507 fprintf(stderr,
508 "Option -n is incompatible with options -f, -s and -L.\n");
509 return (FPART_OPTS_USAGE | FPART_OPTS_NOK | FPART_OPTS_EXIT);
510 }
511
512 if(options->arbitrary_values == OPT_ARBITRARYVALUES) {
513 if((options->add_slash != DFLT_OPT_ADDSLASH) ||
514 (options->follow_symbolic_links != DFLT_OPT_FOLLOWSYMLINKS) ||
515 (options->cross_fs_boundaries != DFLT_OPT_CROSSFSBOUNDARIES) ||
516 (options->include_files != NULL) ||
517 (options->include_files_ci != NULL) ||
518 (options->exclude_files != NULL) ||
519 (options->exclude_files_ci != NULL) ||
520 (options->dirs_include != DFLT_OPT_DIRSINCLUDE) ||
521 (options->dir_depth != DFLT_OPT_DIR_DEPTH) ||
522 (options->leaf_dirs != DFLT_OPT_LEAFDIRS) ||
523 (options->dirs_only != DFLT_OPT_DIRSONLY)) {
524 fprintf(stderr,
525 "Option -a is incompatible with crawling-related options.\n");
526 return (FPART_OPTS_USAGE | FPART_OPTS_NOK | FPART_OPTS_EXIT);
527 }
528 }
529
530 if((options->out_zero == OPT_OUT0) &&
531 options->out_filename == NULL) {
532 fprintf(stderr,
533 "Option -0 is valid only when used with option -o.\n");
534 return (FPART_OPTS_USAGE | FPART_OPTS_NOK | FPART_OPTS_EXIT);
535 }
536
537 /* We do not want to mix -E and -d as directory sizes are computed
538 differently for those options: -E produces a single-depth total while -d
539 computes a recursive total */
540 if((options->dirs_only == OPT_DIRSONLY) &&
541 (options->dir_depth != DFLT_OPT_DIR_DEPTH)) {
542 fprintf(stderr,
543 "Option -E is incompatible with option -d.\n");
544 return (FPART_OPTS_USAGE | FPART_OPTS_NOK | FPART_OPTS_EXIT);
545 }
546
547 /* Options -D and -E imply empty dirs request (option -z) */
548 if(options->leaf_dirs == OPT_LEAFDIRS)
549 options->dirs_include = max(options->dirs_include, OPT_EMPTYDIRS);
550
551 if((options->live_mode == OPT_NOLIVEMODE) &&
552 ((options->pre_part_hook != NULL) ||
553 (options->post_part_hook != NULL))) {
554 fprintf(stderr,
555 "Hooks can only be used with option -L.\n");
556 return (FPART_OPTS_USAGE | FPART_OPTS_NOK | FPART_OPTS_EXIT);
557 }
558
559 if((options->in_filename == NULL) && (*argcp <= 0)) {
560 /* no file specified, force stdin */
561 char *opt_input = "-";
562 size_t malloc_size = strlen(opt_input) + 1;
563 if_not_malloc(options->in_filename, malloc_size,
564 return (FPART_OPTS_NOK | FPART_OPTS_EXIT);
565 )
566 snprintf(options->in_filename, malloc_size, "%s", opt_input);
567 }
568
569 return (FPART_OPTS_OK);
570 }
571
main(int argc,char ** argv)572 int main(int argc, char **argv)
573 {
574 fnum_t totalfiles = 0;
575
576 /******************
577 Handle options
578 ******************/
579
580 /* Program options */
581 struct program_options options;
582
583 /* Set default options */
584 init_options(&options);
585
586 /* Parse and initialize options */
587 int options_init_res = handle_options(&options, &argc, &argv);
588
589 if(options_init_res & FPART_OPTS_USAGE)
590 usage();
591 if(options_init_res & FPART_OPTS_VERSION)
592 version();
593 if(options_init_res & FPART_OPTS_EXIT) {
594 uninit_options(&options);
595 exit(options_init_res & FPART_OPTS_NOK ?
596 EXIT_FAILURE : EXIT_SUCCESS);
597 }
598
599 /**************
600 Handle stdin
601 ***************/
602
603 /* our main double-linked file list */
604 struct file_entry *head = NULL;
605
606 if(options.verbose >= OPT_VERBOSE)
607 fprintf(stderr, "Examining filesystem...\n");
608
609 /* work on each file provided through input file (or stdin) */
610 if(options.in_filename != NULL) {
611 /* handle fd opening */
612 FILE *in_fp = NULL;
613 if((options.in_filename[0] == '-') &&
614 (options.in_filename[1] == '\0')) {
615 /* working from stdin */
616 in_fp = stdin;
617 }
618 else {
619 /* working from a filename */
620 if((in_fp = fopen(options.in_filename, "r")) == NULL) {
621 fprintf(stderr, "%s: %s\n", options.in_filename,
622 strerror(errno));
623 uninit_options(&options);
624 exit(EXIT_FAILURE);
625 }
626 }
627
628 /* read fd and do the work */
629 char line[MAX_LINE_LENGTH];
630 char *line_end_p = NULL;
631 bzero(line, MAX_LINE_LENGTH);
632 while(fgets(line, MAX_LINE_LENGTH, in_fp) != NULL) {
633 /* replace '\n' with '\0' */
634 if ((line_end_p = strchr(line, '\n')) != NULL)
635 *line_end_p = '\0';
636
637 if(handle_argument(line, &totalfiles, &head, &options) != 0) {
638 uninit_file_entries(head, &options);
639 uninit_options(&options);
640 exit(EXIT_FAILURE);
641 }
642
643 /* cleanup */
644 bzero(line, MAX_LINE_LENGTH);
645 }
646
647 /* check for error reading input */
648 if(ferror(in_fp) != 0) {
649 fprintf(stderr, "error reading from input stream\n");
650 }
651
652 /* cleanup */
653 if(in_fp != NULL)
654 fclose(in_fp);
655 }
656
657 /******************
658 Handle arguments
659 *******************/
660
661 /* now, work on each path provided as arguments */
662 int i;
663 for(i = 0 ; i < argc ; i++) {
664 if(handle_argument(argv[i], &totalfiles, &head, &options) != 0) {
665 uninit_file_entries(head, &options);
666 uninit_options(&options);
667 exit(EXIT_FAILURE);
668 }
669 }
670
671 /****************
672 Display status
673 *****************/
674
675 /* come back to the first element */
676 rewind_list(head);
677
678 /* no file found or live mode */
679 if((totalfiles == 0) || (options.live_mode == OPT_LIVEMODE)) {
680 uninit_file_entries(head, &options);
681 /* display status */
682 if(options.verbose >= OPT_VERBOSE)
683 fprintf(stderr, "%ju file(s) found.\n", totalfiles);
684 uninit_options(&options);
685 exit(EXIT_SUCCESS);
686 }
687
688 /* display status */
689 if(options.verbose >= OPT_VERBOSE) {
690 fprintf(stderr, "%ju file(s) found.\n", totalfiles);
691 fprintf(stderr, "Sorting entries...\n");
692 }
693
694 /************************************************
695 Sort entries with a fixed number of partitions
696 *************************************************/
697
698 /* our list of partitions */
699 struct partition *part_head = NULL;
700 pnum_t num_parts = options.num_parts;
701
702 /* sort files with a fixed size of partitions */
703 if(options.num_parts != DFLT_OPT_NUM_PARTS) {
704 /* create a fixed-size array of pointers to sort */
705 struct file_entry **file_entry_p = NULL;
706
707 if_not_malloc(file_entry_p, sizeof(struct file_entry *) * totalfiles,
708 uninit_file_entries(head, &options);
709 uninit_options(&options);
710 exit(EXIT_FAILURE);
711 )
712
713 /* initialize array */
714 init_file_entry_p(file_entry_p, totalfiles, head);
715
716 /* sort array */
717 qsort(&file_entry_p[0], totalfiles, sizeof(struct file_entry *),
718 &sort_file_entry_p);
719
720 /* create a double_linked list of partitions
721 which will hold dispatched files */
722 if(add_partitions(&part_head, options.num_parts, &options) != 0) {
723 fprintf(stderr, "%s(): cannot init list of partitions\n",
724 __func__);
725 uninit_partitions(part_head);
726 free(file_entry_p);
727 uninit_file_entries(head, &options);
728 uninit_options(&options);
729 exit(EXIT_FAILURE);
730 }
731 /* come back to the first element */
732 rewind_list(part_head);
733
734 /* dispatch files */
735 if(dispatch_file_entry_p_by_size
736 (file_entry_p, totalfiles, part_head, options.num_parts) != 0) {
737 fprintf(stderr, "%s(): unable to dispatch file entries\n",
738 __func__);
739 uninit_partitions(part_head);
740 free(file_entry_p);
741 uninit_file_entries(head, &options);
742 uninit_options(&options);
743 exit(EXIT_FAILURE);
744 }
745
746 /* re-dispatch empty files */
747 if(dispatch_empty_file_entries
748 (head, totalfiles, part_head, options.num_parts) != 0) {
749 fprintf(stderr, "%s(): unable to dispatch empty file entries\n",
750 __func__);
751 uninit_partitions(part_head);
752 free(file_entry_p);
753 uninit_file_entries(head, &options);
754 uninit_options(&options);
755 exit(EXIT_FAILURE);
756 }
757
758 /* cleanup */
759 free(file_entry_p);
760 }
761
762 /***************************************************
763 Sort entries with a variable number of partitions
764 ****************************************************/
765
766 /* sort files with a file number or size limit per-partitions.
767 In this case, partitions are dynamically-created */
768 else {
769 if((num_parts = dispatch_file_entries_by_limits
770 (head, &part_head, options.max_entries, options.max_size,
771 &options)) == 0) {
772 fprintf(stderr, "%s(): unable to dispatch file entries\n",
773 __func__);
774 uninit_partitions(part_head);
775 uninit_file_entries(head, &options);
776 uninit_options(&options);
777 exit(EXIT_FAILURE);
778 }
779 /* come back to the first element
780 (we may have exited with part_head set to partition 1,
781 after default partition) */
782 rewind_list(part_head);
783 }
784
785 /***********************
786 Print result and exit
787 ************************/
788
789 /* print result summary */
790 print_partitions(part_head);
791
792 if(options.verbose >= OPT_VERBOSE)
793 fprintf(stderr, "Writing output lists...\n");
794
795 /* print file entries */
796 print_file_entries(head, num_parts, &options);
797
798 if(options.verbose >= OPT_VERBOSE)
799 fprintf(stderr, "Cleaning up...\n");
800
801 /* free stuff */
802 uninit_partitions(part_head);
803 uninit_file_entries(head, &options);
804 uninit_options(&options);
805 exit(EXIT_SUCCESS);
806 }
807