Revision 371fb45476f4f481c803a62229edec1095f7bf30 authored by Emmanuel Thomé on 17 January 2013, 18:25:58 UTC, committed by Emmanuel Thomé on 17 January 2013, 18:26:51 UTC
1 parent 2b6f189
params.c
#include "cado.h"
#include <stdlib.h>
#include <string.h>
#include <ctype.h> /* isdigit isspace */
#include <limits.h> /* INT_MIN INT_MAX */
#include <errno.h>
#include <stdarg.h>
#include <inttypes.h>
#include "params.h"
#include "macros.h"
#include "misc.h"
void param_list_init(param_list pl)
{
memset(pl, 0, sizeof(param_list));
pl->alloc = 16;
pl->p = (parameter *) malloc(pl->alloc * sizeof(parameter));
pl->consolidated = 1;
pl->size = 0;
pl->aliases = NULL;
pl->naliases = 0;
pl->naliases_alloc = 0;
pl->switches = NULL;
pl->nswitches = 0;
pl->nswitches_alloc = 0;
}
void param_list_clear(param_list pl)
{
for(unsigned int i = 0 ; i < pl->size ; i++) {
if (pl->p[i]->key) free(pl->p[i]->key);
free(pl->p[i]->value);
}
free(pl->p);
for(int i = 0 ; i < pl->naliases ; i++) {
if (pl->aliases[i]->alias) free(pl->aliases[i]->alias);
}
free(pl->aliases);
for(int i = 0 ; i < pl->nswitches ; i++) {
free(pl->switches[i]->switchname);
}
free(pl->switches);
memset(pl, 0, sizeof(pl));
}
static void make_room(param_list pl, unsigned int more)
{
if (pl->size + more <= pl->alloc) {
return;
}
for( ; pl->size + more > pl->alloc ; ) {
pl->alloc += 8 + (pl->alloc >> 1);
}
pl->p = (parameter *) realloc(pl->p, pl->alloc * sizeof(parameter));
}
static int param_list_add_key_nostrdup(param_list pl,
char * key, char * value, enum parameter_origin o)
{
make_room(pl, 1);
int r = pl->size;
pl->p[pl->size]->key = key;
pl->p[pl->size]->value = value;
pl->p[pl->size]->origin = o;
// switches always count as parsed, of course. Hence the (value==NULL) thing
pl->p[pl->size]->parsed = (value == NULL);
// values above 1 are built within the sorting step.
pl->p[pl->size]->seen = 1;
pl->size++;
pl->consolidated = 0;
return r;
}
int param_list_add_key(param_list pl,
const char * key, const char * value, enum parameter_origin o)
{
int r = param_list_add_key_nostrdup(pl,
key ? strdup(key) : NULL, value ? strdup(value) : NULL, o);
return r;
}
struct sorting_data {
parameter_srcptr s;
int p;
};
typedef int (*sortfunc_t) (const void *, const void *);
int strcmp_or_null(const char * a, const char * b)
{
if (a == NULL) { return b ? -1 : 0; }
if (b == NULL) { return a ? 1 : 0; }
return strcmp(a, b);
}
int paramcmp(const struct sorting_data * a, const struct sorting_data * b)
{
int r;
r = strcmp_or_null(a->s->key, b->s->key);
if (r) return r;
r = a->s->origin - b->s->origin;
if (r) return r;
/* Compare by pointer position so that the sort is stable */
return a->p - b->p;
}
/* Sort (for searching), and remove duplicates. */
void param_list_consolidate(param_list pl)
{
if (pl->consolidated) {
return;
}
struct sorting_data * intermediate;
intermediate = malloc(pl->size * sizeof(struct sorting_data));
for(unsigned int i = 0 ; i < pl->size ; i++) {
intermediate[i].p = i;
intermediate[i].s = pl->p[i];
}
qsort(intermediate, pl->size, sizeof(struct sorting_data),
(sortfunc_t) ¶mcmp);
parameter * np = (parameter *) malloc(pl->alloc * sizeof(parameter));
for(unsigned int i = 0 ; i < pl->size ; i++) {
memcpy(np[i], pl->p[intermediate[i].p], sizeof(parameter));
}
free(pl->p);
pl->p = np;
free(intermediate);
// now remove duplicates. The sorting has priorities right.
unsigned int j = 0;
for(unsigned int i = 0 ; i < pl->size ; i++) {
if (pl->p[i]->key != NULL && i + 1 < pl->size && strcmp(pl->p[i]->key, pl->p[i+1]->key) == 0) {
/* The latest pair in the list is the one having highest
* priority. Do we don't do the copy at this moment.
*/
free(pl->p[i]->key);
free(pl->p[i]->value);
// this value is useful for switches
pl->p[i+1]->seen += pl->p[i]->seen;
} else {
// I can't see why there could conceivably be a problem if i == j,
// but valgrind complains...
if (i != j) {
memcpy(pl->p[j], pl->p[i], sizeof(parameter));
}
j++;
}
}
pl->size = j;
pl->consolidated = 1;
}
void param_list_remove_key(param_list pl, const char * key)
{
unsigned int j = 0;
for(unsigned int i = 0 ; i < pl->size ; i++) {
if (strcmp_or_null(pl->p[i]->key, key) == 0) {
if (pl->p[i]->key) free(pl->p[i]->key);
free(pl->p[i]->value);
} else {
if (i != j) {
memcpy(pl->p[j], pl->p[i], sizeof(parameter));
}
j++;
}
}
pl->size = j;
pl->consolidated = 1;
}
int param_list_read_stream(param_list pl, FILE *f)
{
int all_ok=1;
const int linelen = 512;
char line[linelen];
char * newkey;
char * newvalue;
while (!feof(f)) {
if (fgets(line, linelen, f) == NULL)
break;
if (line[0] == '#')
continue;
// remove possible comment at end of line.
char * hash;
if ((hash = strchr(line, '#')) != NULL) {
*hash = '\0';
}
char * p = line;
// trailing space
int l = strlen(p);
for( ; l && isspace((int)(unsigned char)p[l-1]) ; l--);
p[l] = '\0';
// leading space.
for( ; *p && isspace((int)(unsigned char)*p) ; p++, l--);
// empty ps are ignored.
if (l == 0)
continue;
// look for a left-hand-side.
l = 0;
if (!(isalpha((int)(unsigned char)p[l]) || p[l] == '_' || p[l] == '-')) {
param_list_add_key(pl, NULL, line, PARAMETER_FROM_FILE);
continue;
}
for( ; p[l] && (isalnum((int)(unsigned char)p[l]) || p[l] == '_' || p[l] == '-') ; l++);
int lhs_length = l;
if (lhs_length == 0) {
fprintf(stderr, "Parse error, no usable key for config line:\n%s\n",
line);
all_ok=0;
continue;
}
/* Now we can match (whitespace*)(separator)(whitespace*)(data)
*/
char * q = p + lhs_length;
for( ; *q && isspace((int)(unsigned char)*q) ; q++);
/* match separator, which is one of : = := */
if (*q == '=') {
q++;
} else if (*q == ':') {
q++;
if (*q == '=')
q++;
} else if (q == p + lhs_length) {
fprintf(stderr, "Parse error, no separator for config line:\n%s\n",
line);
all_ok=0;
continue;
}
for( ; *q && isspace((int)(unsigned char)*q) ; q++);
newkey = malloc(lhs_length + 1);
memcpy(newkey, p, lhs_length);
newkey[lhs_length]='\0';
newvalue = strdup(q);
param_list_add_key_nostrdup(pl, newkey, newvalue, PARAMETER_FROM_FILE);
}
param_list_consolidate(pl);
return all_ok;
}
int param_list_read_file(param_list pl, const char * name)
{
FILE * f;
f = fopen(name, "r");
if (f == NULL) {
fprintf(stderr, "Cannot read %s\n", name);
exit(1);
}
int r = param_list_read_stream(pl, f);
fclose(f);
return r;
}
int param_list_configure_alias(param_list pl, const char * key, const char * alias)
{
size_t len = strlen(alias);
ASSERT_ALWAYS(alias != NULL);
ASSERT_ALWAYS(key != NULL);
/* A switch may be aliases, but only as another switch !!! */
ASSERT_ALWAYS(key[0] != '-' || (alias[0] == '-' && alias[len-1] != '='));
if (alias[0] != '-' && alias[len-1] != '=') {
/* Then, accept both the --xxx and xxx= forms */
char * tmp;
tmp = malloc(len + 4);
snprintf(tmp, len+4, "--%s", alias);
param_list_configure_alias(pl, key, tmp);
snprintf(tmp, len+4, "%s=", alias);
param_list_configure_alias(pl, key, tmp);
free(tmp);
return 0;
}
if (pl->naliases == pl->naliases_alloc) {
pl->naliases_alloc += 1;
pl->naliases_alloc <<= 1;
pl->aliases = realloc(pl->aliases, pl->naliases_alloc * sizeof(param_list_alias));
}
pl->aliases[pl->naliases]->alias = strdup(alias);
pl->aliases[pl->naliases]->key = key;
pl->naliases++;
return 0;
}
int param_list_configure_switch(param_list pl, const char * switchname, int * ptr)
{
ASSERT_ALWAYS(switchname != NULL);
if ((pl->nswitches + 1) >= pl->nswitches_alloc) {
pl->nswitches_alloc += 2;
pl->nswitches_alloc <<= 1;
pl->switches = realloc(pl->switches, pl->nswitches_alloc * sizeof(param_list_switch));
}
char * tmp = (char *) malloc(strlen(switchname)+2);
tmp[0]='-';
if (switchname[1] == '-') { // have -- in the switch
strncpy(tmp, switchname, strlen(switchname) + 1);
} else {
strncpy(tmp+1, switchname, strlen(switchname) + 1);
}
// put the -- version
pl->switches[pl->nswitches]->switchname = strdup(tmp);
pl->switches[pl->nswitches]->ptr = ptr;
if (ptr) *ptr = 0;
pl->nswitches++;
// put the - version
pl->switches[pl->nswitches]->switchname = strdup(tmp+1);
pl->switches[pl->nswitches]->ptr = ptr;
if (ptr) *ptr = 0;
pl->nswitches++;
free(tmp);
return 0;
}
static int param_list_update_cmdline_alias(param_list pl,
param_list_alias al,
int * p_argc, char *** p_argv)
{
if (!pl->cmdline_argv0) {
pl->cmdline_argv0 = *p_argv;
pl->cmdline_argc0 = *p_argc;
}
const char * a = (*p_argv[0]);
if (al->alias[strlen(al->alias)-1] == '=') {
// since switches are aliased only by switches, we know we have a plain
// option here.
if (strncmp(a, al->alias, strlen(al->alias)) != 0)
return 0;
a += strlen(al->alias);
if (a[1] == '\0')
return 0;
// no +1 , since the alias contains the = sign already.
param_list_add_key(pl, al->key, a, PARAMETER_FROM_CMDLINE);
(*p_argv)+=1;
(*p_argc)-=1;
return 1;
}
if (strcmp(a, al->alias) == 0) {
if (al->key[0] == '-') {
/* This is a switch ; we have to treat it accordingly. The
* difficult part is to properly land on
* param_list_update_cmdline_switch at the proper time. This
* means in particular not necessarily there. It's
* considerably easier to simply change the value in the
* command line. Except that it's a const cast, it's ugly.
* Okay, my apologies, blah blah.
*/
(*p_argv)[0] = (char*) al->key;
/* leave argv and argc unchanged. */
return 0;
}
(*p_argv)+=1;
(*p_argc)-=1;
if (*p_argc == 0) {
fprintf(stderr, "Option %s requires an argument\n", a);
exit(1);
}
param_list_add_key(pl, al->key, (*p_argv[0]), PARAMETER_FROM_CMDLINE);
(*p_argv)+=1;
(*p_argc)-=1;
return 1;
}
return 0;
}
static int param_list_update_cmdline_switch(param_list pl,
param_list_switch switchpar,
int * p_argc, char *** p_argv)
{
if (!pl->cmdline_argv0) {
pl->cmdline_argv0 = *p_argv;
pl->cmdline_argc0 = *p_argc;
}
const char * a = (*p_argv[0]);
if (strcmp(a, switchpar->switchname) == 0) {
param_list_add_key(pl, switchpar->switchname, NULL, PARAMETER_FROM_CMDLINE);
(*p_argv)+=1;
(*p_argc)-=1;
if (switchpar->ptr) (*(switchpar->ptr))++;
return 1;
}
if (strncmp(switchpar->switchname, "--", 2) != 0)
return 0;
char * inv_switch;
int rc = asprintf(&inv_switch, "--no-%s", switchpar->switchname+2);
ASSERT_ALWAYS(rc>=0);
int match = strcmp(inv_switch, a) == 0;
free(inv_switch);
if (match) {
param_list_add_key(pl, a, NULL, PARAMETER_FROM_CMDLINE);
(*p_argv)+=1;
(*p_argc)-=1;
if (switchpar->ptr) (*(switchpar->ptr))=0;
return 1;
}
return 0;
}
int param_list_update_cmdline(param_list pl,
int * p_argc, char *** p_argv)
{
if (!pl->cmdline_argv0) {
pl->cmdline_argv0 = *p_argv;
pl->cmdline_argc0 = *p_argc;
}
if (*p_argc == 0)
return 0;
int i;
/* We rely on having alias scanning first, because this incurs a
* command line changed (could get along without except in the case
* of switches where it's particularly handy).
*/
for(i = 0 ; i < pl->naliases ; i++) {
if (param_list_update_cmdline_alias(pl, pl->aliases[i], p_argc, p_argv))
return 1;
}
for(i = 0 ; i < pl->nswitches ; i++) {
if (param_list_update_cmdline_switch(pl, pl->switches[i], p_argc, p_argv))
return 1;
}
const char * a = (*p_argv[0]);
if (*p_argc >= 2 && a[0] == '-') {
a++;
a+= *a == '-';
int x=0;
/* parameters _must_ begin by alphabetic characters, or
* otherwise we suffer to distinguish immediate numerical
* entries.
*/
if (!isalpha((int)(unsigned char)a[x]))
return 0;
for( ; a[x] && (isalnum((int)(unsigned char)a[x]) || a[x] == '_' || a[x] == '-') ; x++);
if (a[x] == '\0') {
param_list_add_key(pl, a, (*p_argv)[1], PARAMETER_FROM_CMDLINE);
(*p_argv)+=2;
(*p_argc)-=2;
return 1;
}
} else {
/* Check for <key>=<value> syntax */
int x=0;
for( ; a[x] && (isalnum((int)(unsigned char)a[x]) || a[x] == '_' || a[x] == '-') ; x++);
if (a[x] == '=' && a[x+1]) {
char * newkey = malloc(x+1);
memcpy(newkey, a, x);
newkey[x]='\0';
char * newvalue = strdup(a+x+1);
param_list_add_key_nostrdup(pl,
newkey, newvalue, PARAMETER_FROM_CMDLINE);
(*p_argv)+=1;
(*p_argc)-=1;
return 1;
}
}
return 0;
}
int param_strcmp(const char * a, parameter_srcptr b)
{
return strcmp_or_null(a, b->key);
}
static int assoc(param_list pl, const char * key)
{
void * found;
param_list_consolidate(pl);
found = bsearch(key, pl->p, pl->size,
sizeof(parameter), (sortfunc_t) param_strcmp);
if (found == NULL)
return -1;
parameter * c = (parameter *) found;
return c-pl->p;
}
int param_list_parse_long(param_list pl, const char * key, long * r)
{
int v = assoc(pl, key);
if (v < 0)
return 0;
char * value = pl->p[v]->value;
pl->p[v]->parsed=1;
char * end;
long res;
res = strtol(value, &end, 0);
if (*end != '\0') {
fprintf(stderr, "Parse error: parameter for key %s is not a long: %s\n",
key, value);
exit(1);
}
if (r)
*r = res;
return pl->p[v]->seen;
}
int param_list_parse_int(param_list pl, const char * key, int * r)
{
long res;
int rc;
rc = param_list_parse_long(pl, key, &res);
if (rc == 0)
return 0;
if (res > INT_MAX || res < INT_MIN) {
fprintf(stderr, "Parse error:"
" parameter for key %s does not fit within an int: %ld\n",
key, res);
exit(1);
}
if (r)
*r = res;
return rc;
}
int param_list_parse_int_and_int(param_list pl, const char * key, int * r, const char * sep)
{
int v = assoc(pl, key);
if (v < 0)
return 0;
char * value = pl->p[v]->value;
pl->p[v]->parsed=1;
char * end;
long res[2];
res[0] = strtol(value, &end, 0);
if (strncmp(end, sep, strlen(sep)) != 0) {
fprintf(stderr, "Parse error: parameter for key %s"
" must match %%d%s%%d; got %s\n",
key, sep, pl->p[v]->value);
exit(1);
}
value = end + strlen(sep);
res[1] = strtol(value, &end, 0);
if (*end != '\0') {
fprintf(stderr, "Parse error: parameter for key %s"
" must match %%d%s%%d; got %s\n",
key, sep, pl->p[v]->value);
exit(1);
}
if (r) {
r[0] = res[0];
r[1] = res[1];
}
return pl->p[v]->seen;
}
int param_list_parse_intxint(param_list pl, const char * key, int * r)
{
return param_list_parse_int_and_int(pl, key, r, "x");
}
int param_list_parse_ulong(param_list pl, const char * key, unsigned long * r)
{
int v = assoc(pl, key);
if (v < 0)
return 0;
char * value = pl->p[v]->value;
pl->p[v]->parsed=1;
char * end;
unsigned long res;
res = strtoul(value, &end, 0);
if (*end != '\0') {
fprintf(stderr, "Parse error:"
" parameter for key %s is not an ulong: %s\n",
key, value);
exit(1);
}
if (r)
*r = res;
return pl->p[v]->seen;
}
int param_list_parse_size_t(param_list pl, const char * key, size_t * r)
{
unsigned long t;
int res;
res = param_list_parse_ulong(pl, key, &t);
if (res && r) { *r = t; }
return res;
}
int param_list_parse_int64(param_list pl, const char * key, int64_t * r)
{
int v = assoc(pl, key);
if (v < 0)
return 0;
char * value = pl->p[v]->value;
pl->p[v]->parsed=1;
char * end;
int64_t res;
res = strtoimax(value, &end, 0);
if (*end != '\0') {
fprintf(stderr, "Parse error:"
" parameter for key %s is not an int64_t: %s\n",
key, value);
exit(1);
}
if (r)
*r = res;
return pl->p[v]->seen;
}
int param_list_parse_uint64(param_list pl, const char * key, uint64_t * r)
{
int v = assoc(pl, key);
if (v < 0)
return 0;
char * value = pl->p[v]->value;
pl->p[v]->parsed=1;
char * end;
uint64_t res;
res = strtoumax(value, &end, 0);
if (*end != '\0') {
fprintf(stderr, "Parse error:"
" parameter for key %s is not an uint64_t: %s\n",
key, value);
exit(1);
}
if (r)
*r = res;
return pl->p[v]->seen;
}
int param_list_parse_uint(param_list pl, const char * key, unsigned int * r)
{
unsigned long res;
int rc = param_list_parse_ulong(pl, key, &res);
if (rc == 0)
return 0;
if (res > UINT_MAX) {
fprintf(stderr, "Parse error:"
" parameter for key %s does not fit within an unsigned int: %ld\n",
key, res);
exit(1);
}
if (r)
*r = res;
return rc;
}
int param_list_parse_double(param_list pl, const char * key, double * r)
{
int v = assoc(pl, key);
if (v < 0)
return 0;
char * value = pl->p[v]->value;
pl->p[v]->parsed=1;
char * end;
double res;
res = strtod(value, &end);
if (*end != '\0') {
fprintf(stderr, "Parse error: parameter for key %s is not an int: %s\n",
key, value);
exit(1);
}
if (r)
*r = res;
return pl->p[v]->seen;
}
int param_list_parse_string(param_list pl, const char * key, char * r, size_t n)
{
int v = assoc(pl, key);
if (v < 0)
return 0;
pl->p[v]->parsed=1;
char * value = pl->p[v]->value;
if (r && strlen(value) > n-1) {
fprintf(stderr, "Parse error:"
" parameter for key %s does not fit within string buffer"
" of length %lu\n", key, (unsigned long) n);
exit(1);
}
if (r)
strncpy(r, value, n);
return pl->p[v]->seen;
}
int param_list_parse_int_list(param_list pl, const char * key, int * r, size_t n, const char * sep)
{
int v = assoc(pl, key);
if (v < 0)
return 0;
char * value = pl->p[v]->value;
pl->p[v]->parsed=1;
char * end;
int * res = malloc(n * sizeof(int));
memset(res, 0, n * sizeof(int));
size_t parsed = 0;
for( ;; ) {
res[parsed] = strtol(value, &end, 0);
if (parsed++ == n)
break;
if (parsed && *end == '\0')
break;
if (strncmp(end, sep, strlen(sep)) != 0) {
fprintf(stderr, "Parse error: parameter for key %s"
" must match %%d(%s%%d)*; got %s\n",
key, sep, pl->p[v]->value);
exit(1);
}
value = end + strlen(sep);
}
if (*end != '\0') {
fprintf(stderr, "Parse error: parameter for key %s"
" must match %%d(%s%%d){0,%zu}; got %s\n",
key, sep, n-1, pl->p[v]->value);
exit(1);
}
if (r) {
memcpy(r, res, n * sizeof(int));
}
free(res);
return parsed;
}
const char * param_list_lookup_string(param_list pl, const char * key)
{
int v = assoc(pl, key);
if (v < 0)
return NULL;
pl->p[v]->parsed=1;
const char * value = pl->p[v]->value;
return value;
}
int param_list_parse_mpz(param_list pl, const char * key, mpz_ptr r)
{
int v = assoc(pl, key);
if (v < 0)
return 0;
pl->p[v]->parsed=1;
unsigned int nread;
int rc;
char * value = pl->p[v]->value;
if (r) {
rc = gmp_sscanf(value, "%Zi%n", r, &nread);
} else {
/* scan even when the result is not wanted */
rc = gmp_sscanf(value, "%*Zi%n", &nread);
}
if (rc != 1 || value[nread] != '\0') {
fprintf(stderr, "Parse error: parameter for key %s is not an mpz: %s\n",
key, value);
exit(1);
}
return pl->p[v]->seen;
}
int param_list_parse_switch(param_list pl, const char * key)
{
int v = assoc(pl, key);
if (v < 0)
return 0;
pl->p[v]->parsed=1;
if (pl->p[v]->value != NULL) {
fprintf(stderr, "Parse error: option %s accepts no argument\n", key);
exit(1);
}
return pl->p[v]->seen;
}
int param_list_all_consumed(param_list pl, char ** extraneous)
{
for(unsigned int i = 0 ; i < pl->size ; i++) {
if (!pl->p[i]->parsed) {
pl->p[i]->parsed = 1;
if (extraneous) {
*extraneous = pl->p[i]->key;
}
return 0;
}
}
return 1;
}
int param_list_warn_unused(param_list pl)
{
int u = 0;
for(unsigned int i = 0 ; i < pl->size ; i++) {
if (pl->p[i]->origin != PARAMETER_FROM_FILE && !pl->p[i]->parsed) {
fprintf(stderr, "Warning: unused command-line parameter %s\n",
pl->p[i]->key);
u++;
}
}
return u;
}
void param_list_display(param_list pl, FILE *f)
{
param_list_consolidate(pl);
for(unsigned int i = 0 ; i < pl->size ; i++) {
fprintf(f,"%s=%s\n", pl->p[i]->key, pl->p[i]->value);
}
}
void param_list_save(param_list pl, const char * filename)
{
FILE * f = fopen(filename, "w");
if (f == NULL) {
fprintf(stderr, "fopen(%s): %s\n", filename, strerror(errno));
exit(1);
}
param_list_display(pl, f);
fclose(f);
}
int param_list_save_parameter(param_list pl, enum parameter_origin o,
const char * key, const char * format, ...)
{
va_list ap;
va_start(ap, format);
char * tmp;
int rc;
rc = vasprintf(&tmp, format, ap);
param_list_add_key(pl, key, tmp, o);
free(tmp);
return rc;
}
void print_command_line(FILE * stream, int argc, char * argv[])
{
/* print command line */
fprintf (stream, "# (%s) %s", CADO_REV, argv[0]);
for (int i = 1; i < argc; i++)
fprintf (stream, " %s", argv[i]);
fprintf (stream, "\n");
#ifdef __GNUC__
fprintf(stream, "# Compiled with gcc " __VERSION__ "\n");
#endif
fprintf(stream, "# Compilation flags " CFLAGS "\n");
}
void param_list_print_command_line(FILE * stream, param_list pl)
{
/* remember that the API for calling param_list functions mandates
* that the binary name $0 is stripped from the provided lists */
print_command_line(stream, pl->cmdline_argc0+1, pl->cmdline_argv0-1);
}
![swh spinner](/static/img/swh-spinner.gif)
Computing file changes ...