/* * batch.c: This is the commandline-version of VideoteXt (minus option & argument parsing, which * is done in vtxget.c) * * $Id: batch.c,v 1.2 1997/03/26 00:15:09 mb Exp mb $ * * Copyright (c) 1994-96 Martin Buck * Read COPYING for more information * */ #include #include #include #include #include #include #include #include #include #include "safe_malloc.h" #include "misc.h" #include "cct.h" #include "vtxtools.h" #include "vtxget.h" #include "vtxdecode.h" #include "toptext.h" #include "fileio.h" #include "postscript.h" #include "batch.h" #define CCTCHK(cmd) \ do { \ int cctretval; \ if ((cctretval = cmd)) { \ report_cct_error(cctretval); \ } \ } while (0) typedef struct { int page, subpage, firstsubpg, lastsubpg, seq; time_t timeout; } pagelist_t; int interleave = 20, interleave_hex = 0x20, interleave_set = FALSE; static int wrote_header; static pagelist_t *pagelist = NULL; static int lastpage = 0x100; tt_pageclass_t init_pglevel, max_pglevel; static const char *ofmt_ext[] = { "", "", "", ".vtx", #ifdef GIF_SUPPORT ".gif", ".gif", #endif ".ppm", #ifdef PNG_SUPPORT ".png", ".png", #endif ".ps", ".ps", ".ps" }; static const char *ctrl_chars[] = { "{AL_BLACK}", "{AL_RED}", "{AL_GREEN}", "{AL_YELLOW}", "{AL_BLUE}", "{AL_MAGENTA}", "{AL_CYAN}", "{AL_WHITE}", "{FLASH_ON}", "{FLASH_OFF}", "{BOX_END}", "{BOX_START}", "{HEIGHT_NORM}", "{HEIGHT_DBL}", "{SO}", "{SI}", "{GR_BLACK}", "{GR_RED}", "{GR_GREEN}", "{GR_YELLOW}", "{GR_BLUE}", "{GR_MAGENTA}", "{GR_CYAN}", "{GR_WHITE}", "{CONCEAL}", "{GR_CONT}", "{GR_SEP}", "{ESC}", "{BG_BLACK}", "{BG_NEW}", "{GR_HOLD}", "{GR_RELEASE}" }; static void my_usleep(unsigned long usec) { struct timeval timeout; timeout.tv_sec = usec / 1000000; timeout.tv_usec = usec - 1000000 * timeout.tv_sec; select(1, NULL, NULL, NULL, &timeout); } /* str2pagenum parses a string and tries to split it up into page- & subpage-number. * Check the online-help for an explanation of possible page-formats. * It returns with page = -1 if the string is invalid. */ int str2pagenum(char *str, int *page, int *subpage) { char tmpchr, tmpstr[3]; if (sscanf(str, "%x.%2[0-9a-fA-FlL]%c", page, tmpstr, &tmpchr) != 2) { if (sscanf(str, "%x%c", page, &tmpchr) != 1) { *page = -1; } *subpage = -1; } else { if (!strcasecmp("l", tmpstr)) { *subpage = -2; } else { if (sscanf(tmpstr, "%x%c", subpage, &tmpchr) != 1) { *page = -1; } else if (*subpage < 0 || *subpage > 0x7f) { *page = -1; } } } if (*page < 0x100 || *page > 0x8ff) { *page = -1; } return (*page != -1); } static void report_cct_error(int err) { fprintf(stderr, "%s: Fatal: videotext-driver returned ", smalloc_progname); switch (err) { case CCTERR: fputs("I/O-", stderr); break; case CCTEINVAL: fputs("invalid argument ", stderr); break; default: fputs("unknown ", stderr); break; } fprintf(stderr, "error for device %s\n", cct_device); exit(1); } static void print_status(const char *msg, int subpg_request, int page, int subpg) { switch (subpg_request) { case -2: fprintf(stderr, "%s: %X.loop\n", msg, page); break; case -1: fprintf(stderr, "%s: %X.next\n", msg, page); break; case 0: fprintf(stderr, "%s: %X.all\n", msg, page); break; default: fprintf(stderr, "%s: %X.%X\n", msg, page, subpg); break; } } static void write_page(const byte_t *buf, const vtx_pageinfo_t *info, int seq, int virtual) { vtxpage_t page; int pos; char *fname; FILE *file = stdout; if (outdir) { if (seq) { fname = smalloc(strlen(outdir) + 1 + strlen(fname_prefix) + 19); sprintf(fname, "%s/%s%X_%d%s", outdir, fname_prefix, info->pagenum, seq, ofmt_ext[ofmt]); } else { fname = smalloc(strlen(outdir) + 1 + strlen(fname_prefix) + 11); sprintf(fname, "%s/%s%X_%02X%s", outdir, fname_prefix, info->pagenum, info->minute, ofmt_ext[ofmt]); } if (!(file = fopen(fname, "w"))) { fprintf(stderr, "%s: %s: %s\n", smalloc_progname, fname, strerror(errno)); exit(1); } } else { fname = sstrdup("stdout"); if (!wrote_header && !stdout_sep) { if (ofmt == FMT_PS || ofmt == FMT_IPS || ofmt == FMT_CPS) { ps_write_header(file, fname, FALSE, ps_xpages, ps_ypages, ps_papertype, ps_landscape); } wrote_header = TRUE; } } page.info = *info; decode_page(buf, &page, 0, 23); if (show_header && ofmt != FMT_VTX #ifdef GIF_SUPPORT && ofmt != FMT_GIF && ofmt != FMT_GIFINT #endif && ofmt != FMT_PPM #ifdef PNG_SUPPORT && ofmt != FMT_PNG && ofmt != FMT_PNGINT #endif && ofmt != FMT_PS) { fprintf(file, "{HEADER}\npage: %X\nhour: %X\nminute: %X\ncharset: %d\n" "delete_page: %d\ninsert_headline: %d\ninsert_subtitle: %d\n" "suppress_header: %d\nupdate_page: %d\ninterrupted_seq: %d\n" "suppress_display: %d\nserial_mode: %d\n", info->pagenum, info->hour, info->minute, info->charset, info->delete, info->headline, info->subtitle, info->supp_header, info->update, info->inter_seq, info->dis_disp, info->serial); } if (!outdir && (ofmt == FMT_ISO || ofmt == FMT_ANSI || ofmt == FMT_TEXT || ((ofmt == FMT_PS || ofmt == FMT_IPS || ofmt == FMT_CPS) && stdout_sep))) { fprintf(file, "{PAGE %X/%X}\n", info->pagenum, info->minute); } switch (ofmt) { case FMT_ISO: export_ascii(file, &page, show_hidden); break; case FMT_ANSI: { attrib_t lastattr = 7; for (pos = 0; pos < VTX_PAGESIZE; pos++) { if (pos % 40 == 0) { fputs("\33[37m\33[40m", file); lastattr = 7; } if ((lastattr & VTX_COLMASK) != (page.attrib[pos] & VTX_COLMASK)) { fprintf(file, "\33[%dm", (page.attrib[pos] & VTX_COLMASK) + 30); } if ((lastattr & VTX_BGMASK) != (page.attrib[pos] & VTX_BGMASK)) { fprintf(file, "\33[%dm", ((page.attrib[pos] >> 3) & VTX_COLMASK) + 40); } if ((lastattr & VTX_FLASH) != (page.attrib[pos] & VTX_FLASH)) { fprintf(file, "\33[%dm", ((page.attrib[pos] & VTX_FLASH) ? 5 : 25)); } lastattr = page.attrib[pos]; if (show_hidden || !(page.attrib[pos] & VTX_HIDDEN)) { fputc(vtx2iso_table[page.chr[pos]], file); } else { fputc(' ', file); } if (pos % 40 == 39) { fputs("\33[0m\n", file); } } fputs("\33[0m\n", file); } break; case FMT_TEXT: { byte_t chr; for (pos = 0; pos < VTX_PAGESIZE; pos++) { chr = buf[pos]; if (!vtx_chkparity(&chr)) { fputs("{PARITY}", file); } else if (chr >= ' ') { if (page.chr[pos] >= 128 && page.chr[pos] <= 191) { fprintf(file, "{GCHR_%d}", page.chr[pos] - 128); } else { fputc(vtx2iso_table[page.chr[pos]], file); } } else { fputs(ctrl_chars[chr], file); } if (pos % 40 == 39) fputc('\n', file); } } break; case FMT_VTX: { save_vtx(file, buf, info, virtual); } break; #ifdef GIF_SUPPORT case FMT_GIF: case FMT_GIFINT: export_gif(file, &page, vtxbmfont, (ofmt == FMT_GIFINT), show_hidden); break; #endif case FMT_PPM: export_ppm(file, &page, vtxbmfont, show_hidden); break; #ifdef PNG_SUPPORT case FMT_PNG: case FMT_PNGINT: { char **msg; int error; error = export_png(file, &page, vtxbmfont, (ofmt == FMT_PNGINT), show_hidden, &msg); if (msg) { for (; *msg; msg++) { fprintf(stderr, "%s: %s: %s\n", smalloc_progname, fname, *msg); } } if (error < 0) { exit(1); } } break; #endif case FMT_PS: case FMT_IPS: case FMT_CPS: if (outdir || stdout_sep) { ps_write_header(file, fname, TRUE, ps_xpages, ps_ypages, ps_papertype, ps_landscape); } ps_write_page(&page, "Unknown", show_hidden, (ofmt == FMT_CPS), (ofmt == FMT_IPS)); if (outdir || stdout_sep) { ps_write_trailer(); } break; } if (ferror(file)) { fprintf(stderr, "%s: %s: %s\n", smalloc_progname, fname, strerror(errno)); exit(1); } free(fname); if (outdir) { fclose(file); } else { fflush(file); } } static void write_trailer(void) { if (wrote_header) { if (ofmt == FMT_PS || ofmt == FMT_IPS || ofmt == FMT_CPS) { ps_write_trailer(); } } } static int pagelist_get_next(int getall, int do_interleave, pagelist_t *page) { int pgnum; static int exit_found = FALSE; char line[256]; if (!pagelist) { /* Read page-number from stdin */ int len; if (!page || exit_found) return !exit_found; if (!fgets(line, sizeof(line), stdin)) { if (!ferror(stdin) || errno == EAGAIN) { page->page = 0; return TRUE; } else { perror("read_pagenum"); exit(1); } } len = strlen(line); if (len > 0 && line[len - 1] == '\n') line[len - 1] = '\0'; if (!strcmp(line, "")) return TRUE; if (!strcasecmp(line, "exit")) { exit_found = TRUE; return FALSE; } if (!str2pagenum(line, &page->page, &page->subpage)) { fprintf(stderr, "invalid page-number: %s\n", line); page->page = 0; } else { page->firstsubpg = page->lastsubpg = 0; page->timeout = time(NULL) + page_timeout; if (page->subpage == -2) { page->seq = 1; } else { page->seq = 0; } } return TRUE; } else if (!getall || !toptext_ok) { /* Normal mode */ int entry, loop, min_page, max_page, page_avail; min_page = ((lastpage - 0x100) + interleave_hex) % 0x800 + 0x100; do { entry = -1; max_page = 0x999; page_avail = FALSE; for (loop = 0; pagelist[loop].page; loop++) { if (pagelist[loop].page > 0) { page_avail = TRUE; if (!do_interleave) { entry = loop; break; } else if (pagelist[loop].page < max_page && pagelist[loop].page >= min_page) { max_page = pagelist[loop].page; entry = loop; } } } min_page = 0; } while (page_avail && entry < 0); if (!page_avail) return FALSE; if (page) { *page = pagelist[entry]; pagelist[entry].page = -1; page->firstsubpg = page->lastsubpg = 0; page->timeout = time(NULL) + page_timeout; if (page->subpage == -2) { page->seq = 1; } else { page->seq = 0; } } return TRUE; } else if (toptext_ok && (pgnum = toptext_getnext(lastpage, interleave, FALSE, init_pglevel, max_pglevel)) > 0) { /* TOP-Text mode */ page->page = pgnum; page->subpage = page->firstsubpg = page->lastsubpg = page->seq = 0; page->timeout = time(NULL) + page_timeout; return TRUE; } return FALSE; } static void top_pagereq(int page, int hour, int minute) { ascii_insert_pagelist(page, minute); } void ascii_insert_pagelist(int page, int subpage) { static int lastentry; pagelist = srealloc(pagelist, (lastentry + 2) * sizeof(pagelist_t)); pagelist[lastentry].page = page; pagelist[lastentry].subpage = subpage; lastentry++; pagelist[lastentry].page = 0; } static void dump_toptext(void) { FILE *file = stdout; char *fname = NULL; if (outdir) { fname = smalloc(strlen(outdir) + 1 + sizeof("toptext")); sprintf(fname, "%s/toptext", outdir); if (!(file = fopen(fname, "w"))) { fprintf(stderr, "%s: %s: %s\n", smalloc_progname, fname, strerror(errno)); exit(1); } } else { puts("{BEGIN_TOP}"); } toptext_dump(file); if (outdir) { if (ferror(file)) { fprintf(stderr, "%s: %s: %s\n", smalloc_progname, fname, strerror(errno)); exit(1); } fclose(file); free(fname); } else { puts("{END_TOP}"); fflush(stdout); } } int ascii_get_pages(int getall, int dump_top) { int buf, bufbusy, newpage, last_topstat, error_occured = FALSE; pagelist_t *dau_status; byte_t vtxraw[VTX_VIRTUALSIZE]; vtx_pageinfo_t pageinfo; init_pglevel = (getall == GET_ALLSUB) ? TT_SUBPG : TT_BLOCK; max_pglevel = (getall == GET_ALLNORM) ? TT_NORM : TT_SUBPG; dau_status = scalloc(vtx_info.numpages, sizeof(pagelist_t)); do { for (buf = 0; buf < vtx_info.numpages; buf++) { if (dau_status[buf].page) { if ((newpage = cct_checkpage(buf)) < 0) { report_cct_error(newpage); } if (!newpage) { my_usleep((vtx_info.cct_type != SAA5249) ? 200000 : 10000); CCTCHK(cct_getpage(buf, 0, 0, 39, output_virtual ? 48 : 23, vtxraw, &pageinfo)); CCTCHK(cct_reset_pgfound(buf)); lastpage = pageinfo.pagenum; last_topstat = toptext_ok; if (!toptext_newpage(vtxraw + 40, &pageinfo, &top_pagereq)) { if (dau_status[buf].subpage == -1 || dau_status[buf].subpage == -2 || !pageinfo.minute) { /* Next page was requested: get it, stop searching (if not in loop-mode) */ write_page(vtxraw, &pageinfo, dau_status[buf].seq, output_virtual); print_status("found", dau_status[buf].subpage, pageinfo.pagenum, pageinfo.minute); if (dau_status[buf].subpage == -2) { dau_status[buf].seq++; dau_status[buf].timeout = time(NULL) + page_timeout; } else { dau_status[buf].page = 0; CCTCHK(cct_stop_dau(buf)); } } else if (dau_status[buf].subpage) { /* A certain subpage was requested */ if (dau_status[buf].subpage == pageinfo.minute) { /* Right subpage arrived: get it, stop searching */ write_page(vtxraw, &pageinfo, 0, output_virtual); print_status("found", dau_status[buf].subpage, pageinfo.pagenum, pageinfo.minute); dau_status[buf].page = 0; CCTCHK(cct_stop_dau(buf)); } else { /* Wrong subpage arrived */ if (dau_status[buf].lastsubpg != pageinfo.minute) { /* Try again, increase timeout */ dau_status[buf].timeout = time(NULL) + 2 * page_timeout; dau_status[buf].lastsubpg = pageinfo.minute; if (!dau_status[buf].firstsubpg) { dau_status[buf].firstsubpg = pageinfo.minute; } else if (pageinfo.minute == dau_status[buf].firstsubpg || pageinfo.minute == dau_status[buf].firstsubpg - 1) { /* Requested subpage doesn't exist: stop searching */ print_status("nonexistent", dau_status[buf].subpage, dau_status[buf].page, dau_status[buf].subpage); error_occured = TRUE; dau_status[buf].page = 0; CCTCHK(cct_stop_dau(buf)); } } } } else if (dau_status[buf].lastsubpg != pageinfo.minute) { /* All subpages were requested: get page (if not already received), increase * timeout */ dau_status[buf].timeout = time(NULL) + 2 * page_timeout; if (pageinfo.minute != dau_status[buf].firstsubpg) { write_page(vtxraw, &pageinfo, 0, output_virtual); print_status("found", 1, pageinfo.pagenum, pageinfo.minute); } dau_status[buf].lastsubpg = pageinfo.minute; if (!dau_status[buf].firstsubpg) { dau_status[buf].firstsubpg = pageinfo.minute; } else if (pageinfo.minute == dau_status[buf].firstsubpg || pageinfo.minute == dau_status[buf].firstsubpg - 1) { fprintf(stderr, "found: %X (subpages complete)\n", pageinfo.pagenum); dau_status[buf].page = 0; CCTCHK(cct_stop_dau(buf)); } } } else { /* Received page was a TOP-page, write TOPTEXT-data if requested */ dau_status[buf].page = 0; if (toptext_ok && toptext_ok != last_topstat) { fputs("found: TOP\n", stderr); if (dump_top) { dump_toptext(); } } } } else if (time(NULL) > dau_status[buf].timeout) { print_status("timeout", dau_status[buf].subpage, dau_status[buf].page, dau_status[buf].subpage); error_occured = TRUE; dau_status[buf].page = 0; CCTCHK(cct_stop_dau(buf)); } } if (!dau_status[buf].page && pagelist_get_next(getall, interleave_set, &dau_status[buf])) { if (dau_status[buf].page) { print_status("searching", dau_status[buf].subpage, dau_status[buf].page, dau_status[buf].subpage); CCTCHK(cct_searchpage(dau_status[buf].page, 0, 0, PGMASK_PAGE, buf)); CCTCHK(cct_reset_pgfound(buf)); } } } my_usleep((vtx_info.cct_type != SAA5249) ? 100000 : 10000); bufbusy = FALSE; for (buf = 0; buf < vtx_info.numpages; buf++) { if (dau_status[buf].page) bufbusy = TRUE; } } while (bufbusy || pagelist_get_next(getall, FALSE, NULL)); if (!toptext_ok) { if (getall) { fputs("Error", stderr); error_occured = TRUE; } else if (dump_top) { fputs("Warning", stderr); } if (getall || dump_top) { fputs(": TOP-text failed ", stderr); fputs(toptext_failed ? "(invalid TOP-page encountered)\n" : "(no TOP-text broadcast)\n", stderr); } } write_trailer(); return error_occured; } int display_files(char *fname[]) { byte_t buffer[VTX_VIRTUALSIZE]; vtx_pageinfo_t info; FILE *file; int virtual = FALSE, status; for (; *fname; fname++) { if ((file = fopen(*fname, "r"))) { if ((status = load_vtx(file, buffer, &info, &virtual)) < 0) { fprintf(stderr, "%s: %s: ", smalloc_progname, *fname); switch (status) { case LOADERR: fputs(strerror(errno), stderr); break; case LOADEMAGIC: fputs("Magic number missing", stderr); break; case LOADEVERSION: fputs("Incompatible file format", stderr); break; case LOADECORRUPT: fputs("File is corrupt", stderr); break; } fputc('\n', stderr); fclose(file); break; } else { memset(buffer, vtx_mkparity(7), 8); write_page(buffer, &info, 0, virtual); fclose(file); } } else { fprintf(stderr, "%s: %s: %s\n", smalloc_progname, *fname, strerror(errno)); break; } } write_trailer(); return *fname ? -1 : 0; }