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;
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);
48 RcsVersion **versions_date;
50 RcsVersion *head_version;
51 RcsVersion *root_version;
59 BinCounter *avg_version_size;
60 IntStat* version_stat;
61 IntStat* forward_stat;
62 IntStat* reverse_stat;
64 IntStat* unencoded_stat;
65 IntStat* literal_stat;
93 enum _ConfigArgument {
99 typedef enum _ConfigArgument ConfigArgument;
101 enum _ConfigOptionType {
108 typedef enum _ConfigOptionType ConfigOptionType;
116 typedef enum _ConfigStyle ConfigStyle;
118 struct _ConfigOption {
123 ConfigOptionType type;
128 /* RCS inspection stuff
131 void rcswalk_init (void);
132 int rcswalk (RcsWalker *walker, const char* copy_base);
133 void rcswalk_report (RcsStats* stats);
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);
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);
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);
147 /* Experiment configuration stuff
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, ...);
169 #include <sys/types.h>
170 #include <sys/stat.h>
171 #include <sys/wait.h>
179 #define BUFSIZE (1<<14)
182 gboolean tmp_file_1_free = TRUE;
184 gboolean tmp_file_2_free = TRUE;
191 extern time_t str2time (char const *, time_t, long);
193 static guint8 readbuf[BUFSIZE];
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;
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 }
205 static ConfigOption config_options[] = {
206 { "config_output_base", "ob", CS_Ignore, CO_Required, CD_String, & config_output_base }
211 rcswalk_free_segment (RcsVersion *v)
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);
228 rcswalk_checkout (RcsFile* rcs, RcsWalker* walker, RcsVersion *v)
236 sprintf (cmdbuf, "co -ko -p%s %s 2>/dev/null\n", v->vname, rcs->filename);
238 g_assert (! v->segment);
240 v->segment = g_malloc (alloc);
242 if (! (out = popen (cmdbuf, "r")))
244 g_warning ("popen failed: %s: %s", cmdbuf, g_strerror (errno));
250 nread = fread (readbuf, 1, BUFSIZE, out);
257 g_warning ("fread failed: %s", g_strerror (errno));
261 if (pos + nread > alloc)
264 v->segment = g_realloc (v->segment, alloc);
267 memcpy (v->segment + pos, readbuf, nread);
272 if (pclose (out) < 0)
274 g_warning ("pclose failed");
280 if (walker->write_files)
284 if (! file && tmp_file_1_free)
287 tmp_file_1_free = FALSE;
290 if (! file && tmp_file_2_free)
293 tmp_file_2_free = FALSE;
300 if (! (out = fopen (file, "w")))
302 g_warning ("fopen failed: %s\n", file);
306 if (fwrite (v->segment, v->size, 1, out) != 1)
308 g_warning ("fwrite failed: %s\n", file);
312 if (fclose (out) < 0)
314 g_warning ("fclose failed: %s\n", file);
323 rcswalk_delta_date (RcsFile* rcs, RcsWalker* walker, void* data)
327 RcsVersion *vf = NULL;
328 RcsVersion *vt = NULL;
330 for (i = 0; i < (rcs->version_count-1); i += 1)
332 vf = rcs->versions_date[i+1];
333 vt = rcs->versions_date[i];
335 if (! vt->segment && (ret = rcswalk_checkout (rcs, walker, vt))) {
339 if ((ret = rcswalk_checkout (rcs, walker, vf))) {
343 if ((ret = walker->delta_date (rcs, vf, vt, data))) {
347 rcswalk_free_segment (vt);
350 if (vf) rcswalk_free_segment (vf);
351 if (vt) rcswalk_free_segment (vt);
357 rcswalk_delta_orig (RcsFile* rcs, RcsWalker* walker, RcsVersion* version, int *count, void* data)
363 for (c = version->children; c; c = c->next)
369 if (! version->segment)
371 if ((ret = rcswalk_checkout (rcs, walker, version))) {
376 if ((ret = rcswalk_checkout (rcs, walker, child))) {
380 reverse = version->on_trunk && child->on_trunk;
384 if ((ret = walker->delta_orig (rcs, reverse ? child : version, reverse ? version : child, data))) {
388 rcswalk_free_segment (version);
390 if ((ret = rcswalk_delta_orig (rcs, walker, child, count, data))) {
395 rcswalk_free_segment (version);
400 rcswalk_dateorder (RcsFile* rcs, RcsWalker *walker, RcsStats *stats, void* data)
404 for (i = 0; i < rcs->version_count; i += 1)
406 RcsVersion *v = rcs->versions_date[i];
408 if ((ret = rcswalk_checkout (rcs, walker, v))) {
412 stat_bincount_add_item (stats->avg_version_size, i, v->size);
414 if ((ret = walker->dateorder (rcs, v, data))) {
418 rcswalk_free_segment (v);
425 rcswalk_match (char** line_p, char* str)
427 int len = strlen (str);
429 if (strncmp (*line_p, str, len) == 0)
439 rcswalk_find_parent (RcsFile *rcs, GHashTable* hash, RcsVersion *v)
446 strcpy (mbuf, v->vname);
448 if (! (lastdot = strchr (mbuf, '.')))
451 if (! (lastdot = strchr (lastdot+1, '.')))
454 lastdot = strrchr (mbuf, '.');
455 lastn = atoi (lastdot + 1);
463 if (strcmp (mbuf, "1") == 0)
465 /* Assuming the first version is always "1.1".
467 rcs->root_version = v;
470 else if (! (lastdot = strrchr (mbuf, '.')))
473 int br = atoi (mbuf) - 1;
474 RcsVersion *p2 = NULL;
476 /* Now we have something like "2.1" and need to
477 * search for the highest "1.x" version.
482 sprintf (mbuf, "%d.%d", br, i++);
485 while ((p2 = g_hash_table_lookup (hash, mbuf)));
489 rcs->root_version = v;
499 lastdot = strrchr (mbuf, '.');
500 lastn = atoi (lastdot + 1);
506 sprintf (lastdot, ".%d", lastn);
509 while (! (p = g_hash_table_lookup (hash, mbuf)));
515 p->children = g_slist_prepend (p->children, v);
519 rcswalk_traverse_graph (RcsFile* rcs, RcsVersion* version, RcsVersion *parent)
524 version->cc = g_slist_length (version->children);
527 rcs->branch_count += (version->cc - 1);
531 /* Insure that there is proper date ordering. */
532 if (version->date <= parent->date)
533 version->date = parent->date + 1;
535 if (parent->on_trunk && version->on_trunk)
536 rcs->reverse_count += 1;
538 rcs->forward_count += 1;
541 for (c = version->children; c; c = c->next)
543 int c_dist = rcswalk_traverse_graph (rcs, c->data, version);
545 distance = MAX (distance, c_dist);
548 if (version == rcs->head_version)
553 version->chain_length = distance;
562 rcswalk_compute_chain_length (RcsFile* rcs, RcsVersion* version, RcsVersion *parent)
568 g_assert (version->chain_length >= 0);
570 else if (version->chain_length < 0)
572 version->chain_length = parent->chain_length + 1;
575 for (c = version->children; c; c = c->next)
577 rcswalk_compute_chain_length (rcs, c->data, version);
582 rcswalk_date_compare (const void* a, const void* b)
584 RcsVersion **ra = (void*) a;
585 RcsVersion **rb = (void*) b;
587 return (*ra)->date - (*rb)->date;
591 rcswalk_build_graph (RcsFile* rcs)
593 GHashTable* hash = g_hash_table_new (g_str_hash, g_str_equal);
596 for (i = 0; i < rcs->version_count; i += 1)
597 g_hash_table_insert (hash, rcs->versions[i].vname, rcs->versions + i);
599 for (i = 0; i < rcs->version_count; i += 1)
601 RcsVersion *v = rcs->versions + i;
603 v->chain_length = -1;
606 rcswalk_find_parent (rcs, hash, v);
609 rcs->head_version = g_hash_table_lookup (hash, rcs->headname);
611 rcswalk_traverse_graph (rcs, rcs->root_version, NULL);
613 rcswalk_compute_chain_length (rcs, rcs->root_version, NULL);
615 for (i = 0; i < rcs->version_count; i += 1)
616 rcs->versions_date[i] = rcs->versions + i;
618 qsort (rcs->versions_date, rcs->version_count, sizeof (RcsVersion*), & rcswalk_date_compare);
620 for (i = 0; i < rcs->version_count; i += 1)
622 RcsVersion *v = rcs->versions_date[i];
627 g_hash_table_destroy (hash);
638 rcswalk_load (RcsFile *rcs, gboolean *skip)
642 char oneline[1024], *oneline_p;
644 int version_i = 0, ret;
645 int read_state = HEAD_STATE;
647 sprintf (cmdbuf, "rlog %s", rcs->filename);
649 if (! (rlog = popen (cmdbuf, "r")))
651 g_warning ("popen failed: %s", cmdbuf);
655 rcs->headname = NULL;
657 while (fgets (oneline, 1024, rlog))
661 if (read_state == HEAD_STATE && rcswalk_match (& oneline_p, "total revisions: "))
663 if (sscanf (oneline_p, "%d", & rcs->version_count) != 1)
666 rcs->versions = g_new0 (RcsVersion, rcs->version_count);
667 rcs->versions_date = g_new (RcsVersion*, rcs->version_count);
668 read_state = BAR_STATE;
670 else if (read_state == HEAD_STATE && rcswalk_match (& oneline_p, "head: "))
672 if (sscanf (oneline_p, "%s", rbuf) != 1)
675 rcs->headname = g_strdup (rbuf);
676 read_state = HEAD_STATE; /* no change */
678 else if (read_state == BAR_STATE && rcswalk_match (& oneline_p, "----------------------------"))
680 read_state = REV_STATE;
682 else if (read_state == REV_STATE && rcswalk_match (& oneline_p, "revision "))
684 if (version_i >= rcs->version_count)
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?
690 fprintf (stderr, "rcswalk: too many versions: skipping file %s\n", rcs->filename);
697 if (sscanf (oneline_p, "%s", rbuf) != 1)
700 rcs->versions[version_i].vname = g_strdup (rbuf);
701 read_state = DATE_STATE;
703 g_assert (rcs->versions[version_i].vname);
705 else if (read_state == DATE_STATE && rcswalk_match (& oneline_p, "date: "))
707 char* semi = strchr (oneline_p, ';');
712 strncpy (rbuf, oneline_p, semi - oneline_p);
714 rbuf[semi - oneline_p] = 0;
716 rcs->versions[version_i].date = str2time (rbuf, 0, 0);
719 read_state = BAR_STATE;
725 fprintf (stderr, "rcswalk: no head version: skipping file %s\n", rcs->filename);
732 if (pclose (rlog) < 0)
734 g_warning ("pclose failed: %s", cmdbuf);
738 if ((ret = rcswalk_build_graph (rcs))) {
748 g_warning ("rlog syntax error");
753 rcswalk_free (RcsFile* rcs)
757 for (i = 0; i < rcs->version_count; i += 1)
759 g_free (rcs->versions[i].vname);
760 g_slist_free (rcs->versions[i].children);
763 g_free (rcs->filename);
764 g_free (rcs->headname);
765 g_free (rcs->versions);
766 g_free (rcs->versions_date);
771 rcswalk_one (char* rcsfile, char* copyfile, RcsWalker* walker, RcsStats* stats, void* data)
775 long long maxsize = 0;
776 gboolean skip = FALSE;
778 rcs = g_new0 (RcsFile, 1);
780 rcs->filename = g_strdup (rcsfile);
781 rcs->copyname = copyfile;
783 if ((ret = rcswalk_load (rcs, & skip))) {
787 if (walker->min_versions > rcs->version_count)
793 if (walker->max_versions < rcs->version_count)
803 if (walker->dateorder && (ret = rcswalk_dateorder (rcs, walker, stats, data))) {
807 if (walker->delta_orig)
811 if ((ret = rcswalk_delta_orig (rcs, walker, rcs->root_version, & count, data))) {
815 g_assert (count == (rcs->version_count - 1));
818 if (walker->delta_date && (ret = rcswalk_delta_date (rcs, walker, data))) {
822 for (i = 0; i < rcs->version_count; i += 1)
824 rcs->total_size += rcs->versions[i].size;
825 maxsize = MAX (rcs->versions[i].size, maxsize);
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);
835 if (walker->onefile && (ret = walker->onefile (rcs, stats, data))) {
846 rcswalk_dir (const char* dir, RcsWalker* walker, RcsStats* stats, void* data, const char* copy_dir)
852 if (copy_dir && (ret = config_create_dir (copy_dir))) {
856 if (! (thisdir = opendir (dir)))
858 g_warning ("opendir failed: %s", dir);
862 while ((ent = readdir (thisdir)))
864 char* name = ent->d_name;
868 char* copyname = NULL;
870 if (strcmp (name, ".") == 0)
873 if (strcmp (name, "..") == 0)
878 fullname = g_strdup_printf ("%s/%s", dir, name);
881 copyname = g_strdup_printf ("%s/%s", copy_dir, name);
883 if (len > 2 && strcmp (name + len - 2, ",v") == 0)
885 if ((ret = rcswalk_one (fullname, copyname, walker, stats, data))) {
891 if (stat (fullname, & buf) < 0)
893 g_warning ("stat failed: %s\n", fullname);
897 if (S_ISDIR (buf.st_mode))
899 if ((ret = rcswalk_dir (fullname, walker, stats, data, copyname))) {
911 if (closedir (thisdir) < 0)
913 g_warning ("closedir failed: %s", dir);
930 config_register (rcswalk_options, ARRAY_SIZE (rcswalk_options));
934 rcswalk (RcsWalker *walker, const char* copy_base)
945 memset (& stats, 0, sizeof (stats));
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 */
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 ());
958 if (walker->initialize)
959 data = walker->initialize ();
961 if ((ret = rcswalk_dir (rcswalk_input_dir, walker, & stats, data, copy_base))) {
965 if (walker->finalize)
967 if ((ret = walker->finalize (& stats, data))) {
975 fprintf (stderr, "rcswalk: processed %d files: too small %d; too large: %d; damaged: %d\n", process_count, small_count, large_count, skip_count);
984 rcswalk_report (RcsStats* set)
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);
998 stat_int_new (const char* name)
1000 IntStat* s = g_new0 (IntStat, 1);
1003 s->values = g_array_new (FALSE, FALSE, sizeof (long long));
1009 stat_int_add_item (IntStat* stat, long long v)
1014 stat->min = MIN (v, stat->min);
1015 stat->max = MAX (v, stat->max);
1018 g_array_append_val (stat->values, v);
1022 stat_int_stddev (IntStat *stat)
1025 double m = (double) stat->sum / (double) stat->count;
1029 for (i = 0; i < stat->count; i += 1)
1031 long long x = g_array_index (stat->values, long long, i);
1033 f += (m - (double) x) * (m - (double) x);
1036 v = f / (double) stat->count;
1042 ll_comp (const void* a, const void* b)
1044 const long long* lla = a;
1045 const long long* llb = b;
1046 return (*lla) - (*llb);
1050 stat_int_histogram (IntStat *stat)
1058 if (! (p_out = config_output ("%s.pop.hist", stat->name)))
1061 if (! (s_out = config_output ("%s.sum.hist", stat->name)))
1064 qsort (stat->values->data, stat->count, sizeof (long long), ll_comp);
1066 for (i = 0; i < stat->count; i += consec)
1068 long long ix = g_array_index (stat->values, long long, i);
1070 for (consec = 1; (i+consec) < stat->count; consec += 1)
1072 long long jx = g_array_index (stat->values, long long, i+consec);
1078 cum += consec * g_array_index (stat->values, long long, i);
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);
1084 if (fclose (p_out) < 0 || fclose (s_out) < 0)
1086 g_error ("fclose failed\n");
1091 stat_int_report (IntStat* stat)
1095 if (! (out = config_output ("%s.stat", stat->name)))
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));
1106 if (fclose (out) < 0)
1107 g_error ("fclose failed");
1109 stat_int_histogram (stat);
1116 stat_dbl_new (const char* name)
1118 DblStat* s = g_new0 (DblStat, 1);
1121 s->values = g_array_new (FALSE, FALSE, sizeof (double));
1127 stat_dbl_add_item (DblStat* stat, double v)
1132 stat->min = MIN (v, stat->min);
1133 stat->max = MAX (v, stat->max);
1136 g_array_append_val (stat->values, v);
1140 stat_dbl_stddev (DblStat *stat)
1143 double m = stat->sum / stat->count;
1147 for (i = 0; i < stat->count; i += 1)
1149 double x = g_array_index (stat->values, double, i);
1151 f += (m - x) * (m - x);
1154 v = f / stat->count;
1160 dbl_comp (const void* a, const void* b)
1162 const double* da = a;
1163 const double* db = b;
1164 double diff = (*da) - (*db);
1168 else if (diff < 0.0)
1175 stat_dbl_histogram (DblStat *stat)
1183 if (! (p_out = config_output ("%s.pop.hist", stat->name)))
1186 if (! (s_out = config_output ("%s.sum.hist", stat->name)))
1189 qsort (stat->values->data, stat->count, sizeof (double), dbl_comp);
1191 for (i = 0; i < stat->count; i += consec)
1193 double ix = g_array_index (stat->values, double, i);
1195 for (consec = 1; (i+consec) < stat->count; consec += 1)
1197 double jx = g_array_index (stat->values, double, i+consec);
1203 cum += ((double) consec) * g_array_index (stat->values, double, i);
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);
1209 if (fclose (p_out) < 0 || fclose (s_out) < 0)
1211 g_error ("fclose failed\n");
1216 stat_dbl_report (DblStat* stat)
1220 if (! (out = config_output ("%s.stat", stat->name)))
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));
1231 if (fclose (out) < 0)
1232 g_error ("fclose failed");
1234 stat_dbl_histogram (stat);
1240 stat_bincount_new (const char* name)
1242 BinCounter* bc = g_new0 (BinCounter, 1);
1245 bc->bins = g_ptr_array_new ();
1251 stat_bincount_add_item (BinCounter* bc, int bin, double val)
1256 if (bin >= bc->bins->len)
1258 g_ptr_array_set_size (bc->bins, bin+1);
1261 if (! (one = bc->bins->pdata[bin]))
1263 one = bc->bins->pdata[bin] = g_array_new (FALSE, TRUE, sizeof (double));
1270 g_array_set_size (one, last + 1);
1272 g_array_index (one, double, last) = val;
1276 stat_bincount_report (BinCounter* bc)
1282 if (! (avg_out = config_output ("%s.avg", bc->name)))
1285 if (! (raw_out = config_output ("%s.raw", bc->name)))
1288 for (i = 0; i < bc->bins->len; i += 1)
1290 GArray* one = bc->bins->pdata[i];
1295 for (j = 0; j < one->len; j += 1)
1297 double d = g_array_index (one, double, j);
1301 fprintf (raw_out, "%e ", d);
1304 fprintf (raw_out, "\n");
1305 fprintf (avg_out, "%e %d\n", sum / one->len, one->len);
1308 if (fclose (avg_out) < 0)
1309 g_error ("fclose failed");
1311 if (fclose (raw_out) < 0)
1312 g_error ("fclose failed");
1319 config_create_dir (const char* dirname)
1323 if (stat (dirname, & buf) < 0)
1325 if (mkdir (dirname, 0777) < 0)
1327 fprintf (stderr, "mkdir failed: %s\n", dirname);
1333 if (! S_ISDIR (buf.st_mode))
1335 fprintf (stderr, "not a directory: %s\n", dirname);
1344 config_clear_dir (const char* dir)
1350 sprintf (buf, "rm -rf %s", dir);
1358 static ConfigOption all_options[64];
1359 static int option_count;
1364 static gboolean once = FALSE;
1368 config_register (config_options, ARRAY_SIZE (config_options));
1373 config_register (ConfigOption *opts, int nopts)
1379 for (i = 0; i < nopts; i += 1)
1381 all_options[option_count++] = opts[i];
1386 config_set_string (const char* var, const char* val)
1390 for (i = 0; i < option_count; i += 1)
1392 ConfigOption *opt = all_options + i;
1394 if (strcmp (opt->name, var) == 0)
1396 (* (const char**) opt->value) = val;
1404 config_parse (const char* config_file)
1407 char oname[1024], value[1024];
1410 if (! (in = fopen (config_file, "r")))
1412 fprintf (stderr, "fopen failed: %s\n", config_file);
1418 ConfigOption *opt = NULL;
1420 if (fscanf (in, "%s", oname) != 1)
1423 for (i = 0; i < option_count; i += 1)
1425 if (strcmp (oname, all_options[i].name) == 0)
1427 opt = all_options + i;
1432 if (opt && opt->arg == CO_None)
1434 (* (gboolean*) opt->value) = TRUE;
1439 if (fscanf (in, "%s", value) != 1)
1441 fprintf (stderr, "no value for option: %s; file: %s\n", oname, config_file);
1447 /*fprintf (stderr, "unrecognized option: %s\n", oname);*/
1455 if (strcasecmp (value, "yes") == 0 ||
1456 strcasecmp (value, "true") == 0 ||
1457 strcmp (value, "1") == 0 ||
1458 strcasecmp (value, "on") == 0)
1460 ((gboolean*) opt->value) = TRUE;
1464 ((gboolean*) opt->value) = FALSE;
1470 if (sscanf (value, "%d", (gint32*) opt->value) != 1)
1472 fprintf (stderr, "parse error for option: %s; file: %s\n", oname, config_file);
1479 if (sscanf (value, "%lf", (double*) opt->value) != 1)
1481 fprintf (stderr, "parse error for option: %s; file: %s\n", oname, config_file);
1488 (* (const char**) opt->value) = g_strdup (value);
1508 config_compute_output_dir ()
1513 gboolean last = FALSE;
1517 for (i = 0; i < option_count; i += 1)
1519 ConfigOption *opt = all_options + i;
1521 if (opt->style == CS_Ignore)
1532 strcat (buf, opt->abbrev);
1539 if (* (gboolean*) opt->value)
1540 strcat (buf, "true");
1542 strcat (buf, "false");
1547 sprintf (tmp, "%d", (* (gint32*) opt->value));
1553 sprintf (tmp, "%0.2f", (* (double*) opt->value));
1559 if (opt->style == CS_UseAsFile)
1561 const char* str = (* (const char**) opt->value);
1562 const char* ls = strrchr (str, '/');
1564 strcat (buf, ls ? (ls + 1) : str);
1568 strcat (buf, (* (const char**) opt->value));
1575 config_output_dir = g_strdup_printf ("%s/%s", config_output_base, buf);
1586 for (i = 0; i < option_count; i += 1)
1588 ConfigOption *opt = all_options + i;
1590 if (! opt->found && opt->arg == CO_Required)
1592 fprintf (stderr, "required option not found: %s\n", all_options[i].name);
1597 if ((ret = config_compute_output_dir ())) {
1601 if ((ret = config_clear_dir (config_output_dir))) {
1605 if ((ret = config_create_dir (config_output_dir))) {
1609 if (! (out = config_output ("Options")))
1612 for (i = 0; i < option_count; i += 1)
1614 ConfigOption *opt = all_options + i;
1616 fprintf (out, "option: %s; value: ", all_options[i].name);
1622 fprintf (out, "%s", (* (gboolean*) opt->value) ? "TRUE" : "FALSE");
1627 fprintf (out, "%d", (* (gint32*) opt->value));
1632 fprintf (out, "%0.2f", (* (double*) opt->value));
1637 fprintf (out, "%s", (* (const char**) opt->value));
1642 fprintf (out, "\n");
1647 fprintf (stderr, "fclose failed\n");
1655 config_help_arg (ConfigOption *opt)
1671 config_help_type (ConfigOption *opt)
1701 fprintf (stderr, "Expecting the following options in one or more config files on the command line:\n");
1703 for (i = 0; i < option_count; i += 1)
1705 ConfigOption *opt = all_options + i;
1707 fprintf (stderr, "%s: %s %s\n",
1709 config_help_arg (opt),
1710 config_help_type (opt));
1715 config_output (const char* format, ...)
1722 va_start (args, format);
1723 buffer = g_strdup_vprintf (format, args);
1726 file = g_strdup_printf ("%s/%s", config_output_dir, buffer);
1728 if (! (f = fopen (file, "w")))
1729 g_error ("fopen failed: %s\n", buffer);
1740 #include <edsiostdio.h>
1744 /* Warning: very cheesy!
1747 #ifdef DEBUG_EXTRACT
1748 FileHandle *fh2 = handle_read_file (filename);
1750 guint8* debug_buf = g_malloc (buflen);
1752 if (! handle_read (fh2, debug_buf, buflen))
1753 g_error ("read failed");
1757 rcs_count (const char* filename, guint *encoded_size)
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);
1769 (* encoded_size) = 0;
1771 readbuf0 = g_new (guint8, buflen);
1775 int c = handle_gets (fh, readbuf0, buflen);
1782 if (strncmp (readbuf, "text", 4) == 0)
1785 if (! in_string && readbuf[0] == '@')
1787 string_start = current_pos + 1;
1796 while ((readbuf = strchr (readbuf, '@')))
1798 if (readbuf[1] == '@')
1800 string_start += 1; /* @@@ bogus, just counting. */
1809 string_end = current_pos - 2;
1811 if (in_text && ! in_string)
1815 /*g_free (current_delta);
1816 current_delta = NULL;*/
1818 (* encoded_size) += (string_end - string_start);
1824 if (isdigit (readbuf[0]))
1827 (* strchr (readbuf, '\n')) = 0;
1829 g_free (current_delta);
1830 current_delta = g_strdup (readbuf);
1841 g_free (current_delta);
1849 main (int argc, char** argv)
1854 g_error ("usage: %s RCS_file\n", argv[0]);
1856 if (! rcs_count (argv[1], &size))
1857 g_error ("rcs_parse failed");