/* cdetac.c by TOYODA Eizi - GPLv3 */ #define USE_READLINE 1 #include #include #include #include #include #include "cdetac.h" #if USE_READLINE #include #include #endif long knot_to_mps(long val) { if (val == LONG_MIN) { return val; } return (val * 18520) / 3600; } long cccc2ra(const char *c) { switch (c[0]) { case 'A': return 5; case 'B': return c[1] == 'I' ? 6 : 2; case 'C': return 4; case 'D': return c[1] == 'E' ? 2 : c[1] == 'K' ? 2 : 1; case 'E': return 6; case 'F': return 1; case 'G': return 1; case 'H': return 1; case 'I': return 6; case 'K': return 4; case 'L': return 6; case 'M': return c[1] == 'J' ? 6 : 4; case 'N': return 5; case 'O': return 2; case 'P': return 4; case 'R': return c[1] == 'P' ? 5 : 2; case 'S': return c[1] == 'X' ? 6 : 3; case 'T': return 4; case 'U': return c[1] == 'A' ? 2 : 6; case 'V': return 2; case 'W': return 5; case 'Y': return 6; case 'Z': return 6; default: return LONG_MIN; } } int string_match(const char *str, const char *pat) { for (;;) { int hit; if ((*str == '\0') && (*pat == '\0')) { return 1; } if (*pat == '%') { return 1; } if ((*str == '\0') || (*pat == '\0')) { return 0; } if (*pat == '[') { hit = 0; for (;;) { pat++; if (*pat == '\0') { hit = 0; break; } if ((*pat == ']') && (pat[-1] != '[')) { break; } if ((*pat == '-') && (pat[-1] != '[') && pat[1] && (pat[1] != ']')) { if ((*str > pat[-1]) && (*str < pat[1])) { hit = 1; } } else if (*pat == *str) { hit = 1; } } } else { hit = (*str == *pat); } if (!hit) { return 0; } str++, pat++; } } long tac_atoi(const char *ary, size_t ofs, size_t width) { long r = 0; size_t i; for (i = 0; i < ofs; i++) { if (ary[i] == '\0') { return LONG_MIN; } } for (i = ofs; i < ofs + width; i++) { if ((ary[i] >= '0') && (ary[i] <= '9')) { r *= 10; r += (ary[i] - '0'); } else { return LONG_MIN; } } return r; } long tac_atosi(const char *ary, int signchar, size_t ofs, size_t width) { long r; r = tac_atoi(ary, ofs, width); if (r == LONG_MIN) { return LONG_MIN; } switch (signchar) { case '0': return r; case '1': return -r; } return LONG_MIN; } unsigned long strhash(const char *name) { unsigned long r = 0x12345678; int i; for (i = 0; name[i]; i++) { r += ((unsigned char *)name)[i] * i; } return r; } struct message_t * ms_new(const char *ahl, const char *mimj, size_t nelems) { struct message_t *ms; size_t i; long region = LONG_MIN; ms = calloc(sizeof(struct message_t), 1); if (ms == NULL) { goto err1; } StringCopy(ms->ms_ahl, ahl, 22); StringCopy(ms->ms_mimj, mimj, 6); if ((ms->ms_cccc = strchr(ms->ms_ahl, ' ')) != NULL) { ms->ms_cccc++; } ms->ms_ndes = nelems; ms->ms_des = calloc(sizeof(struct dataelem_t), ms->ms_ndes); if (ms->ms_des == NULL) { goto err2; } ms->ms_verbsiz = nelems * 3; ms->ms_verb = calloc(sizeof(char), ms->ms_verbsiz); if (ms->ms_verb == NULL) { goto err3; } ms->ms_verbend = ms->ms_verb; /* now memory okay */ ms_set(ms, "!RA", NULL, cccc2ra(ms->ms_cccc)); if (StringEq(ms->ms_mimj, "AAXX")) { ms->ms_handler = ms_surf; ms->ms_state = MSS_YYGGi; } else if (StringEq(ms->ms_mimj, "BBXX")) { ms->ms_handler = ms_surf; ms->ms_state = MSS_DD; } else if (StringEq(ms->ms_mimj, "OOXX")) { ms->ms_handler = ms_surf; ms->ms_state = MSS_DD; } else if (StringEq(ms->ms_mimj, "ZZYY")) { ms->ms_handler = ms_buoy; ms->ms_state = MSS_DD; } else if (string_match(ms->ms_mimj, "UU[A-D][A-D]")) { ms->ms_handler = ms_upper; ms->ms_state = MSS_DD; } else if (StringEq(ms->ms_mimj, "METAR") || StringEq(ms->ms_mimj, "SPECI")) { ms->ms_handler = ms_metar; ms->ms_state = MSS_MetarMain; } else { ms->ms_handler = ms_default; } return ms; err3: free(ms->ms_des); err2: free(ms); err1: return NULL; } int de_compare(const void *ap, const void *bp) { const struct dataelem_t *a = ap; const struct dataelem_t *b = bp; return strncmp(a->de_name, b->de_name, 8); } void ms_print(struct message_t *ms, FILE *fp) { size_t i; const char *sfmt = "%-7.7s %-7.7s %8s\n"; const char *dfmt = "%-7.7s %-7.7s %8ld\n"; fprintf(fp, sfmt, "$AHL", "", ms->ms_ahl); fprintf(fp, sfmt, "$MiMj", "", ms->ms_mimj); fprintf(fp, sfmt, "$ID", "", ms->ms_id); qsort(ms->ms_des, ms->ms_ndes, sizeof(struct dataelem_t), de_compare); for (i = 0; i < ms->ms_ndes; i++) { struct dataelem_t *de = &(ms->ms_des[i]); if (de->de_name[0] == 0) { continue; } if (de->de_val == LONG_MIN) { fprintf(fp, sfmt, de->de_name, de->de_lev, "/"); } else { fprintf(fp, dfmt, de->de_name, de->de_lev, de->de_val); } } fprintf(fp, "!RAW %s: [[", ms->ms_mimj); fputs(ms->ms_verb, fp); fputs("]]\n", fp); fprintf(fp, "\n"); } void ms_del(struct message_t *ms) { free(ms->ms_des); free(ms->ms_verb); free(ms); } int ms_findname(struct message_t *ms, const char *name, const char *lev, int setp) { int i, is, ie; if (lev == NULL) { lev = ""; } is = ((strhash(name) + strhash(lev)) % ms->ms_ndes); for (i = 0; i < ms->ms_ndes; i++) { ie = (i + is) % ms->ms_ndes; if (setp && ms->ms_des[ie].de_name[0] == '\0') { StringCopy(ms->ms_des[ie].de_name, name, 7); StringCopy(ms->ms_des[ie].de_lev, lev, 7); return ie; } else if ( (strncmp(ms->ms_des[ie].de_name, name, 7) == 0) && (strncmp(ms->ms_des[ie].de_lev, lev, 7) == 0) ) { return ie; } } return -1; } long ms_get(struct message_t *ms, const char *name, const char *lev) { int i; i = ms_findname(ms, name, lev, 0); if (i == -1) { return LONG_MIN; } return ms->ms_des[i].de_val; } int ms_set(struct message_t *ms, const char *name, const char *lev, long val) { int i; i = ms_findname(ms, name, lev, 1); if (i == -1) { return -1; } StringCopy(ms->ms_des[i].de_lev, lev, 7); ms->ms_des[i].de_val = val; return 0; } static struct msgfilter_t { char ttaaii[128]; char cccc[128]; char yygggg[128]; char mimj[128]; char id[128]; } msgfilter; int ms_filter_set(int kind, const char *pat) { switch (kind) { case 't': strncpy(msgfilter.ttaaii, pat, 126); strcat(msgfilter.ttaaii, "%"); return 0; case 'c': strncpy(msgfilter.cccc, pat, 126); strcat(msgfilter.cccc, "%"); return 0; case 'y': strncpy(msgfilter.yygggg, pat, 126); strcat(msgfilter.yygggg, "%"); return 0; case 'm': strncpy(msgfilter.mimj, pat, 126); strcat(msgfilter.mimj, "%"); printf("## %s\n", msgfilter.mimj); return 0; case 'i': strncpy(msgfilter.id, pat, 126); strcat(msgfilter.id, "%"); return 0; } } int ms_filter_hit(struct message_t *ms) { if (msgfilter.ttaaii[0]) { if (!string_match(ms->ms_ahl, msgfilter.ttaaii)) { return 0; } } if (msgfilter.cccc[0]) { if (!string_match(ms->ms_cccc, msgfilter.cccc)) { return 0; } } if (msgfilter.yygggg[0]) { if (!string_match(ms->ms_cccc + 5, msgfilter.yygggg)) { return 0; } } if (msgfilter.mimj[0]) { if (!string_match(ms->ms_mimj, msgfilter.mimj)) { return 0; } } if (msgfilter.id[0]) { if (!string_match(ms->ms_id, msgfilter.id)) { return 0; } } return 1; } struct message_t * ms_flush(struct message_t *ms) { if (ms == NULL) { return NULL; } ms->ms_handler(ms, TTEQ, "=", TTEOT, "NNNN"); if (ms_filter_hit(ms)) { ms_print(ms, stdout); } ms_del(ms); return NULL; } int ms_default(struct message_t *ms, enum TokenType tt, const char *token, enum TokenType tt2, const char *token2) { if (((ms->ms_verbend - ms->ms_verb) + strlen(token) + 4) >= ms->ms_verbsiz) { return 1; } if (tt == TTXWORD) { strcpy(ms->ms_verbend, "#"); } strcat(ms->ms_verbend, token); strcat(ms->ms_verbend, " "); ms->ms_verbend += strlen(ms->ms_verbend); return 0; } int dispatch( enum TokenType tt, const char *token, enum TokenType tt2, const char *token2) { static struct message_t *ms = NULL; /* strlen("TTAAii CCCC YYGGgg") = 18 */ /* strlen("TTAAii CCCC YYGGgg BBB") = 22 */ static char yyahl[22 + 1] = ""; /* "CLIMAT" is the longest */ static char yymimj[6 + 1] = ""; switch (tt) { case TTAHL: StringCopy(yyahl, token, 22); break; case TTEQ: ms = ms_flush(ms); if (strncmp(yymimj, "TT", 2) == 0) { yymimj[0] = '\0'; } break; case TTEOT: case TTEOF: ms = ms_flush(ms); yymimj[0] = yyahl[0] = '\0'; break; case TTMIMJ: StringCopy(yymimj, token, 6); if (*yyahl) { ms = ms_new(yyahl, yymimj, 5000); if (ms == NULL) { perror(yyahl); return 1; } } break; default: if (ms) { ms->ms_handler(ms, tt, token, tt2, token2); } break; } } static const char **cdetac_argv; /* * 0: no prompt * 1: prompt for each datafile */ int cdetac_interactive = 0; int yywrap(void) { for (;;) { if (*cdetac_argv == NULL) { return 1; } if (StringEq(*cdetac_argv, "-")) { yyin = stdin; } else { yyin = fopen(*cdetac_argv, "r"); } cdetac_argv++; if (yyin) { break; } perror(cdetac_argv[-1]); } return 0; } int cdetac_command(const char *cmd) { const char *arg; if (*cmd == '-') { cmd++; } arg = cmd + 1; while (*arg && isspace(*arg)) { arg++; } switch (*cmd) { case 'h': fputs("usage: cdetac [-command] [--] [files ...]\n", stdout); fputs(" -h this help\n", stdout); fputs(" -tTTAAii filters GTS header\n", stdout); fputs(" -cCCCC filters GTS originator centre\n", stdout); fputs(" -yYYGGgg filters GTS day-hour-minute\n", stdout); fputs(" -mMiMj filters code form indicator MiMj\n", stdout); return 1; break; case 't': case 'c': case 'y': case 'm': case 'i': ms_filter_set(*cmd, arg); break; case 'f': { static char fnam[255 + 1]; static const char *filebuf[2]; StringCopy(fnam, arg, 255); filebuf[0] = fnam; filebuf[1] = NULL; cdetac_argv = filebuf; yywrap(); } break; default: fprintf(stderr, "unknown command (%c)\n", *cmd); return 1; } return 0; } int prompt(void) { while (1) { char *line, *newline; line = readline("cdetac> "); if ((line == NULL) || (*line == 'q')) { putchar('\n'); return 1; } if (*line) { add_history(line); } if ((newline = strrchr(line, '\n')) != NULL) { *newline = '\0'; } if ((newline = strrchr(line, '\r')) != NULL) { *newline = '\0'; } if ((*line == '\0') || (*line == 'd')) { return 0; } else { add_history(line); cdetac_command(line); } } } int main(int argc, const char **argv) { enum TokenType tt, tt2; char prevtoken[31 + 1]; int argi = 1; while (argv[argi] && (argv[argi][0] == '-')) { if (StringEq(argv[argi], "--")) { argi++; break; } else { if (cdetac_command(argv[argi] + 1) != 0) { return 2; } } argi++; } cdetac_argv = argv + argi; if (*cdetac_argv == NULL) { cdetac_interactive = 1; fputs("TAC Decoder in C by TOYODA Eizi. Distributed by GNU GPL v3.\n", stdout); prompt_again: if (prompt() != 0) { return 0; } } else { if (yywrap()) { return 1; } } tt = TTEOF; while (tt2 = yylex()) { if (tt != TTEOF) { dispatch(tt, prevtoken, tt2, yytext); } tt = tt2; StringCopy(prevtoken, yytext, 31); } dispatch(tt, prevtoken, TTEOF, ""); if (cdetac_interactive) { goto prompt_again; } return 0; }