/* This is basically tzselect converted to a C program. Unfortunately, * the busybox ash shell and awk are not powerful enough to run * tzselect. * * Does not support the POSIX time option. */ #include #include #include #include #include #include #include #define TZDIR "/persistent/usr/share/zoneinfo" #define TZ_COUNTRY_TABLE TZDIR"/iso3166.tab" #define TZ_ZONE_TABLE TZDIR"/zone.tab" static char *continent_list[] = { NULL, #ifdef FULL "Africa", "Americas", "Antarctica", "Arctic Ocean", "Asia", "Atlantic Ocean", "Australia", "Europe", "Indian Ocean", "Pacific Ocean", /* "none - I want to specify the time zone using the Posix TZ format.", */ #else "Americas", "Europe", #endif }; #define N_CONTINENTS (sizeof(continent_list) / sizeof(char*)) static char *continent_real_list[] = { NULL, #ifdef FULL "Africa", "America", "Antarctica", "Arctic", "Asia", "Atlantic", "Australia", "Europe", "Indian", "Pacific", /* "none", */ #else "America", "Europe", #endif }; #define N_REAL_CONTINENTS (sizeof(continent_real_list) / sizeof(char*)) struct country_list { char *code; char *country; int id; struct country_list *next; }; static struct country_list *countries; struct region_list { char *region; int id; struct region_list *next; }; static struct region_list *regions; void new_code(char *code) { static struct country_list *tail; static int id; struct country_list *c; for(c = countries; c; c = c->next) if (strcmp(c->code, code) == 0) return; /* dup */ if (!(c = calloc(sizeof(struct country_list), 1)) || !(c->code = strdup(code))) { fputs("INTERNAL ERROR: out of memory\n", stderr); exit(1); } c->id = ++id; if (tail) tail->next = c; else countries = c; tail = c; } void add_region(char *region) { static struct region_list *tail; static int id; struct region_list *r; if (!(r = calloc(sizeof(struct region_list), 1)) || !(r->region = strdup(region))) { fputs("INTERNAL ERROR: out of memory\n", stderr); exit(1); } r->id = ++id; if (tail) tail->next = r; else regions = r; tail = r; } void file_copy(char *from, char *to) { char cmd[1024]; int rc; sprintf(cmd, "cp %s %s", from, to); rc = system(cmd); if (rc) { fprintf(stderr, "Copy from %s to %s failed\n", from, to); exit(1); } } void file_link(char *from, char *to) { char cmd[1024]; int rc; sprintf(cmd, "ln -s %s %s", from, to); rc = system(cmd); if (rc) { fprintf(stderr, "Soft link from %s to %s failed\n", from, to); exit(1); } } int main(int argc, char *argv[]) { FILE *ctfp, *ztfp; char str[256], *p; char *continent; char *code, *country, *region; int i, n, line; struct country_list *c; struct region_list *r; if (N_CONTINENTS != N_REAL_CONTINENTS) { fputs("INTERNAL ARRAY MISMATCH ERROR\n", stderr); exit(1); } if (!(ctfp= fopen(TZ_COUNTRY_TABLE, "r")) || !(ztfp = fopen(TZ_ZONE_TABLE,"r"))) { fputs("time zone files are not set up correctly\n", stderr); exit(1); } fputs("Please identify a location so that time zone rules can be set" " correctly.\n", stderr); /* Ask the user for continent or ocean. */ fputs("Please select a continent or ocean.\n", stderr); for (i = 1; i < N_CONTINENTS; ++i) printf("%2d) %s\n", i, continent_list[i]); do { fputs("#? ", stdout); fflush(stdout); if (!fgets(str, sizeof(str), stdin)) exit(1); i = strtol(str, 0, 0); if (i < 1 || i >= N_CONTINENTS) fputs("Please enter a number in range.\n", stderr); } while(i < 1 || i >= N_CONTINENTS); continent = continent_real_list[i]; /* Get list of names of countries in the continent or ocean. */ line = 0; while(fgets(str, sizeof(str), ztfp)) { if ((p = strrchr(str, '\n')) == NULL) { fputs("INTERNAL ERROR: String too short!!!\n", stderr); exit(1); } *p = '\0'; ++line; if (*str == '#') continue; /* comment */ code = strtok(str, "\t"); p = strtok(NULL, "\t"); country = strtok(NULL, "/"); region = strtok(NULL, "\t"); if (!code || !p || !country || !region) { fprintf(stderr, "INTERNAL ERROR: bad line %d in zone file\n", line); exit(1); } /* Add the code to the list */ if (strcmp(country, continent) == 0) new_code(code); } /* Lookup the country codes */ line = 0; while (fgets(str, sizeof(str), ctfp)) { if ((p = strrchr(str, '\n')) == NULL) { fputs("INTERNAL ERROR: String 2 too short!!!\n", stderr); exit(1); } *p = '\0'; ++line; if (*str == '#') continue; /* comment */ code = strtok(str, "\t"); country = strtok(NULL, "\t"); if (!code || !country) { fprintf(stderr, "INTERNAL ERROR: bad line %d in country file\n", line); exit(1); } for (c = countries; c; c = c->next) if (strcmp(code, c->code) == 0) { if (!(c->country = strdup(country))) { fputs("INTERNAL ERROR: out of memory\n", stderr); exit(1); } break; } } /* did we get all of them? */ for (c = countries; c; c = c->next) if (c->country == NULL) { fprintf(stderr, "Did not match code %s\n", c->code); exit(1); } /* Pick the country */ fputs("Please select a country.\n", stderr); for (i = 0, c = countries; c; c = c->next, ++i) { printf("%2d) %-22s", c->id, c->country); if ((i % 3) == 2) putchar('\n'); } n = i; if ((i % 3)) putchar('\n'); do { fputs("#? ", stdout); fflush(stdout); if (!fgets(str, sizeof(str), stdin)) exit(1); i = strtol(str, 0, 0); if (i < 1 || i > n) fputs("Please enter a number in range.\n", stderr); } while(i < 1 || i > n); for(n = 1, c = countries; n < i; ++n, c = c->next) ; /* Now get the regions */ rewind(ztfp); while(fgets(str, sizeof(str), ztfp)) { p = strrchr(str, '\n'); *p = '\0'; if (*str == '#') continue; /* comment */ code = strtok(str, "\t"); p = strtok(NULL, "\t"); country = strtok(NULL, "/"); region = strtok(NULL, "\t"); if (strcmp(code, c->code) == 0) add_region(region); } if (regions->next) { /* Pick a region */ fputs("Please select one of the following" " time zone regions\n", stderr); for (i = 0, r = regions; r; r = r->next, ++i) { printf("%2d) %-22s", r->id, r->region); if ((i % 3) == 2) putchar('\n'); } n = i; if ((i % 3)) putchar('\n'); do { fputs("#? ", stdout); fflush(stdout); if (!fgets(str, sizeof(str), stdin)) exit(1); i = strtol(str, 0, 0); if (i < 1 || i > n) fputs("Please enter a number in range.\n", stderr); } while(i < 1 || i > n); for(n = 1, r = regions; n < i; ++n, r = r->next) ; } else r = regions; fclose(ctfp); fclose(ztfp); /* See if the file exists */ sprintf(str, TZDIR "/%s/%s", continent, r->region); if (access(str, R_OK)) { fputs("time zone files are not set up correctly\n", stderr); exit(1); } /* Copy - this allows them to remove the /usr/share/zoneinfo directory. */ file_copy(str, "/persistent/etc/localtime"); /* It's ok if this fails. */ file_link("/persistent/etc/localtime", "/etc/localtime"); return 0; } /* * Local Variables: * compile-command: "${CROSS_COMPILE}gcc -O3 -Wall timezone.c -o timezone" * End: */