1 /* ----------------------------------------------------------------------- *
2  *
3  *   Copyright 2010-2011 Gene Cumm
4  *
5  *   This program is free software; you can redistribute it and/or modify
6  *   it under the terms of the GNU General Public License as published by
7  *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
8  *   Boston MA 02111-1307, USA; either version 2 of the License, or
9  *   (at your option) any later version; incorporated herein by reference.
10  *
11  * ----------------------------------------------------------------------- */
12 
13 /*
14  * cptime.c	Version 1.4
15  *
16  * Timed copy; read entire file then output total time, bytes transferred,
17  * and compute transfer rate.
18  *
19  * cptime [-s|-l] [-v|-q] [-b _SIZE_] [-n _LEN_] _FILE_...
20  *	-s	Change to simple output mode without computing transfer rate
21  *	-l	Change to long output mode (to allow for overriding previous -s)
22  *	-v	Verbose output
23  *	-q	Quiet output
24  *	-b _SIZE_	use _SIZE_ for transfer size
25  *	-n _LEN_	maximum length to fetch
26  *	_FILE_...	Space delimited list of files to dump
27  * Note: The last instance of -s or -l wins, along with the last use of -b and -n and the winning option will be applied to all operations
28  *
29  * Hisory:
30  * 1.4	Use fread() rather than read(); use CLK_TCK when available.
31  * 1.3	Added -v/-q; rework some argument processing.
32  * 1.2	Added -n
33  * 1.1	Added -l and -b switches; more flexible command line processing
34  * 1.0	First release
35  */
36 
37 /*
38  * ToDos:
39  * - Refine timing to be more precise.  Low priority.
40  * - Add -o for offset.  Wishlist.
41  */
42 
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <errno.h>
46 #include <fcntl.h>
47 #include <unistd.h>
48 #include <sys/times.h>
49 #include <consoles.h>
50 #include <minmax.h>
51 #include <limits.h>
52 #include <string.h>
53 #include <stdint.h>
54 #include <console.h>
55 
56 #ifdef __COM32__
57 #  define BUFSZ_DEF	(size_t)2048
58 /* What's optimal?  Under 4k?
59  *	layout.inc: xfer_buf_seg	equ 1000h
60  *	com32.inc: push dword (1 << 16)		; 64K bounce buffer
61  */
62 /* typedef size_t off_t */
63 
64 #  define TPS_T	float
65 #  ifdef CLK_TCK
get_tps(void)66 static inline TPS_T get_tps(void) {	return CLK_TCK;	}
67 #  else
get_tps(void)68 static inline TPS_T get_tps(void) {	return 18.2;	}
69 #  endif
70 
71 #else /* __COM32__ */
72 
73 #  define BUFSZ_DEF	(size_t)16384
74 /* Need to check what might be a "best" buffer/fetch block size here */
75 
76 #  define TPS_T	long
get_tps(void)77 static inline TPS_T get_tps(void) {	return sysconf(_SC_CLK_TCK);	}
78 
79 #endif /* __COM32__ */
80 
81 #ifndef SSIZE_MAX
82 #  define SSIZE_MAX	PTRDIFF_MAX
83 #endif
84 /* typedef ptrdiff_t ssize_t; */
85 #define BUFSZ_MAX	(size_t)SSIZE_MAX
86 /* ssize_t max */
87 #define BUFSZ_MIN	(size_t)1
88 
89 
90 /* Please note: I don't know the origin of these two macros nor their license */
91 #define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
92 #define TYPE_MAX(t) \
93   ((t) (! TYPE_SIGNED (t) \
94         ? (t) -1 \
95 	: ~ (~ (t) 0 << (sizeof (t) * CHAR_BIT - 1))))
96 
97 #ifndef OFF_T_MAX
98 #  define OFF_T_MAX	TYPE_MAX(off_t)
99 #endif
100 /* Can't be SIZE_MAX or SSIZE_MAX as Syslinux/COM32 is unsigned while Linux
101  * is signed.
102  */
103 
104 #define LEN_MAX		OFF_T_MAX
105 /* off_t max */
106 #define LEN_MIN		(off_t)0
107 
print_cp_result_tick(size_t bcnt,clock_t et,TPS_T tps,int offs)108 void print_cp_result_tick(size_t bcnt, clock_t et, TPS_T tps, int offs)
109 {
110 	size_t dr;
111 	/* prevent divide by 0 */
112 	dr = max(bcnt, (bcnt * tps)) / max((clock_t)1, (et + offs));
113 	printf("  %+d %zu B/s; %zu KiB/s; %zu MiB/s\n", offs, dr, dr/1024, dr/1048576);
114 }	/* void print_cp_result_tick(size_t bcnt, clock_t et, TPS_T tps, int offs) */
115 
print_cp_result_long(char * fn,size_t bcnt,clock_t bc,clock_t ec,size_t bufsz,char do_verbose)116 void print_cp_result_long(char *fn, size_t bcnt, clock_t bc, clock_t ec, size_t bufsz, char do_verbose)
117 {
118 	TPS_T tps;
119 	if (do_verbose > 2)
120 		printf("Enter print_cp_result_long()\n");
121 	tps = get_tps();
122 	printf("  %zu B in %d ticks from '%s'\n", bcnt, (int)(ec - bc), fn);
123 	printf("  ~%d ticks per second; %zu B block/transfer size\n", (int)tps, bufsz);
124 	print_cp_result_tick(bcnt, (ec - bc), tps, 0);
125 	print_cp_result_tick(bcnt, (ec - bc), tps, 1);
126 	print_cp_result_tick(bcnt, (ec - bc), tps, -1);
127 }	/* void print_cp_result_long(char *fn, size_t bcnt, clock_t bc, clock_t ec, size_t bufsz) */
128 
print_cp_result_simple(char * fn,size_t bcnt,clock_t bc,clock_t ec,size_t bufsz,char do_verbose)129 void print_cp_result_simple(char *fn, size_t bcnt, clock_t bc, clock_t ec, size_t bufsz, char do_verbose)
130 {
131 	if (do_verbose) {}
132 	printf("  %zuB  %dt %zux '%s'\n", bcnt, (int)(ec - bc), bufsz, fn);
133 }	/* void print_cp_result_simple(char *fn, int bcnt, clock_t bc, clock_t ec, char do_verbose) */
134 
time_copy_bufsz(size_t bufsz,size_t bcnt,off_t maxlen)135 size_t time_copy_bufsz(size_t bufsz, size_t bcnt, off_t maxlen)
136 {
137 	return min(bufsz, (maxlen - bcnt));
138 }	/* size_t time_copy_bufsz(size_t bufsz, size_t bcnt, off_t maxlen) */
139 
time_copy(char * fn,char do_simple,char do_verbose,size_t ibufsz,off_t maxlen)140 int time_copy(char *fn, char do_simple, char do_verbose, size_t ibufsz, off_t maxlen)
141 {
142 // 	int fd;
143 	int rv = 0;
144 	int i = 0;
145 	FILE *f;
146 	size_t bufsz, bcnt = 0;
147 	int numrd;
148 	struct tms tm;
149 	clock_t bc, ec;
150 	char buf[ibufsz + 1];
151 
152 	buf[0] = 0;
153 	if (do_verbose)
154 		printf("Trying file '%s'\n", fn);
155 	errno = 0;
156 // 	fd = open(fn, O_RDONLY);
157 	f = fopen(fn, "r");
158 // 	if (fd == -1) {
159 	if (!f) {
160 		switch (errno) {
161 		case ENOENT :
162 			printf("File '%s' does not exist\n", fn);
163 			break;
164 		case EBADF:
165 			printf("File '%s': Bad File Descriptor\n", fn);
166 			break;
167 		default :
168 			printf("Error '%d' opening file '%s'\n", errno, fn);
169 		}
170 		rv = 1;
171 	} else {
172 		if (do_verbose)
173 			printf("File '%s' opened\n", fn);
174 		bufsz = time_copy_bufsz(ibufsz, bcnt, maxlen);
175 		bc = times(&tm);
176 // 		numrd = read(fd, buf, bufsz);
177 // 		numrd = fread(buf, bufsz, 1, f);
178 		numrd = fread(buf, 1, bufsz, f);
179 		i++;
180 		if (numrd > 0)
181 			bcnt = numrd;
182 		while ((numrd > 0) && (bufsz > 0)) {
183 			bufsz = time_copy_bufsz(bufsz, bcnt, maxlen);
184 // 			numrd = read(fd, buf, bufsz);
185 // 			numrd = fread(buf, bufsz, 1, f);
186 			numrd = fread(buf, 1, bufsz, f);
187 			i++;
188 			if (numrd >= 0)
189 // 				bcnt = bcnt + numrd;
190 				bcnt += numrd;
191 		}
192 		ec = times(&tm);
193 // 		close(fd);
194 		fclose(f);
195 		if (do_verbose)
196 			printf("File '%s' closed\n", fn);
197 		if (numrd < 0) {
198 			switch (errno) {
199 			case EIO :
200 				printf("IO Error at %zu B reading file '%s'\n", bcnt, fn);
201 				break;
202 			case EINVAL :
203 				printf("Invalid Mode at %zu B reading file '%s'\n", bcnt, fn);
204 				break;
205 			default :
206 				printf("Error '%d' at %zu B reading file '%s'\n", errno, bcnt, fn);
207 			}
208 			rv = 2;
209 		}
210 		if (bcnt > 0) {
211 			if (bufsz == 0)
212 				printf("maxed out on maxln\n");
213 			if (do_simple)
214 				print_cp_result_simple(fn, bcnt, bc, ec, ibufsz, do_verbose);
215 			else
216 				print_cp_result_long(fn, bcnt, bc, ec, ibufsz, do_verbose);
217 		}
218 		if (do_verbose)
219 			printf("  numrd %d bcnt %d bufsz %d i %d\n", numrd, bcnt, bufsz, i);
220 	}
221 	return rv;
222 }	/* int time_copy(char *fn, char do_simple, int bufsz, off_t maxlen) */
223 
main(int argc,char * argv[])224 int main(int argc, char *argv[])
225 {
226 	int i;
227 	char do_simple = 0, do_pbuf = 0, do_plen = 0, do_verbose = 0;
228 	char *arg;
229 	size_t tbufsz, bufsz = min((BUFSZ_DEF), (BUFSZ_MAX));
230 	off_t tmaxlen, maxlen = LEN_MAX;
231 	int numfl = 0;
232 	console_ansi_std();
233 // 	openconsole(&dev_stdcon_r, &dev_stdcon_w);
234 	for (i = 1; i < argc; i++) {
235 		if (argv[i][0] == '-') {
236 			arg = argv[i] + 1;
237 			if (strcmp(arg, "b") == 0) {
238 				i++;
239 				if (i < argc) {
240 					tbufsz = atoi(argv[i]);
241 					if (tbufsz > 0)
242 						bufsz = min(max((BUFSZ_MIN), tbufsz), (BUFSZ_MAX));
243 					do_pbuf = 1;
244 				}
245 			} else if (strcmp(arg, "n") == 0) {
246 				i++;
247 				if (i < argc) {
248 					tmaxlen = atoi(argv[i]);
249 					if (tmaxlen > 0)
250 						maxlen = min(max((LEN_MIN), tmaxlen), (LEN_MAX));
251 					do_plen = 1;
252 				}
253 			} else if (strcmp(arg, "s") == 0)
254 				do_simple = 1;
255 			else if (strcmp(arg, "l") == 0)
256 				do_simple = 0;
257 			else if (strcmp(arg, "v") == 0)
258 				do_verbose = 1;
259 			else if (strcmp(arg, "q") == 0)
260 				do_verbose = 0;
261 		}
262 	}
263 	if (do_pbuf || do_verbose)
264 		printf("Using bufsz %zu\n", bufsz);
265 	if (do_plen || do_verbose)
266 		printf("Using maxlen %zu\n", maxlen);
267 	for (i = 1; i < argc; i++) {
268 		if (argv[i][0] == '-') {
269 			arg = argv[i] + 1;
270 			if ((strcmp(arg, "b") == 0) || (strcmp(arg, "n") == 0))
271 				i++; 	/* Skip next arg */
272 			else if (!((strcmp(arg, "s") == 0) || (strcmp(arg, "l") == 0) || (strcmp(arg, "v") == 0) || (strcmp(arg, "q") == 0))) {
273 				time_copy(argv[i], do_simple, do_verbose, bufsz, maxlen);
274 				numfl++;
275 			}
276 		} else {
277 			time_copy(argv[i], do_simple, do_verbose, bufsz, maxlen);
278 			numfl++;
279 		}
280 	}
281 	if (numfl == 0)
282 		fprintf(stderr, "%s: Please specify a file\n", argv[0]);
283 	return 0;
284 }	/* int main(int argc, char *argv[]) */
285