1// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
2//
3//  Copyright (c) 2001-2003, Haiku
4//
5//  This software is part of the Haiku distribution and is covered
6//  by the MIT License.
7//
8//
9//  File:        unchop.c
10//  Author:      Daniel Reinhold (danielre@users.sf.net)
11//  Description: recreates a file previously split with chop
12//
13// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
14
15#include <OS.h>
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19#include <errno.h>
20#include <fcntl.h>
21#include <unistd.h>
22#include <sys/stat.h>
23
24
25void  append_file (int, int);
26void  do_unchop   (char *, char *);
27void  replace     (char *, char *);
28char *temp_file   (void);
29void  usage       (void);
30bool  valid_file  (char *);
31
32
33// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
34// globals
35
36#define BLOCKSIZE 64 * 1024     // file data is read in BLOCKSIZE blocks
37static char Block[BLOCKSIZE];   // and stored in the global Block array
38
39static int Errors = 0;
40
41
42
43void
44usage()
45{
46	printf("Usage: unchop file\n");
47	printf("Concatenates files named file00, file01... into file\n");
48}
49
50
51int
52main(int argc, char *argv[])
53{
54	if (argc != 2) {
55		usage();
56		return 0;
57	}
58	else {
59		char *origfile = argv[1];
60		char *tmpfile  = origfile;
61		bool  needs_replace = false;
62
63		if (valid_file(origfile)) {
64			// output file already exists -- write to temp file
65			tmpfile = temp_file();
66			needs_replace = true;
67		}
68
69		do_unchop(tmpfile, origfile);
70
71		if (needs_replace) {
72			if (Errors == 0)
73				replace(origfile, tmpfile);
74			else
75				remove(tmpfile);
76		}
77	}
78
79	return Errors;
80}
81
82
83void
84do_unchop(char *outfile, char *basename)
85{
86	int fdout = open(outfile, O_WRONLY|O_CREAT|O_APPEND);
87	if (fdout < 0)
88		fprintf(stderr, "can't open '%s': %s\n", outfile, strerror(errno));
89
90	else {
91		int  i;
92		char fnameN[256];
93
94		for (i = 0; i < 999999; ++i) {
95			sprintf(fnameN,  "%s%02d", basename, i);
96
97			if (valid_file(fnameN)) {
98				int fdin = open(fnameN, O_RDONLY);
99				if (fdin < 0) {
100					fprintf(stderr, "can't open '%s': %s\n", fnameN, strerror(errno));
101					++Errors;
102				} else {
103					append_file(fdin, fdout);
104					close(fdin);
105				}
106			} else {
107				if (i == 0)
108					printf("No chunk files present (%s)", fnameN);
109				break;
110			}
111		}
112		close(fdout);
113	}
114}
115
116
117void
118append_file(int fdin, int fdout)
119{
120	// appends the entire contents of the input file
121	// to the output file
122
123	ssize_t got;
124
125	for (;;) {
126		got = read(fdin, Block, BLOCKSIZE);
127		if (got <= 0)
128			break;
129
130		write(fdout, Block, got);
131	}
132}
133
134
135bool
136valid_file(char *fname)
137{
138	// for this program, a valid file is one that:
139	//   a) exists (that always helps)
140	//   b) is a regular file (not a directory, link, etc.)
141
142	struct stat e;
143
144	if (stat(fname, &e) == -1) {
145		// no such file
146		return false;
147	}
148
149	return (S_ISREG(e.st_mode));
150}
151
152
153void
154replace(char *origfile, char *newfile)
155{
156	// replace the contents of the original file
157	// with the contents of the new file
158
159	char buf[1000];
160
161	// delete the original file
162	remove(origfile);
163
164	// rename the new file to the original file name
165	sprintf(buf, "mv \"%s\" \"%s\"", newfile, origfile);
166	system(buf);
167}
168
169
170char *
171temp_file(void)
172{
173	// creates a new, temporary file and returns its name
174
175	char *tmp = tmpnam(NULL);
176
177	FILE *fp = fopen(tmp, "w");
178	fclose(fp);
179
180	return tmp;
181}
182