diff options
author | dotdotisdead <dotdotisdead@a3eca27d-f21b-0410-9b4a-6511e771f64e> | 2006-08-27 18:39:38 +0000 |
---|---|---|
committer | dotdotisdead <dotdotisdead@a3eca27d-f21b-0410-9b4a-6511e771f64e> | 2006-08-27 18:39:38 +0000 |
commit | a6f202275ec093b9f8948d77b9783f0820f930d8 (patch) | |
tree | 95974d74bd0a6577e80ee2eb917cd852ce6ba011 /xdelta3/rcs_junk.cc |
Source snapshot... broken.
Diffstat (limited to 'xdelta3/rcs_junk.cc')
-rwxr-xr-x | xdelta3/rcs_junk.cc | 1861 |
1 files changed, 1861 insertions, 0 deletions
diff --git a/xdelta3/rcs_junk.cc b/xdelta3/rcs_junk.cc new file mode 100755 index 0000000..ac49644 --- /dev/null +++ b/xdelta3/rcs_junk.cc | |||
@@ -0,0 +1,1861 @@ | |||
1 | typedef struct _RcsWalker RcsWalker; | ||
2 | typedef struct _RcsFile RcsFile; | ||
3 | typedef struct _RcsVersion RcsVersion; | ||
4 | typedef struct _RcsStats RcsStats; | ||
5 | typedef struct _IntStat IntStat; | ||
6 | typedef struct _DblStat DblStat; | ||
7 | typedef struct _BinCounter BinCounter; | ||
8 | typedef struct _ConfigOption ConfigOption; | ||
9 | |||
10 | struct _RcsWalker { | ||
11 | void* (* initialize) (void); | ||
12 | int (* finalize) (RcsStats* stats, void* data); | ||
13 | int (* onefile) (RcsFile* rcs, RcsStats* stats, void* data); | ||
14 | int (* dateorder) (RcsFile* rcs, RcsVersion* v, void* data); | ||
15 | int (* delta_orig) (RcsFile* rcs, RcsVersion* from, RcsVersion *to, void* data); | ||
16 | int (* delta_date) (RcsFile* rcs, RcsVersion* from, RcsVersion *to, void* data); | ||
17 | int min_versions; | ||
18 | int max_versions; | ||
19 | gboolean write_files; | ||
20 | }; | ||
21 | |||
22 | struct _RcsVersion { | ||
23 | RcsFile *rcs; | ||
24 | time_t date; | ||
25 | int dateseq; | ||
26 | int chain_length; | ||
27 | char *vname; | ||
28 | off_t size; | ||
29 | int cc; | ||
30 | guint8* segment; | ||
31 | char *filename; | ||
32 | RcsVersion *parent; | ||
33 | GSList *children; | ||
34 | guint on_trunk : 1; | ||
35 | }; | ||
36 | |||
37 | struct _RcsFile { | ||
38 | char *filename; | ||
39 | char *copyname; | ||
40 | char *headname; | ||
41 | |||
42 | int version_count; | ||
43 | int forward_count; | ||
44 | int reverse_count; | ||
45 | int branch_count; | ||
46 | |||
47 | RcsVersion *versions; | ||
48 | RcsVersion **versions_date; | ||
49 | |||
50 | RcsVersion *head_version; | ||
51 | RcsVersion *root_version; | ||
52 | |||
53 | off_t total_size; | ||
54 | |||
55 | guint atflag : 1; | ||
56 | }; | ||
57 | |||
58 | struct _RcsStats { | ||
59 | BinCounter *avg_version_size; | ||
60 | IntStat* version_stat; | ||
61 | IntStat* forward_stat; | ||
62 | IntStat* reverse_stat; | ||
63 | IntStat* branch_stat; | ||
64 | IntStat* unencoded_stat; | ||
65 | IntStat* literal_stat; | ||
66 | }; | ||
67 | |||
68 | struct _IntStat { | ||
69 | const char* name; | ||
70 | int count; | ||
71 | long long sum; | ||
72 | long long min; | ||
73 | long long max; | ||
74 | |||
75 | GArray *values; | ||
76 | }; | ||
77 | |||
78 | struct _DblStat { | ||
79 | const char* name; | ||
80 | int count; | ||
81 | double sum; | ||
82 | double min; | ||
83 | double max; | ||
84 | |||
85 | GArray *values; | ||
86 | }; | ||
87 | |||
88 | struct _BinCounter { | ||
89 | const char *name; | ||
90 | GPtrArray *bins; | ||
91 | }; | ||
92 | |||
93 | enum _ConfigArgument { | ||
94 | CO_Required, | ||
95 | CO_Optional, | ||
96 | CO_None | ||
97 | }; | ||
98 | |||
99 | typedef enum _ConfigArgument ConfigArgument; | ||
100 | |||
101 | enum _ConfigOptionType { | ||
102 | CD_Bool, | ||
103 | CD_Int32, | ||
104 | CD_Double, | ||
105 | CD_String | ||
106 | }; | ||
107 | |||
108 | typedef enum _ConfigOptionType ConfigOptionType; | ||
109 | |||
110 | enum _ConfigStyle { | ||
111 | CS_Ignore, | ||
112 | CS_UseAsFile, | ||
113 | CS_Use | ||
114 | }; | ||
115 | |||
116 | typedef enum _ConfigStyle ConfigStyle; | ||
117 | |||
118 | struct _ConfigOption { | ||
119 | const char *name; | ||
120 | const char *abbrev; | ||
121 | ConfigStyle style; | ||
122 | ConfigArgument arg; | ||
123 | ConfigOptionType type; | ||
124 | void *value; | ||
125 | gboolean found; | ||
126 | }; | ||
127 | |||
128 | /* RCS inspection stuff | ||
129 | */ | ||
130 | |||
131 | void rcswalk_init (void); | ||
132 | int rcswalk (RcsWalker *walker, const char* copy_base); | ||
133 | void rcswalk_report (RcsStats* stats); | ||
134 | |||
135 | IntStat* stat_int_new (const char* name); | ||
136 | void stat_int_add_item (IntStat* stat, long long v); | ||
137 | void stat_int_report (IntStat* stat); | ||
138 | |||
139 | DblStat* stat_dbl_new (const char* name); | ||
140 | void stat_dbl_add_item (DblStat* stat, double v); | ||
141 | void stat_dbl_report (DblStat* stat); | ||
142 | |||
143 | BinCounter* stat_bincount_new (const char* name); | ||
144 | void stat_bincount_add_item (BinCounter* bc, int bin, double val); | ||
145 | void stat_bincount_report (BinCounter* bc); | ||
146 | |||
147 | /* Experiment configuration stuff | ||
148 | */ | ||
149 | |||
150 | void config_register (ConfigOption *opts, int nopts); | ||
151 | int config_parse (const char* config_file); | ||
152 | int config_done (void); | ||
153 | void config_help (void); | ||
154 | void config_set_string (const char* var, const char* val); | ||
155 | int config_clear_dir (const char* dir); | ||
156 | int config_create_dir (const char* dir); | ||
157 | FILE* config_output (const char* fmt, ...); | ||
158 | |||
159 | #ifdef __cplusplus | ||
160 | } | ||
161 | #endif | ||
162 | |||
163 | #endif | ||
164 | #include "rcswalk.h" | ||
165 | #include "edsio.h" | ||
166 | #include <stdio.h> | ||
167 | #include <stdlib.h> | ||
168 | #include <string.h> | ||
169 | #include <sys/types.h> | ||
170 | #include <sys/stat.h> | ||
171 | #include <sys/wait.h> | ||
172 | #include <fcntl.h> | ||
173 | #include <errno.h> | ||
174 | #include <dirent.h> | ||
175 | #include <unistd.h> | ||
176 | #include <math.h> | ||
177 | |||
178 | #undef BUFSIZE | ||
179 | #define BUFSIZE (1<<14) | ||
180 | |||
181 | char *tmp_file_1; | ||
182 | gboolean tmp_file_1_free = TRUE; | ||
183 | char *tmp_file_2; | ||
184 | gboolean tmp_file_2_free = TRUE; | ||
185 | |||
186 | int skip_count; | ||
187 | int small_count; | ||
188 | int large_count; | ||
189 | int process_count; | ||
190 | |||
191 | extern time_t str2time (char const *, time_t, long); | ||
192 | |||
193 | static guint8 readbuf[BUFSIZE]; | ||
194 | |||
195 | static const char* rcswalk_input_dir = NULL; | ||
196 | static const char* config_output_base = NULL; | ||
197 | static const char* config_output_dir = NULL; | ||
198 | static const char* rcswalk_experiment = NULL; | ||
199 | |||
200 | static ConfigOption rcswalk_options[] = { | ||
201 | { "rcswalk_experiment", "ex", CS_Use, CO_Required, CD_String, & rcswalk_experiment }, | ||
202 | { "rcs_input_dir", "id", CS_UseAsFile, CO_Required, CD_String, & rcswalk_input_dir } | ||
203 | }; | ||
204 | |||
205 | static ConfigOption config_options[] = { | ||
206 | { "config_output_base", "ob", CS_Ignore, CO_Required, CD_String, & config_output_base } | ||
207 | }; | ||
208 | |||
209 | |||
210 | void | ||
211 | rcswalk_free_segment (RcsVersion *v) | ||
212 | { | ||
213 | if (v->segment) | ||
214 | g_free (v->segment); | ||
215 | |||
216 | if (v->filename == tmp_file_1) | ||
217 | tmp_file_1_free = TRUE; | ||
218 | else if (v->filename == tmp_file_2) | ||
219 | tmp_file_2_free = TRUE; | ||
220 | else if (v->filename) | ||
221 | g_free (v->filename); | ||
222 | |||
223 | v->segment = NULL; | ||
224 | v->filename = NULL; | ||
225 | } | ||
226 | |||
227 | int | ||
228 | rcswalk_checkout (RcsFile* rcs, RcsWalker* walker, RcsVersion *v) | ||
229 | { | ||
230 | FILE* out; | ||
231 | char cmdbuf[1024]; | ||
232 | int nread; | ||
233 | int alloc = BUFSIZE; | ||
234 | int pos = 0; | ||
235 | |||
236 | sprintf (cmdbuf, "co -ko -p%s %s 2>/dev/null\n", v->vname, rcs->filename); | ||
237 | |||
238 | g_assert (! v->segment); | ||
239 | |||
240 | v->segment = g_malloc (alloc); | ||
241 | |||
242 | if (! (out = popen (cmdbuf, "r"))) | ||
243 | { | ||
244 | g_warning ("popen failed: %s: %s", cmdbuf, g_strerror (errno)); | ||
245 | return errno; | ||
246 | } | ||
247 | |||
248 | for (;;) | ||
249 | { | ||
250 | nread = fread (readbuf, 1, BUFSIZE, out); | ||
251 | |||
252 | if (nread == 0) | ||
253 | break; | ||
254 | |||
255 | if (nread < 0) | ||
256 | { | ||
257 | g_warning ("fread failed: %s", g_strerror (errno)); | ||
258 | return errno; | ||
259 | } | ||
260 | |||
261 | if (pos + nread > alloc) | ||
262 | { | ||
263 | alloc *= 2; | ||
264 | v->segment = g_realloc (v->segment, alloc); | ||
265 | } | ||
266 | |||
267 | memcpy (v->segment + pos, readbuf, nread); | ||
268 | |||
269 | pos += nread; | ||
270 | } | ||
271 | |||
272 | if (pclose (out) < 0) | ||
273 | { | ||
274 | g_warning ("pclose failed"); | ||
275 | return errno; | ||
276 | } | ||
277 | |||
278 | v->size = pos; | ||
279 | |||
280 | if (walker->write_files) | ||
281 | { | ||
282 | char* file = NULL; | ||
283 | |||
284 | if (! file && tmp_file_1_free) | ||
285 | { | ||
286 | file = tmp_file_1; | ||
287 | tmp_file_1_free = FALSE; | ||
288 | } | ||
289 | |||
290 | if (! file && tmp_file_2_free) | ||
291 | { | ||
292 | file = tmp_file_2; | ||
293 | tmp_file_2_free = FALSE; | ||
294 | } | ||
295 | |||
296 | g_assert (file); | ||
297 | |||
298 | v->filename = file; | ||
299 | |||
300 | if (! (out = fopen (file, "w"))) | ||
301 | { | ||
302 | g_warning ("fopen failed: %s\n", file); | ||
303 | return errno; | ||
304 | } | ||
305 | |||
306 | if (fwrite (v->segment, v->size, 1, out) != 1) | ||
307 | { | ||
308 | g_warning ("fwrite failed: %s\n", file); | ||
309 | return errno; | ||
310 | } | ||
311 | |||
312 | if (fclose (out) < 0) | ||
313 | { | ||
314 | g_warning ("fclose failed: %s\n", file); | ||
315 | return errno; | ||
316 | } | ||
317 | } | ||
318 | |||
319 | return 0; | ||
320 | } | ||
321 | |||
322 | int | ||
323 | rcswalk_delta_date (RcsFile* rcs, RcsWalker* walker, void* data) | ||
324 | { | ||
325 | int i; | ||
326 | int ret; | ||
327 | RcsVersion *vf = NULL; | ||
328 | RcsVersion *vt = NULL; | ||
329 | |||
330 | for (i = 0; i < (rcs->version_count-1); i += 1) | ||
331 | { | ||
332 | vf = rcs->versions_date[i+1]; | ||
333 | vt = rcs->versions_date[i]; | ||
334 | |||
335 | if (! vt->segment && (ret = rcswalk_checkout (rcs, walker, vt))) { | ||
336 | return ret; | ||
337 | } | ||
338 | |||
339 | if ((ret = rcswalk_checkout (rcs, walker, vf))) { | ||
340 | return ret; | ||
341 | } | ||
342 | |||
343 | if ((ret = walker->delta_date (rcs, vf, vt, data))) { | ||
344 | return ret; | ||
345 | } | ||
346 | |||
347 | rcswalk_free_segment (vt); | ||
348 | } | ||
349 | |||
350 | if (vf) rcswalk_free_segment (vf); | ||
351 | if (vt) rcswalk_free_segment (vt); | ||
352 | |||
353 | return 0; | ||
354 | } | ||
355 | |||
356 | int | ||
357 | rcswalk_delta_orig (RcsFile* rcs, RcsWalker* walker, RcsVersion* version, int *count, void* data) | ||
358 | { | ||
359 | int ret; | ||
360 | GSList *c; | ||
361 | RcsVersion *child; | ||
362 | |||
363 | for (c = version->children; c; c = c->next) | ||
364 | { | ||
365 | gboolean reverse; | ||
366 | |||
367 | child = c->data; | ||
368 | |||
369 | if (! version->segment) | ||
370 | { | ||
371 | if ((ret = rcswalk_checkout (rcs, walker, version))) { | ||
372 | return ret; | ||
373 | } | ||
374 | } | ||
375 | |||
376 | if ((ret = rcswalk_checkout (rcs, walker, child))) { | ||
377 | return ret; | ||
378 | } | ||
379 | |||
380 | reverse = version->on_trunk && child->on_trunk; | ||
381 | |||
382 | (* count) += 1; | ||
383 | |||
384 | if ((ret = walker->delta_orig (rcs, reverse ? child : version, reverse ? version : child, data))) { | ||
385 | return ret; | ||
386 | } | ||
387 | |||
388 | rcswalk_free_segment (version); | ||
389 | |||
390 | if ((ret = rcswalk_delta_orig (rcs, walker, child, count, data))) { | ||
391 | return ret; | ||
392 | } | ||
393 | } | ||
394 | |||
395 | rcswalk_free_segment (version); | ||
396 | return 0; | ||
397 | } | ||
398 | |||
399 | int | ||
400 | rcswalk_dateorder (RcsFile* rcs, RcsWalker *walker, RcsStats *stats, void* data) | ||
401 | { | ||
402 | int i, ret; | ||
403 | |||
404 | for (i = 0; i < rcs->version_count; i += 1) | ||
405 | { | ||
406 | RcsVersion *v = rcs->versions_date[i]; | ||
407 | |||
408 | if ((ret = rcswalk_checkout (rcs, walker, v))) { | ||
409 | return ret; | ||
410 | } | ||
411 | |||
412 | stat_bincount_add_item (stats->avg_version_size, i, v->size); | ||
413 | |||
414 | if ((ret = walker->dateorder (rcs, v, data))) { | ||
415 | return ret; | ||
416 | } | ||
417 | |||
418 | rcswalk_free_segment (v); | ||
419 | } | ||
420 | |||
421 | return 0; | ||
422 | } | ||
423 | |||
424 | gboolean | ||
425 | rcswalk_match (char** line_p, char* str) | ||
426 | { | ||
427 | int len = strlen (str); | ||
428 | |||
429 | if (strncmp (*line_p, str, len) == 0) | ||
430 | { | ||
431 | (*line_p) += len; | ||
432 | return TRUE; | ||
433 | } | ||
434 | |||
435 | return FALSE; | ||
436 | } | ||
437 | |||
438 | void | ||
439 | rcswalk_find_parent (RcsFile *rcs, GHashTable* hash, RcsVersion *v) | ||
440 | { | ||
441 | char *lastdot; | ||
442 | char mbuf[1024]; | ||
443 | int lastn; | ||
444 | RcsVersion *p; | ||
445 | |||
446 | strcpy (mbuf, v->vname); | ||
447 | |||
448 | if (! (lastdot = strchr (mbuf, '.'))) | ||
449 | abort (); | ||
450 | |||
451 | if (! (lastdot = strchr (lastdot+1, '.'))) | ||
452 | v->on_trunk = TRUE; | ||
453 | |||
454 | lastdot = strrchr (mbuf, '.'); | ||
455 | lastn = atoi (lastdot + 1); | ||
456 | |||
457 | do | ||
458 | { | ||
459 | if (lastn == 1) | ||
460 | { | ||
461 | (*lastdot) = 0; | ||
462 | |||
463 | if (strcmp (mbuf, "1") == 0) | ||
464 | { | ||
465 | /* Assuming the first version is always "1.1". | ||
466 | */ | ||
467 | rcs->root_version = v; | ||
468 | return; | ||
469 | } | ||
470 | else if (! (lastdot = strrchr (mbuf, '.'))) | ||
471 | { | ||
472 | int i = 1; | ||
473 | int br = atoi (mbuf) - 1; | ||
474 | RcsVersion *p2 = NULL; | ||
475 | |||
476 | /* Now we have something like "2.1" and need to | ||
477 | * search for the highest "1.x" version. | ||
478 | */ | ||
479 | |||
480 | do | ||
481 | { | ||
482 | sprintf (mbuf, "%d.%d", br, i++); | ||
483 | p = p2; | ||
484 | } | ||
485 | while ((p2 = g_hash_table_lookup (hash, mbuf))); | ||
486 | |||
487 | if (p == NULL) | ||
488 | { | ||
489 | rcs->root_version = v; | ||
490 | return; | ||
491 | } | ||
492 | |||
493 | break; | ||
494 | } | ||
495 | else | ||
496 | { | ||
497 | /* 1.2.3.1 => 1.2 */ | ||
498 | (*lastdot) = 0; | ||
499 | lastdot = strrchr (mbuf, '.'); | ||
500 | lastn = atoi (lastdot + 1); | ||
501 | } | ||
502 | } | ||
503 | else | ||
504 | { | ||
505 | lastn -= 1; | ||
506 | sprintf (lastdot, ".%d", lastn); | ||
507 | } | ||
508 | } | ||
509 | while (! (p = g_hash_table_lookup (hash, mbuf))); | ||
510 | |||
511 | g_assert (p); | ||
512 | |||
513 | v->parent = p; | ||
514 | |||
515 | p->children = g_slist_prepend (p->children, v); | ||
516 | } | ||
517 | |||
518 | int | ||
519 | rcswalk_traverse_graph (RcsFile* rcs, RcsVersion* version, RcsVersion *parent) | ||
520 | { | ||
521 | GSList *c; | ||
522 | int distance = -1; | ||
523 | |||
524 | version->cc = g_slist_length (version->children); | ||
525 | |||
526 | if (version->cc > 1) | ||
527 | rcs->branch_count += (version->cc - 1); | ||
528 | |||
529 | if (parent) | ||
530 | { | ||
531 | /* Insure that there is proper date ordering. */ | ||
532 | if (version->date <= parent->date) | ||
533 | version->date = parent->date + 1; | ||
534 | |||
535 | if (parent->on_trunk && version->on_trunk) | ||
536 | rcs->reverse_count += 1; | ||
537 | else | ||
538 | rcs->forward_count += 1; | ||
539 | } | ||
540 | |||
541 | for (c = version->children; c; c = c->next) | ||
542 | { | ||
543 | int c_dist = rcswalk_traverse_graph (rcs, c->data, version); | ||
544 | |||
545 | distance = MAX (distance, c_dist); | ||
546 | } | ||
547 | |||
548 | if (version == rcs->head_version) | ||
549 | distance = 0; | ||
550 | |||
551 | if (distance >= 0) | ||
552 | { | ||
553 | version->chain_length = distance; | ||
554 | |||
555 | return distance + 1; | ||
556 | } | ||
557 | |||
558 | return -1; | ||
559 | } | ||
560 | |||
561 | void | ||
562 | rcswalk_compute_chain_length (RcsFile* rcs, RcsVersion* version, RcsVersion *parent) | ||
563 | { | ||
564 | GSList *c; | ||
565 | |||
566 | if (! parent) | ||
567 | { | ||
568 | g_assert (version->chain_length >= 0); | ||
569 | } | ||
570 | else if (version->chain_length < 0) | ||
571 | { | ||
572 | version->chain_length = parent->chain_length + 1; | ||
573 | } | ||
574 | |||
575 | for (c = version->children; c; c = c->next) | ||
576 | { | ||
577 | rcswalk_compute_chain_length (rcs, c->data, version); | ||
578 | } | ||
579 | } | ||
580 | |||
581 | int | ||
582 | rcswalk_date_compare (const void* a, const void* b) | ||
583 | { | ||
584 | RcsVersion **ra = (void*) a; | ||
585 | RcsVersion **rb = (void*) b; | ||
586 | |||
587 | return (*ra)->date - (*rb)->date; | ||
588 | } | ||
589 | |||
590 | int | ||
591 | rcswalk_build_graph (RcsFile* rcs) | ||
592 | { | ||
593 | GHashTable* hash = g_hash_table_new (g_str_hash, g_str_equal); | ||
594 | int i; | ||
595 | |||
596 | for (i = 0; i < rcs->version_count; i += 1) | ||
597 | g_hash_table_insert (hash, rcs->versions[i].vname, rcs->versions + i); | ||
598 | |||
599 | for (i = 0; i < rcs->version_count; i += 1) | ||
600 | { | ||
601 | RcsVersion *v = rcs->versions + i; | ||
602 | |||
603 | v->chain_length = -1; | ||
604 | v->rcs = rcs; | ||
605 | |||
606 | rcswalk_find_parent (rcs, hash, v); | ||
607 | } | ||
608 | |||
609 | rcs->head_version = g_hash_table_lookup (hash, rcs->headname); | ||
610 | |||
611 | rcswalk_traverse_graph (rcs, rcs->root_version, NULL); | ||
612 | |||
613 | rcswalk_compute_chain_length (rcs, rcs->root_version, NULL); | ||
614 | |||
615 | for (i = 0; i < rcs->version_count; i += 1) | ||
616 | rcs->versions_date[i] = rcs->versions + i; | ||
617 | |||
618 | qsort (rcs->versions_date, rcs->version_count, sizeof (RcsVersion*), & rcswalk_date_compare); | ||
619 | |||
620 | for (i = 0; i < rcs->version_count; i += 1) | ||
621 | { | ||
622 | RcsVersion *v = rcs->versions_date[i]; | ||
623 | |||
624 | v->dateseq = i; | ||
625 | } | ||
626 | |||
627 | g_hash_table_destroy (hash); | ||
628 | |||
629 | return 0; | ||
630 | } | ||
631 | |||
632 | #define HEAD_STATE 0 | ||
633 | #define BAR_STATE 1 | ||
634 | #define REV_STATE 2 | ||
635 | #define DATE_STATE 3 | ||
636 | |||
637 | int | ||
638 | rcswalk_load (RcsFile *rcs, gboolean *skip) | ||
639 | { | ||
640 | FILE* rlog; | ||
641 | char cmdbuf[1024]; | ||
642 | char oneline[1024], *oneline_p; | ||
643 | char rbuf[1024]; | ||
644 | int version_i = 0, ret; | ||
645 | int read_state = HEAD_STATE; | ||
646 | |||
647 | sprintf (cmdbuf, "rlog %s", rcs->filename); | ||
648 | |||
649 | if (! (rlog = popen (cmdbuf, "r"))) | ||
650 | { | ||
651 | g_warning ("popen failed: %s", cmdbuf); | ||
652 | return errno; | ||
653 | } | ||
654 | |||
655 | rcs->headname = NULL; | ||
656 | |||
657 | while (fgets (oneline, 1024, rlog)) | ||
658 | { | ||
659 | oneline_p = oneline; | ||
660 | |||
661 | if (read_state == HEAD_STATE && rcswalk_match (& oneline_p, "total revisions: ")) | ||
662 | { | ||
663 | if (sscanf (oneline_p, "%d", & rcs->version_count) != 1) | ||
664 | goto badscan; | ||
665 | |||
666 | rcs->versions = g_new0 (RcsVersion, rcs->version_count); | ||
667 | rcs->versions_date = g_new (RcsVersion*, rcs->version_count); | ||
668 | read_state = BAR_STATE; | ||
669 | } | ||
670 | else if (read_state == HEAD_STATE && rcswalk_match (& oneline_p, "head: ")) | ||
671 | { | ||
672 | if (sscanf (oneline_p, "%s", rbuf) != 1) | ||
673 | goto badscan; | ||
674 | |||
675 | rcs->headname = g_strdup (rbuf); | ||
676 | read_state = HEAD_STATE; /* no change */ | ||
677 | } | ||
678 | else if (read_state == BAR_STATE && rcswalk_match (& oneline_p, "----------------------------")) | ||
679 | { | ||
680 | read_state = REV_STATE; | ||
681 | } | ||
682 | else if (read_state == REV_STATE && rcswalk_match (& oneline_p, "revision ")) | ||
683 | { | ||
684 | if (version_i >= rcs->version_count) | ||
685 | { | ||
686 | /* jkh likes to insert the rlog of one RCS file into the log | ||
687 | * message of another, and this can confuse things. Why, oh why, | ||
688 | * doesn't rlog have an option to not print the log? | ||
689 | */ | ||
690 | fprintf (stderr, "rcswalk: too many versions: skipping file %s\n", rcs->filename); | ||
691 | *skip = TRUE; | ||
692 | skip_count += 1; | ||
693 | pclose (rlog); | ||
694 | return 0; | ||
695 | } | ||
696 | |||
697 | if (sscanf (oneline_p, "%s", rbuf) != 1) | ||
698 | goto badscan; | ||
699 | |||
700 | rcs->versions[version_i].vname = g_strdup (rbuf); | ||
701 | read_state = DATE_STATE; | ||
702 | |||
703 | g_assert (rcs->versions[version_i].vname); | ||
704 | } | ||
705 | else if (read_state == DATE_STATE && rcswalk_match (& oneline_p, "date: ")) | ||
706 | { | ||
707 | char* semi = strchr (oneline_p, ';'); | ||
708 | |||
709 | if (! semi) | ||
710 | goto badscan; | ||
711 | |||
712 | strncpy (rbuf, oneline_p, semi - oneline_p); | ||
713 | |||
714 | rbuf[semi - oneline_p] = 0; | ||
715 | |||
716 | rcs->versions[version_i].date = str2time (rbuf, 0, 0); | ||
717 | |||
718 | version_i += 1; | ||
719 | read_state = BAR_STATE; | ||
720 | } | ||
721 | } | ||
722 | |||
723 | if (! rcs->headname) | ||
724 | { | ||
725 | fprintf (stderr, "rcswalk: no head version: skipping file %s\n", rcs->filename); | ||
726 | *skip = TRUE; | ||
727 | skip_count += 1; | ||
728 | pclose (rlog); | ||
729 | return 0; | ||
730 | } | ||
731 | |||
732 | if (pclose (rlog) < 0) | ||
733 | { | ||
734 | g_warning ("pclose failed: %s", cmdbuf); | ||
735 | return errno; | ||
736 | } | ||
737 | |||
738 | if ((ret = rcswalk_build_graph (rcs))) { | ||
739 | return ret; | ||
740 | } | ||
741 | |||
742 | return 0; | ||
743 | |||
744 | badscan: | ||
745 | |||
746 | pclose (rlog); | ||
747 | |||
748 | g_warning ("rlog syntax error"); | ||
749 | return -1; | ||
750 | } | ||
751 | |||
752 | void | ||
753 | rcswalk_free (RcsFile* rcs) | ||
754 | { | ||
755 | int i; | ||
756 | |||
757 | for (i = 0; i < rcs->version_count; i += 1) | ||
758 | { | ||
759 | g_free (rcs->versions[i].vname); | ||
760 | g_slist_free (rcs->versions[i].children); | ||
761 | } | ||
762 | |||
763 | g_free (rcs->filename); | ||
764 | g_free (rcs->headname); | ||
765 | g_free (rcs->versions); | ||
766 | g_free (rcs->versions_date); | ||
767 | g_free (rcs); | ||
768 | } | ||
769 | |||
770 | int | ||
771 | rcswalk_one (char* rcsfile, char* copyfile, RcsWalker* walker, RcsStats* stats, void* data) | ||
772 | { | ||
773 | RcsFile* rcs; | ||
774 | int i, ret; | ||
775 | long long maxsize = 0; | ||
776 | gboolean skip = FALSE; | ||
777 | |||
778 | rcs = g_new0 (RcsFile, 1); | ||
779 | |||
780 | rcs->filename = g_strdup (rcsfile); | ||
781 | rcs->copyname = copyfile; | ||
782 | |||
783 | if ((ret = rcswalk_load (rcs, & skip))) { | ||
784 | return ret; | ||
785 | } | ||
786 | |||
787 | if (walker->min_versions > rcs->version_count) | ||
788 | { | ||
789 | small_count += 1; | ||
790 | skip = TRUE; | ||
791 | } | ||
792 | |||
793 | if (walker->max_versions < rcs->version_count) | ||
794 | { | ||
795 | large_count += 1; | ||
796 | skip = TRUE; | ||
797 | } | ||
798 | |||
799 | if (! skip) | ||
800 | { | ||
801 | process_count += 1; | ||
802 | |||
803 | if (walker->dateorder && (ret = rcswalk_dateorder (rcs, walker, stats, data))) { | ||
804 | return ret; | ||
805 | } | ||
806 | |||
807 | if (walker->delta_orig) | ||
808 | { | ||
809 | int count = 0; | ||
810 | |||
811 | if ((ret = rcswalk_delta_orig (rcs, walker, rcs->root_version, & count, data))) { | ||
812 | return ret; | ||
813 | } | ||
814 | |||
815 | g_assert (count == (rcs->version_count - 1)); | ||
816 | } | ||
817 | |||
818 | if (walker->delta_date && (ret = rcswalk_delta_date (rcs, walker, data))) { | ||
819 | return ret; | ||
820 | } | ||
821 | |||
822 | for (i = 0; i < rcs->version_count; i += 1) | ||
823 | { | ||
824 | rcs->total_size += rcs->versions[i].size; | ||
825 | maxsize = MAX (rcs->versions[i].size, maxsize); | ||
826 | } | ||
827 | |||
828 | stat_int_add_item (stats->version_stat, rcs->version_count); | ||
829 | stat_int_add_item (stats->forward_stat, rcs->forward_count); | ||
830 | stat_int_add_item (stats->reverse_stat, rcs->reverse_count); | ||
831 | stat_int_add_item (stats->branch_stat, rcs->branch_count); | ||
832 | stat_int_add_item (stats->unencoded_stat, rcs->total_size); | ||
833 | stat_int_add_item (stats->literal_stat, maxsize); | ||
834 | |||
835 | if (walker->onefile && (ret = walker->onefile (rcs, stats, data))) { | ||
836 | return ret; | ||
837 | } | ||
838 | } | ||
839 | |||
840 | rcswalk_free (rcs); | ||
841 | |||
842 | return 0; | ||
843 | } | ||
844 | |||
845 | int | ||
846 | rcswalk_dir (const char* dir, RcsWalker* walker, RcsStats* stats, void* data, const char* copy_dir) | ||
847 | { | ||
848 | int ret; | ||
849 | DIR* thisdir; | ||
850 | struct dirent* ent; | ||
851 | |||
852 | if (copy_dir && (ret = config_create_dir (copy_dir))) { | ||
853 | return ret; | ||
854 | } | ||
855 | |||
856 | if (! (thisdir = opendir (dir))) | ||
857 | { | ||
858 | g_warning ("opendir failed: %s", dir); | ||
859 | return errno; | ||
860 | } | ||
861 | |||
862 | while ((ent = readdir (thisdir))) | ||
863 | { | ||
864 | char* name = ent->d_name; | ||
865 | int len; | ||
866 | struct stat buf; | ||
867 | char* fullname; | ||
868 | char* copyname = NULL; | ||
869 | |||
870 | if (strcmp (name, ".") == 0) | ||
871 | continue; | ||
872 | |||
873 | if (strcmp (name, "..") == 0) | ||
874 | continue; | ||
875 | |||
876 | len = strlen (name); | ||
877 | |||
878 | fullname = g_strdup_printf ("%s/%s", dir, name); | ||
879 | |||
880 | if (copy_dir) | ||
881 | copyname = g_strdup_printf ("%s/%s", copy_dir, name); | ||
882 | |||
883 | if (len > 2 && strcmp (name + len - 2, ",v") == 0) | ||
884 | { | ||
885 | if ((ret = rcswalk_one (fullname, copyname, walker, stats, data))) { | ||
886 | goto abort; | ||
887 | } | ||
888 | } | ||
889 | else | ||
890 | { | ||
891 | if (stat (fullname, & buf) < 0) | ||
892 | { | ||
893 | g_warning ("stat failed: %s\n", fullname); | ||
894 | goto abort; | ||
895 | } | ||
896 | |||
897 | if (S_ISDIR (buf.st_mode)) | ||
898 | { | ||
899 | if ((ret = rcswalk_dir (fullname, walker, stats, data, copyname))) { | ||
900 | goto abort; | ||
901 | } | ||
902 | } | ||
903 | } | ||
904 | |||
905 | g_free (fullname); | ||
906 | |||
907 | if (copyname) | ||
908 | g_free (copyname); | ||
909 | } | ||
910 | |||
911 | if (closedir (thisdir) < 0) | ||
912 | { | ||
913 | g_warning ("closedir failed: %s", dir); | ||
914 | return errno; | ||
915 | } | ||
916 | |||
917 | return 0; | ||
918 | |||
919 | abort: | ||
920 | |||
921 | if (thisdir) | ||
922 | closedir (thisdir); | ||
923 | |||
924 | return -1; | ||
925 | } | ||
926 | |||
927 | void | ||
928 | rcswalk_init (void) | ||
929 | { | ||
930 | config_register (rcswalk_options, ARRAY_SIZE (rcswalk_options)); | ||
931 | } | ||
932 | |||
933 | int | ||
934 | rcswalk (RcsWalker *walker, const char* copy_base) | ||
935 | { | ||
936 | void* data = NULL; | ||
937 | RcsStats stats; | ||
938 | int ret; | ||
939 | |||
940 | skip_count = 0; | ||
941 | small_count = 0; | ||
942 | process_count = 0; | ||
943 | large_count = 0; | ||
944 | |||
945 | memset (& stats, 0, sizeof (stats)); | ||
946 | |||
947 | stats.avg_version_size = stat_bincount_new ("AvgVersionSize"); /* @@@ leak */ | ||
948 | stats.version_stat = stat_int_new ("Version"); /* @@@ leak */ | ||
949 | stats.forward_stat = stat_int_new ("Forward"); /* @@@ leak */ | ||
950 | stats.reverse_stat = stat_int_new ("Reverse"); /* @@@ leak */ | ||
951 | stats.branch_stat = stat_int_new ("Branch"); /* @@@ leak */ | ||
952 | stats.unencoded_stat = stat_int_new ("Unencoded"); /* @@@ leak */ | ||
953 | stats.literal_stat = stat_int_new ("Literal"); /* @@@ leak */ | ||
954 | |||
955 | tmp_file_1 = g_strdup_printf ("%s/rcs1.%d", g_get_tmp_dir (), (int) getpid ()); | ||
956 | tmp_file_2 = g_strdup_printf ("%s/rcs2.%d", g_get_tmp_dir (), (int) getpid ()); | ||
957 | |||
958 | if (walker->initialize) | ||
959 | data = walker->initialize (); | ||
960 | |||
961 | if ((ret = rcswalk_dir (rcswalk_input_dir, walker, & stats, data, copy_base))) { | ||
962 | return ret; | ||
963 | } | ||
964 | |||
965 | if (walker->finalize) | ||
966 | { | ||
967 | if ((ret = walker->finalize (& stats, data))) { | ||
968 | return ret; | ||
969 | } | ||
970 | } | ||
971 | |||
972 | unlink (tmp_file_1); | ||
973 | unlink (tmp_file_2); | ||
974 | |||
975 | fprintf (stderr, "rcswalk: processed %d files: too small %d; too large: %d; damaged: %d\n", process_count, small_count, large_count, skip_count); | ||
976 | |||
977 | return 0; | ||
978 | } | ||
979 | |||
980 | /* Statistics | ||
981 | */ | ||
982 | |||
983 | void | ||
984 | rcswalk_report (RcsStats* set) | ||
985 | { | ||
986 | stat_bincount_report (set->avg_version_size); | ||
987 | stat_int_report (set->version_stat); | ||
988 | stat_int_report (set->forward_stat); | ||
989 | stat_int_report (set->reverse_stat); | ||
990 | stat_int_report (set->branch_stat); | ||
991 | stat_int_report (set->unencoded_stat); | ||
992 | stat_int_report (set->literal_stat); | ||
993 | } | ||
994 | |||
995 | /* Int stat | ||
996 | */ | ||
997 | IntStat* | ||
998 | stat_int_new (const char* name) | ||
999 | { | ||
1000 | IntStat* s = g_new0 (IntStat, 1); | ||
1001 | |||
1002 | s->name = name; | ||
1003 | s->values = g_array_new (FALSE, FALSE, sizeof (long long)); | ||
1004 | |||
1005 | return s; | ||
1006 | } | ||
1007 | |||
1008 | void | ||
1009 | stat_int_add_item (IntStat* stat, long long v) | ||
1010 | { | ||
1011 | if (! stat->count) | ||
1012 | stat->min = v; | ||
1013 | stat->count += 1; | ||
1014 | stat->min = MIN (v, stat->min); | ||
1015 | stat->max = MAX (v, stat->max); | ||
1016 | stat->sum += v; | ||
1017 | |||
1018 | g_array_append_val (stat->values, v); | ||
1019 | } | ||
1020 | |||
1021 | double | ||
1022 | stat_int_stddev (IntStat *stat) | ||
1023 | { | ||
1024 | double f = 0; | ||
1025 | double m = (double) stat->sum / (double) stat->count; | ||
1026 | double v; | ||
1027 | int i; | ||
1028 | |||
1029 | for (i = 0; i < stat->count; i += 1) | ||
1030 | { | ||
1031 | long long x = g_array_index (stat->values, long long, i); | ||
1032 | |||
1033 | f += (m - (double) x) * (m - (double) x); | ||
1034 | } | ||
1035 | |||
1036 | v = f / (double) stat->count; | ||
1037 | |||
1038 | return sqrt (v); | ||
1039 | } | ||
1040 | |||
1041 | int | ||
1042 | ll_comp (const void* a, const void* b) | ||
1043 | { | ||
1044 | const long long* lla = a; | ||
1045 | const long long* llb = b; | ||
1046 | return (*lla) - (*llb); | ||
1047 | } | ||
1048 | |||
1049 | void | ||
1050 | stat_int_histogram (IntStat *stat) | ||
1051 | { | ||
1052 | int i, consec; | ||
1053 | long long cum = 0; | ||
1054 | |||
1055 | FILE* p_out; | ||
1056 | FILE* s_out; | ||
1057 | |||
1058 | if (! (p_out = config_output ("%s.pop.hist", stat->name))) | ||
1059 | abort (); | ||
1060 | |||
1061 | if (! (s_out = config_output ("%s.sum.hist", stat->name))) | ||
1062 | abort (); | ||
1063 | |||
1064 | qsort (stat->values->data, stat->count, sizeof (long long), ll_comp); | ||
1065 | |||
1066 | for (i = 0; i < stat->count; i += consec) | ||
1067 | { | ||
1068 | long long ix = g_array_index (stat->values, long long, i); | ||
1069 | |||
1070 | for (consec = 1; (i+consec) < stat->count; consec += 1) | ||
1071 | { | ||
1072 | long long jx = g_array_index (stat->values, long long, i+consec); | ||
1073 | |||
1074 | if (ix != jx) | ||
1075 | break; | ||
1076 | } | ||
1077 | |||
1078 | cum += consec * g_array_index (stat->values, long long, i); | ||
1079 | |||
1080 | fprintf (p_out, "%qd, %0.3f\n", g_array_index (stat->values, long long, i), (double) (i+consec) / (double) stat->count); | ||
1081 | fprintf (s_out, "%qd, %0.3f\n", g_array_index (stat->values, long long, i), (double) cum / (double) stat->sum); | ||
1082 | } | ||
1083 | |||
1084 | if (fclose (p_out) < 0 || fclose (s_out) < 0) | ||
1085 | { | ||
1086 | g_error ("fclose failed\n"); | ||
1087 | } | ||
1088 | } | ||
1089 | |||
1090 | void | ||
1091 | stat_int_report (IntStat* stat) | ||
1092 | { | ||
1093 | FILE* out; | ||
1094 | |||
1095 | if (! (out = config_output ("%s.stat", stat->name))) | ||
1096 | abort (); | ||
1097 | |||
1098 | fprintf (out, "Name: %s\n", stat->name); | ||
1099 | fprintf (out, "Count: %d\n", stat->count); | ||
1100 | fprintf (out, "Min: %qd\n", stat->min); | ||
1101 | fprintf (out, "Max: %qd\n", stat->max); | ||
1102 | fprintf (out, "Sum: %qd\n", stat->sum); | ||
1103 | fprintf (out, "Mean: %0.2f\n", (double) stat->sum / (double) stat->count); | ||
1104 | fprintf (out, "Stddev: %0.2f\n", stat_int_stddev (stat)); | ||
1105 | |||
1106 | if (fclose (out) < 0) | ||
1107 | g_error ("fclose failed"); | ||
1108 | |||
1109 | stat_int_histogram (stat); | ||
1110 | } | ||
1111 | |||
1112 | /* Dbl stat | ||
1113 | */ | ||
1114 | |||
1115 | DblStat* | ||
1116 | stat_dbl_new (const char* name) | ||
1117 | { | ||
1118 | DblStat* s = g_new0 (DblStat, 1); | ||
1119 | |||
1120 | s->name = name; | ||
1121 | s->values = g_array_new (FALSE, FALSE, sizeof (double)); | ||
1122 | |||
1123 | return s; | ||
1124 | } | ||
1125 | |||
1126 | void | ||
1127 | stat_dbl_add_item (DblStat* stat, double v) | ||
1128 | { | ||
1129 | if (! stat->count) | ||
1130 | stat->min = v; | ||
1131 | stat->count += 1; | ||
1132 | stat->min = MIN (v, stat->min); | ||
1133 | stat->max = MAX (v, stat->max); | ||
1134 | stat->sum += v; | ||
1135 | |||
1136 | g_array_append_val (stat->values, v); | ||
1137 | } | ||
1138 | |||
1139 | double | ||
1140 | stat_dbl_stddev (DblStat *stat) | ||
1141 | { | ||
1142 | double f = 0; | ||
1143 | double m = stat->sum / stat->count; | ||
1144 | double v; | ||
1145 | int i; | ||
1146 | |||
1147 | for (i = 0; i < stat->count; i += 1) | ||
1148 | { | ||
1149 | double x = g_array_index (stat->values, double, i); | ||
1150 | |||
1151 | f += (m - x) * (m - x); | ||
1152 | } | ||
1153 | |||
1154 | v = f / stat->count; | ||
1155 | |||
1156 | return sqrt (v); | ||
1157 | } | ||
1158 | |||
1159 | int | ||
1160 | dbl_comp (const void* a, const void* b) | ||
1161 | { | ||
1162 | const double* da = a; | ||
1163 | const double* db = b; | ||
1164 | double diff = (*da) - (*db); | ||
1165 | |||
1166 | if (diff > 0.0) | ||
1167 | return 1; | ||
1168 | else if (diff < 0.0) | ||
1169 | return -1; | ||
1170 | else | ||
1171 | return 0; | ||
1172 | } | ||
1173 | |||
1174 | void | ||
1175 | stat_dbl_histogram (DblStat *stat) | ||
1176 | { | ||
1177 | int i, consec; | ||
1178 | double cum = 0.0; | ||
1179 | |||
1180 | FILE* p_out; | ||
1181 | FILE* s_out; | ||
1182 | |||
1183 | if (! (p_out = config_output ("%s.pop.hist", stat->name))) | ||
1184 | abort (); | ||
1185 | |||
1186 | if (! (s_out = config_output ("%s.sum.hist", stat->name))) | ||
1187 | abort (); | ||
1188 | |||
1189 | qsort (stat->values->data, stat->count, sizeof (double), dbl_comp); | ||
1190 | |||
1191 | for (i = 0; i < stat->count; i += consec) | ||
1192 | { | ||
1193 | double ix = g_array_index (stat->values, double, i); | ||
1194 | |||
1195 | for (consec = 1; (i+consec) < stat->count; consec += 1) | ||
1196 | { | ||
1197 | double jx = g_array_index (stat->values, double, i+consec); | ||
1198 | |||
1199 | if (ix != jx) | ||
1200 | break; | ||
1201 | } | ||
1202 | |||
1203 | cum += ((double) consec) * g_array_index (stat->values, double, i); | ||
1204 | |||
1205 | fprintf (p_out, "%0.6f, %0.3f\n", g_array_index (stat->values, double, i), (double) (i+consec) / (double) stat->count); | ||
1206 | fprintf (s_out, "%0.6f, %0.3f\n", g_array_index (stat->values, double, i), cum / stat->sum); | ||
1207 | } | ||
1208 | |||
1209 | if (fclose (p_out) < 0 || fclose (s_out) < 0) | ||
1210 | { | ||
1211 | g_error ("fclose failed\n"); | ||
1212 | } | ||
1213 | } | ||
1214 | |||
1215 | void | ||
1216 | stat_dbl_report (DblStat* stat) | ||
1217 | { | ||
1218 | FILE* out; | ||
1219 | |||
1220 | if (! (out = config_output ("%s.stat", stat->name))) | ||
1221 | abort (); | ||
1222 | |||
1223 | fprintf (out, "Name: %s\n", stat->name); | ||
1224 | fprintf (out, "Count: %d\n", stat->count); | ||
1225 | fprintf (out, "Min: %0.6f\n", stat->min); | ||
1226 | fprintf (out, "Max: %0.6f\n", stat->max); | ||
1227 | fprintf (out, "Sum: %0.6f\n", stat->sum); | ||
1228 | fprintf (out, "Mean: %0.6f\n", stat->sum / stat->count); | ||
1229 | fprintf (out, "Stddev: %0.6f\n", stat_dbl_stddev (stat)); | ||
1230 | |||
1231 | if (fclose (out) < 0) | ||
1232 | g_error ("fclose failed"); | ||
1233 | |||
1234 | stat_dbl_histogram (stat); | ||
1235 | } | ||
1236 | |||
1237 | /* Bincount | ||
1238 | */ | ||
1239 | BinCounter* | ||
1240 | stat_bincount_new (const char* name) | ||
1241 | { | ||
1242 | BinCounter* bc = g_new0 (BinCounter, 1); | ||
1243 | |||
1244 | bc->name = name; | ||
1245 | bc->bins = g_ptr_array_new (); | ||
1246 | |||
1247 | return bc; | ||
1248 | } | ||
1249 | |||
1250 | void | ||
1251 | stat_bincount_add_item (BinCounter* bc, int bin, double val) | ||
1252 | { | ||
1253 | GArray* one; | ||
1254 | int last; | ||
1255 | |||
1256 | if (bin >= bc->bins->len) | ||
1257 | { | ||
1258 | g_ptr_array_set_size (bc->bins, bin+1); | ||
1259 | } | ||
1260 | |||
1261 | if (! (one = bc->bins->pdata[bin])) | ||
1262 | { | ||
1263 | one = bc->bins->pdata[bin] = g_array_new (FALSE, TRUE, sizeof (double)); | ||
1264 | } | ||
1265 | |||
1266 | g_assert (one); | ||
1267 | |||
1268 | last = one->len; | ||
1269 | |||
1270 | g_array_set_size (one, last + 1); | ||
1271 | |||
1272 | g_array_index (one, double, last) = val; | ||
1273 | } | ||
1274 | |||
1275 | void | ||
1276 | stat_bincount_report (BinCounter* bc) | ||
1277 | { | ||
1278 | FILE *avg_out; | ||
1279 | FILE *raw_out; | ||
1280 | int i; | ||
1281 | |||
1282 | if (! (avg_out = config_output ("%s.avg", bc->name))) | ||
1283 | abort (); | ||
1284 | |||
1285 | if (! (raw_out = config_output ("%s.raw", bc->name))) | ||
1286 | abort (); | ||
1287 | |||
1288 | for (i = 0; i < bc->bins->len; i += 1) | ||
1289 | { | ||
1290 | GArray* one = bc->bins->pdata[i]; | ||
1291 | |||
1292 | double sum = 0.0; | ||
1293 | int j; | ||
1294 | |||
1295 | for (j = 0; j < one->len; j += 1) | ||
1296 | { | ||
1297 | double d = g_array_index (one, double, j); | ||
1298 | |||
1299 | sum += d; | ||
1300 | |||
1301 | fprintf (raw_out, "%e ", d); | ||
1302 | } | ||
1303 | |||
1304 | fprintf (raw_out, "\n"); | ||
1305 | fprintf (avg_out, "%e %d\n", sum / one->len, one->len); | ||
1306 | } | ||
1307 | |||
1308 | if (fclose (avg_out) < 0) | ||
1309 | g_error ("fclose failed"); | ||
1310 | |||
1311 | if (fclose (raw_out) < 0) | ||
1312 | g_error ("fclose failed"); | ||
1313 | } | ||
1314 | |||
1315 | /* Config stuff | ||
1316 | */ | ||
1317 | |||
1318 | int | ||
1319 | config_create_dir (const char* dirname) | ||
1320 | { | ||
1321 | struct stat buf; | ||
1322 | |||
1323 | if (stat (dirname, & buf) < 0) | ||
1324 | { | ||
1325 | if (mkdir (dirname, 0777) < 0) | ||
1326 | { | ||
1327 | fprintf (stderr, "mkdir failed: %s\n", dirname); | ||
1328 | return errno; | ||
1329 | } | ||
1330 | } | ||
1331 | else | ||
1332 | { | ||
1333 | if (! S_ISDIR (buf.st_mode)) | ||
1334 | { | ||
1335 | fprintf (stderr, "not a directory: %s\n", dirname); | ||
1336 | return errno; | ||
1337 | } | ||
1338 | } | ||
1339 | |||
1340 | return 0; | ||
1341 | } | ||
1342 | |||
1343 | int | ||
1344 | config_clear_dir (const char* dir) | ||
1345 | { | ||
1346 | char buf[1024]; | ||
1347 | |||
1348 | if (dir) | ||
1349 | { | ||
1350 | sprintf (buf, "rm -rf %s", dir); | ||
1351 | |||
1352 | system (buf); | ||
1353 | } | ||
1354 | |||
1355 | return 0; | ||
1356 | } | ||
1357 | |||
1358 | static ConfigOption all_options[64]; | ||
1359 | static int option_count; | ||
1360 | |||
1361 | void | ||
1362 | config_init () | ||
1363 | { | ||
1364 | static gboolean once = FALSE; | ||
1365 | if (! once) | ||
1366 | { | ||
1367 | once = TRUE; | ||
1368 | config_register (config_options, ARRAY_SIZE (config_options)); | ||
1369 | } | ||
1370 | } | ||
1371 | |||
1372 | void | ||
1373 | config_register (ConfigOption *opts, int nopts) | ||
1374 | { | ||
1375 | int i; | ||
1376 | |||
1377 | config_init (); | ||
1378 | |||
1379 | for (i = 0; i < nopts; i += 1) | ||
1380 | { | ||
1381 | all_options[option_count++] = opts[i]; | ||
1382 | } | ||
1383 | } | ||
1384 | |||
1385 | void | ||
1386 | config_set_string (const char* var, const char* val) | ||
1387 | { | ||
1388 | int i; | ||
1389 | |||
1390 | for (i = 0; i < option_count; i += 1) | ||
1391 | { | ||
1392 | ConfigOption *opt = all_options + i; | ||
1393 | |||
1394 | if (strcmp (opt->name, var) == 0) | ||
1395 | { | ||
1396 | (* (const char**) opt->value) = val; | ||
1397 | opt->found = TRUE; | ||
1398 | return; | ||
1399 | } | ||
1400 | } | ||
1401 | } | ||
1402 | |||
1403 | int | ||
1404 | config_parse (const char* config_file) | ||
1405 | { | ||
1406 | FILE *in; | ||
1407 | char oname[1024], value[1024]; | ||
1408 | int i; | ||
1409 | |||
1410 | if (! (in = fopen (config_file, "r"))) | ||
1411 | { | ||
1412 | fprintf (stderr, "fopen failed: %s\n", config_file); | ||
1413 | return errno; | ||
1414 | } | ||
1415 | |||
1416 | for (;;) | ||
1417 | { | ||
1418 | ConfigOption *opt = NULL; | ||
1419 | |||
1420 | if (fscanf (in, "%s", oname) != 1) | ||
1421 | break; | ||
1422 | |||
1423 | for (i = 0; i < option_count; i += 1) | ||
1424 | { | ||
1425 | if (strcmp (oname, all_options[i].name) == 0) | ||
1426 | { | ||
1427 | opt = all_options + i; | ||
1428 | break; | ||
1429 | } | ||
1430 | } | ||
1431 | |||
1432 | if (opt && opt->arg == CO_None) | ||
1433 | { | ||
1434 | (* (gboolean*) opt->value) = TRUE; | ||
1435 | opt->found = TRUE; | ||
1436 | continue; | ||
1437 | } | ||
1438 | |||
1439 | if (fscanf (in, "%s", value) != 1) | ||
1440 | { | ||
1441 | fprintf (stderr, "no value for option: %s; file: %s\n", oname, config_file); | ||
1442 | goto abort; | ||
1443 | } | ||
1444 | |||
1445 | if (! opt) | ||
1446 | { | ||
1447 | /*fprintf (stderr, "unrecognized option: %s\n", oname);*/ | ||
1448 | continue; | ||
1449 | } | ||
1450 | |||
1451 | switch (opt->type) | ||
1452 | { | ||
1453 | case CD_Bool: | ||
1454 | |||
1455 | if (strcasecmp (value, "yes") == 0 || | ||
1456 | strcasecmp (value, "true") == 0 || | ||
1457 | strcmp (value, "1") == 0 || | ||
1458 | strcasecmp (value, "on") == 0) | ||
1459 | { | ||
1460 | ((gboolean*) opt->value) = TRUE; | ||
1461 | } | ||
1462 | else | ||
1463 | { | ||
1464 | ((gboolean*) opt->value) = FALSE; | ||
1465 | } | ||
1466 | |||
1467 | break; | ||
1468 | case CD_Int32: | ||
1469 | |||
1470 | if (sscanf (value, "%d", (gint32*) opt->value) != 1) | ||
1471 | { | ||
1472 | fprintf (stderr, "parse error for option: %s; file: %s\n", oname, config_file); | ||
1473 | goto abort; | ||
1474 | } | ||
1475 | |||
1476 | break; | ||
1477 | case CD_Double: | ||
1478 | |||
1479 | if (sscanf (value, "%lf", (double*) opt->value) != 1) | ||
1480 | { | ||
1481 | fprintf (stderr, "parse error for option: %s; file: %s\n", oname, config_file); | ||
1482 | goto abort; | ||
1483 | } | ||
1484 | |||
1485 | break; | ||
1486 | case CD_String: | ||
1487 | |||
1488 | (* (const char**) opt->value) = g_strdup (value); | ||
1489 | |||
1490 | break; | ||
1491 | } | ||
1492 | |||
1493 | opt->found = TRUE; | ||
1494 | } | ||
1495 | |||
1496 | fclose (in); | ||
1497 | |||
1498 | return 0; | ||
1499 | |||
1500 | abort: | ||
1501 | |||
1502 | fclose (in); | ||
1503 | |||
1504 | return -1; | ||
1505 | } | ||
1506 | |||
1507 | int | ||
1508 | config_compute_output_dir () | ||
1509 | { | ||
1510 | char tmp[1024]; | ||
1511 | char buf[1024]; | ||
1512 | int i; | ||
1513 | gboolean last = FALSE; | ||
1514 | |||
1515 | buf[0] = 0; | ||
1516 | |||
1517 | for (i = 0; i < option_count; i += 1) | ||
1518 | { | ||
1519 | ConfigOption *opt = all_options + i; | ||
1520 | |||
1521 | if (opt->style == CS_Ignore) | ||
1522 | continue; | ||
1523 | |||
1524 | if (! opt->found) | ||
1525 | continue; | ||
1526 | |||
1527 | if (last) | ||
1528 | strcat (buf, ","); | ||
1529 | |||
1530 | last = TRUE; | ||
1531 | |||
1532 | strcat (buf, opt->abbrev); | ||
1533 | strcat (buf, "="); | ||
1534 | |||
1535 | switch (opt->type) | ||
1536 | { | ||
1537 | case CD_Bool: | ||
1538 | |||
1539 | if (* (gboolean*) opt->value) | ||
1540 | strcat (buf, "true"); | ||
1541 | else | ||
1542 | strcat (buf, "false"); | ||
1543 | |||
1544 | break; | ||
1545 | case CD_Int32: | ||
1546 | |||
1547 | sprintf (tmp, "%d", (* (gint32*) opt->value)); | ||
1548 | strcat (buf, tmp); | ||
1549 | |||
1550 | break; | ||
1551 | case CD_Double: | ||
1552 | |||
1553 | sprintf (tmp, "%0.2f", (* (double*) opt->value)); | ||
1554 | strcat (buf, tmp); | ||
1555 | |||
1556 | break; | ||
1557 | case CD_String: | ||
1558 | |||
1559 | if (opt->style == CS_UseAsFile) | ||
1560 | { | ||
1561 | const char* str = (* (const char**) opt->value); | ||
1562 | const char* ls = strrchr (str, '/'); | ||
1563 | |||
1564 | strcat (buf, ls ? (ls + 1) : str); | ||
1565 | } | ||
1566 | else | ||
1567 | { | ||
1568 | strcat (buf, (* (const char**) opt->value)); | ||
1569 | } | ||
1570 | |||
1571 | break; | ||
1572 | } | ||
1573 | } | ||
1574 | |||
1575 | config_output_dir = g_strdup_printf ("%s/%s", config_output_base, buf); | ||
1576 | |||
1577 | return 0; | ||
1578 | } | ||
1579 | |||
1580 | int | ||
1581 | config_done (void) | ||
1582 | { | ||
1583 | int i, ret; | ||
1584 | FILE *out; | ||
1585 | |||
1586 | for (i = 0; i < option_count; i += 1) | ||
1587 | { | ||
1588 | ConfigOption *opt = all_options + i; | ||
1589 | |||
1590 | if (! opt->found && opt->arg == CO_Required) | ||
1591 | { | ||
1592 | fprintf (stderr, "required option not found: %s\n", all_options[i].name); | ||
1593 | return -1; | ||
1594 | } | ||
1595 | } | ||
1596 | |||
1597 | if ((ret = config_compute_output_dir ())) { | ||
1598 | return ret; | ||
1599 | } | ||
1600 | |||
1601 | if ((ret = config_clear_dir (config_output_dir))) { | ||
1602 | return ret; | ||
1603 | } | ||
1604 | |||
1605 | if ((ret = config_create_dir (config_output_dir))) { | ||
1606 | return ret; | ||
1607 | } | ||
1608 | |||
1609 | if (! (out = config_output ("Options"))) | ||
1610 | abort (); | ||
1611 | |||
1612 | for (i = 0; i < option_count; i += 1) | ||
1613 | { | ||
1614 | ConfigOption *opt = all_options + i; | ||
1615 | |||
1616 | fprintf (out, "option: %s; value: ", all_options[i].name); | ||
1617 | |||
1618 | switch (opt->type) | ||
1619 | { | ||
1620 | case CD_Bool: | ||
1621 | |||
1622 | fprintf (out, "%s", (* (gboolean*) opt->value) ? "TRUE" : "FALSE"); | ||
1623 | |||
1624 | break; | ||
1625 | case CD_Int32: | ||
1626 | |||
1627 | fprintf (out, "%d", (* (gint32*) opt->value)); | ||
1628 | |||
1629 | break; | ||
1630 | case CD_Double: | ||
1631 | |||
1632 | fprintf (out, "%0.2f", (* (double*) opt->value)); | ||
1633 | |||
1634 | break; | ||
1635 | case CD_String: | ||
1636 | |||
1637 | fprintf (out, "%s", (* (const char**) opt->value)); | ||
1638 | |||
1639 | break; | ||
1640 | } | ||
1641 | |||
1642 | fprintf (out, "\n"); | ||
1643 | } | ||
1644 | |||
1645 | if (fclose (out)) | ||
1646 | { | ||
1647 | fprintf (stderr, "fclose failed\n"); | ||
1648 | return errno; | ||
1649 | } | ||
1650 | |||
1651 | return 0; | ||
1652 | } | ||
1653 | |||
1654 | const char* | ||
1655 | config_help_arg (ConfigOption *opt) | ||
1656 | { | ||
1657 | switch (opt->arg) | ||
1658 | { | ||
1659 | case CO_Required: | ||
1660 | return "required"; | ||
1661 | case CO_Optional: | ||
1662 | return "optional"; | ||
1663 | case CO_None: | ||
1664 | return "no value"; | ||
1665 | } | ||
1666 | |||
1667 | return "unknown"; | ||
1668 | } | ||
1669 | |||
1670 | const char* | ||
1671 | config_help_type (ConfigOption *opt) | ||
1672 | { | ||
1673 | switch (opt->arg) | ||
1674 | { | ||
1675 | case CO_None: | ||
1676 | return "boolean"; | ||
1677 | default: | ||
1678 | break; | ||
1679 | } | ||
1680 | |||
1681 | switch (opt->type) | ||
1682 | { | ||
1683 | case CD_Bool: | ||
1684 | return "boolean"; | ||
1685 | case CD_Int32: | ||
1686 | return "int"; | ||
1687 | case CD_Double: | ||
1688 | return "double"; | ||
1689 | case CD_String: | ||
1690 | return "string"; | ||
1691 | } | ||
1692 | |||
1693 | return "unknown"; | ||
1694 | } | ||
1695 | |||
1696 | void | ||
1697 | config_help (void) | ||
1698 | { | ||
1699 | int i; | ||
1700 | |||
1701 | fprintf (stderr, "Expecting the following options in one or more config files on the command line:\n"); | ||
1702 | |||
1703 | for (i = 0; i < option_count; i += 1) | ||
1704 | { | ||
1705 | ConfigOption *opt = all_options + i; | ||
1706 | |||
1707 | fprintf (stderr, "%s: %s %s\n", | ||
1708 | opt->name, | ||
1709 | config_help_arg (opt), | ||
1710 | config_help_type (opt)); | ||
1711 | } | ||
1712 | } | ||
1713 | |||
1714 | FILE* | ||
1715 | config_output (const char* format, ...) | ||
1716 | { | ||
1717 | gchar *buffer; | ||
1718 | gchar *file; | ||
1719 | va_list args; | ||
1720 | FILE *f; | ||
1721 | |||
1722 | va_start (args, format); | ||
1723 | buffer = g_strdup_vprintf (format, args); | ||
1724 | va_end (args); | ||
1725 | |||
1726 | file = g_strdup_printf ("%s/%s", config_output_dir, buffer); | ||
1727 | |||
1728 | if (! (f = fopen (file, "w"))) | ||
1729 | g_error ("fopen failed: %s\n", buffer); | ||
1730 | |||
1731 | g_free (file); | ||
1732 | |||
1733 | g_free (buffer); | ||
1734 | |||
1735 | return f; | ||
1736 | } | ||
1737 | |||
1738 | |||
1739 | #include <edsio.h> | ||
1740 | #include <edsiostdio.h> | ||
1741 | #include <ctype.h> | ||
1742 | #include "xdfs.h" | ||
1743 | |||
1744 | /* Warning: very cheesy! | ||
1745 | */ | ||
1746 | |||
1747 | #ifdef DEBUG_EXTRACT | ||
1748 | FileHandle *fh2 = handle_read_file (filename); | ||
1749 | |||
1750 | guint8* debug_buf = g_malloc (buflen); | ||
1751 | |||
1752 | if (! handle_read (fh2, debug_buf, buflen)) | ||
1753 | g_error ("read failed"); | ||
1754 | #endif | ||
1755 | |||
1756 | gboolean | ||
1757 | rcs_count (const char* filename, guint *encoded_size) | ||
1758 | { | ||
1759 | char *readbuf0, *readbuf; | ||
1760 | gboolean in_string = FALSE; | ||
1761 | gboolean in_text = FALSE; | ||
1762 | guint string_start = 0; | ||
1763 | guint string_end = 0; | ||
1764 | guint current_pos = 0; | ||
1765 | /*char *current_delta = NULL;*/ | ||
1766 | FileHandle *fh = handle_read_file (filename); | ||
1767 | guint buflen = handle_length (fh); | ||
1768 | |||
1769 | (* encoded_size) = 0; | ||
1770 | |||
1771 | readbuf0 = g_new (guint8, buflen); | ||
1772 | |||
1773 | for (;;) | ||
1774 | { | ||
1775 | int c = handle_gets (fh, readbuf0, buflen); | ||
1776 | |||
1777 | readbuf = readbuf0; | ||
1778 | |||
1779 | if (c < 0) | ||
1780 | break; | ||
1781 | |||
1782 | if (strncmp (readbuf, "text", 4) == 0) | ||
1783 | in_text = TRUE; | ||
1784 | |||
1785 | if (! in_string && readbuf[0] == '@') | ||
1786 | { | ||
1787 | string_start = current_pos + 1; | ||
1788 | in_string = TRUE; | ||
1789 | readbuf += 1; | ||
1790 | } | ||
1791 | |||
1792 | current_pos += c; | ||
1793 | |||
1794 | if (in_string) | ||
1795 | { | ||
1796 | while ((readbuf = strchr (readbuf, '@'))) | ||
1797 | { | ||
1798 | if (readbuf[1] == '@') | ||
1799 | { | ||
1800 | string_start += 1; /* @@@ bogus, just counting. */ | ||
1801 | readbuf += 2; | ||
1802 | continue; | ||
1803 | } | ||
1804 | |||
1805 | in_string = FALSE; | ||
1806 | break; | ||
1807 | } | ||
1808 | |||
1809 | string_end = current_pos - 2; | ||
1810 | |||
1811 | if (in_text && ! in_string) | ||
1812 | { | ||
1813 | in_text = FALSE; | ||
1814 | |||
1815 | /*g_free (current_delta); | ||
1816 | current_delta = NULL;*/ | ||
1817 | |||
1818 | (* encoded_size) += (string_end - string_start); | ||
1819 | } | ||
1820 | |||
1821 | continue; | ||
1822 | } | ||
1823 | |||
1824 | if (isdigit (readbuf[0])) | ||
1825 | { | ||
1826 | #if 0 | ||
1827 | (* strchr (readbuf, '\n')) = 0; | ||
1828 | if (current_delta) | ||
1829 | g_free (current_delta); | ||
1830 | current_delta = g_strdup (readbuf); | ||
1831 | #endif | ||
1832 | } | ||
1833 | } | ||
1834 | |||
1835 | handle_close (fh); | ||
1836 | |||
1837 | g_free (readbuf0); | ||
1838 | |||
1839 | #if 0 | ||
1840 | if (current_delta) | ||
1841 | g_free (current_delta); | ||
1842 | #endif | ||
1843 | |||
1844 | return TRUE; | ||
1845 | } | ||
1846 | |||
1847 | #if 0 | ||
1848 | int | ||
1849 | main (int argc, char** argv) | ||
1850 | { | ||
1851 | guint size; | ||
1852 | |||
1853 | if (argc != 2) | ||
1854 | g_error ("usage: %s RCS_file\n", argv[0]); | ||
1855 | |||
1856 | if (! rcs_count (argv[1], &size)) | ||
1857 | g_error ("rcs_parse failed"); | ||
1858 | |||
1859 | return 0; | ||
1860 | } | ||
1861 | #endif | ||