pax_global_header00006660000000000000000000000064132456352150014520gustar00rootroot0000000000000052 comment=e56a28e3e958276d4986db591320eb372877a8b5 colorized-logs-2.3/000077500000000000000000000000001324563521500143205ustar00rootroot00000000000000colorized-logs-2.3/.gitignore000066400000000000000000000002521324563521500163070ustar00rootroot00000000000000.#* ansi2html ansi2txt DEADJOE Makefile *.o *.orig *~ pipetty ttyrec2ansi config.h valgrind.* CMakeCache.txt CMakeFiles/ cmake_install.cmake CTestTestfile.cmake Testing/ colorized-logs-2.3/.travis.yml000066400000000000000000000003231324563521500164270ustar00rootroot00000000000000language: c matrix: include: - os: linux dist: trusty addons: apt: packages: - cmake - os: osx script: cmake . && make && CTEST_OUTPUT_ON_FAILURE=on make test colorized-logs-2.3/CMakeLists.txt000066400000000000000000000025341324563521500170640ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8.9) project(colorized-logs C) include(CheckIncludeFiles) include(CheckLibraryExists) include(CheckFunctionExists) # OpenBSD has _ancient_ gcc but new cmake, some distros have new gcc but old # cmake, thus: if (CMAKE_VERSION VERSION_LESS "3.1") if (CMAKE_C_COMPILER_ID STREQUAL "GNU") set (CMAKE_C_FLAGS "--std=gnu99 ${CMAKE_C_FLAGS}") endif () else () set (CMAKE_C_STANDARD 99) endif () CHECK_INCLUDE_FILES(pty.h HAVE_PTY_H) CHECK_INCLUDE_FILES(libutil.h HAVE_LIBUTIL_H) CHECK_INCLUDE_FILES(util.h HAVE_UTIL_H) CHECK_LIBRARY_EXISTS(util openpty "" HAVE_LIB_UTIL) if (HAVE_LIB_UTIL) set(CMAKE_REQUIRED_LIBRARIES util) endif (HAVE_LIB_UTIL) CHECK_FUNCTION_EXISTS(openpty HAVE_FUNC_OPENPTY) add_executable(ansi2html ansi2html.c) add_executable(ansi2txt ansi2txt.c) add_executable(ttyrec2ansi ttyrec2ansi.c) add_executable(pipetty pipetty.c) IF (HAVE_LIB_UTIL) set(LIB_UTIL util) ENDIF (HAVE_LIB_UTIL) target_link_libraries(pipetty ${LIB_UTIL}) configure_file(config.h.cmake ${CMAKE_BINARY_DIR}/config.h) include_directories(${CMAKE_BINARY_DIR}) install(TARGETS ansi2html ansi2txt ttyrec2ansi pipetty RUNTIME DESTINATION bin) install( FILES ansi2html.1 ansi2txt.1 ttyrec2ansi.1 pipetty.1 DESTINATION share/man/man1 ) enable_testing() add_subdirectory(tests) colorized-logs-2.3/ChangeLog000066400000000000000000000005061324563521500160730ustar00rootroot000000000000002.3: * pipetty: hard-code PAGER=cat 2.2: * ttyrec2ansi: use cached stdio, for >4× speedup 2.1: * bugfix: infinite loop on 0xFF on unsigned-char architectures 2.0: * split out of KBtin * migrate from autotools to CMake * relicense to MIT * use a much darker palette in on-white mode, especially for brown and yellow colorized-logs-2.3/LICENSE000066400000000000000000000020001324563521500153150ustar00rootroot00000000000000Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. colorized-logs-2.3/README000066400000000000000000000013741324563521500152050ustar00rootroot00000000000000============== colorized-logs ============== Here's a handful of tools for dealing with logs with ANSI color. Some tools like gcc, dmesg, grep --color, colordiff, ccze, etc can enhance their output with color, making reading a lot more pleasant. The difference can be as big as between slogging through twenty pages of a build log to find a failure, and a swift drag of the scroller to do the same within a second. Such colored logs can be usually viewed on a terminal or with "less -R"; this package gives you: * ansi2html: convert logs to HTML * ansi2txt: drop ANSI control codes * ttyrec2ansi: drop timing data from ttyrec files * pipetty: makes a program think its stdout and stderr are connected to a terminal; use as a prefix: "pipetty dmesg|tee" colorized-logs-2.3/ansi2html.1000066400000000000000000000026131324563521500163050ustar00rootroot00000000000000.TH ansi2html 1 2016-07-21 .SH NAME ansi2html \- ansi to HTML converter .SH SYNOPSIS .B ansi2html .RB [ -n ] .RB [ -w ] .BI < log .BI > outfile .SH DESCRIPTION .B ansi2html will convert the ANSI log into an HTML page, converting ANSI color codes into appropriate tags. All standard colors and attributes are supported. .SH OPTIONS .TP .BR -n ", " --no-header Don't produce an HTML header. This allows including the log into a web page you make some other way. You are responsible for setting background color appropriately \(em to black or something dark in the default on-black mode, or to white/light when \fB-w\fR is used. Fancy-schmancy background images are fine as long as they are dark (or light) enough. .TP .BR -w ", " --white Make the background white. This is contrary to most terminals but matches most webpages. Text requested to be bolded (normally bright white) will be turned bold black, but explicit bright white \fIwill not\fR be changed, becoming invisible (just like explicit black is invisible in the normal on-black mode), same as on real terminals. .TP .BR -t ", " --title " \fItext\fR" Set the page title to the next argument. Mutually exclusive with .BR -n . .SH CAVEATS Most ANSI codes other than color codes are ignored; possibly causing misformatted output. In general, anything unfit for a hardcopy terminal won't work. .SH "SEE ALSO" .BR ansi2txt , .BR ttyrec2ansi , .BR pipetty . colorized-logs-2.3/ansi2html.c000066400000000000000000000335641324563521500164000ustar00rootroot00000000000000#include #include #include #define BOLD 0x010000 #define DIM 0x020000 #define ITALIC 0x040000 #define UNDERLINE 0x080000 #define BLINK 0x100000 #define INVERSE 0x200000 #define STRIKE 0x400000 static bool no_header=false, white=false, in_span; static int fg, bg, fl, frgb, brgb; static const char *title=0; static const char *cols[]={"BLK","RED","GRN","YEL","BLU","MAG","CYN","WHI", "HIK","HIR","HIG","HIY","HIB","HIM","HIC","HIW"}; typedef unsigned char u8; static int rgb_from_256(int i) { if (i < 16) { /* Standard colours. */ if (white) { if (i == 3) return 0x806000; if (i == 3+8) return 0xcccc00; int c = (i&1 ? 0x010000 : 0x000000) | (i&2 ? 0x000100 : 0x000000) | (i&4 ? 0x000001 : 0x000000); return i<8 ? c*0x80 : c*0xff; } if (i == 3) return 0xaa5500; int c = (i&1 ? 0xaa0000 : 0x000000) | (i&2 ? 0x00aa00 : 0x000000) | (i&4 ? 0x0000aa : 0x000000); return i<8 ? c : c+0x555555; } else if (i < 232) { /* 6x6x6 colour cube. */ i-=16; int r = i / 36, g = i / 6 % 6, b = i % 6; return (r ? r * 0x280000 + 0x370000 : 0) | (g ? g * 0x002800 + 0x003700 : 0) | (b ? b * 0x000028 + 0x000037 : 0); } else/* Grayscale ramp. */ return i*0xa0a0a-((232*10-8)*0x10101); } static inline int rgb_to_int(u8 r, u8 g, u8 b) { return (int)r<<16|(int)g<<8|(int)b; } static void span(void) { int tmp, _fg=fg, _bg=bg, _frgb=frgb, _brgb=brgb; char clbuf[32], *cl=clbuf; if (fg==-1 && bg==-1 && frgb==-1 && brgb==-1 && !fl) return; if (fl&INVERSE) { if (_fg==-1) _fg=white?0:7; if (_bg==-1) _bg=white?7:0; tmp=_fg; _fg=_bg; _bg=tmp; tmp=_frgb; _frgb=_brgb; _brgb=tmp; } if (fl&BLINK) { if (_frgb==-1) _frgb=_fg==-1?white?0x000000:0xaaaaaa:rgb_from_256(_fg); _frgb=rgb_to_int((_frgb>>16&0xff)*3/4, (_frgb>> 8&0xff)*3/4, (_frgb &0xff)*3/4)+0x606060; if (_brgb==-1) _brgb=_bg==-1?white?0xaaaaaa:0x000000:rgb_from_256(_bg); _brgb=rgb_to_int((_brgb>>16&0xff)*3/4, (_brgb>> 8&0xff)*3/4, (_brgb &0xff)*3/4)+0x606060; } if (fl&DIM) _fg=8; if (no_header) goto do_span; printf("clbuf) { *cl=0; if (cl>=clbuf+5) printf(" class=\"%s\"", clbuf+1); else /* implies no spaces */ printf(" class=%s", clbuf+1); } if (_frgb!=-1 || _brgb!=-1) { printf(" style=\""); if (_frgb!=-1) printf("color:#%06x;", _frgb); if (_brgb!=-1) printf("background-color:#%06x", _brgb); printf("\""); } printf(">"); in_span=1; return; do_span: printf(""); in_span=1; } static void unspan(void) { if (in_span) printf(no_header?"":""); in_span=0; } static void print_string(const char *restrict str) { for (; *str; str++) switch (*str) { case '<': printf("<"); break; case '>': printf(">"); break; case '&': printf("&"); break; default: putchar(*str); } } int main(int argc, const char **argv) { for (int i=1; i", white?"000":"bbb"); } else { printf( "\n" "\n" "\n"); if (title) { printf(""); print_string(title); printf("\n"); } printf( "\n" "\n" "\n" "
");
    }
    fg=bg=-1;
    fl=0;
    in_span=false;
    frgb=brgb=-1;
    int ch=getchar();
    unsigned int ntok, tok[16];
normal:
    switch (ch)
    {
    case EOF:
        unspan();
        printf("
\n"); if (!no_header) printf("\n\n"); return 0; case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 11: case 14: case 15: case 16: case 17: case 18: case 19: case 20: case 21: case 22: case 23: case 24: case 25: case 26: case 28: case 29: case 30: case 31: printf("$%02X;", ch); ch=getchar(); goto normal; case 7: printf("♪"); /* bell */ ch=getchar(); goto normal; case 8: printf("⌫"); /* backspace */ ch=getchar(); goto normal; case 12: /* form feed */ formfeed: ch=getchar(); unspan(); printf("\n
\n"); goto normal; case 13: ch=getchar(); unspan(); if (ch!=10) printf("↵\n"); goto normal; case 27: /* ESC */ ch=getchar(); goto esc; case '<': printf("<"); ch=getchar(); goto normal; case '>': printf(">"); ch=getchar(); goto normal; case '&': printf("&"); ch=getchar(); goto normal; case 127: printf("⌦"); /* delete */ ch=getchar(); goto normal; default: putchar(ch); ch=getchar(); goto normal; } /****************************************************************************/ esc: switch (ch) { case '[': break; case ']': ch=getchar(); if (ch<'0'||ch>'9') /* not an OSC, don't try to parse */ goto normal; for (;;ch=getchar()) switch (ch) { case 27: ch=getchar(); /* want ESC \ but we accept ESC anything */ case 7: ch=getchar(); /* BELL is the alternate terminator */ case EOF: goto normal; } case '%': ch=getchar(); default: ch=getchar(); goto normal; } /* [ */ ch=getchar(); ntok=0; tok[0]=0; /****************************************************************************/ csi: switch (ch) { case '?': ch=getchar(); goto csiopt; case ';': if (++ntok>=sizeof(tok)/sizeof(tok[0])) goto normal; /* too many tokens, something is fishy */ tok[ntok]=0; ch=getchar(); goto csi; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': tok[ntok]=tok[ntok]*10+ch-'0'; ch=getchar(); goto csi; case 'm': for (unsigned int i=0;i<=ntok;i++) switch (tok[i]) { case 0: fg=bg=-1; fl=0; frgb=brgb=-1; break; case 1: fl|=BOLD; fl&=~DIM; break; case 2: fl|=DIM; fl&=~BOLD; break; case 3: fl|=ITALIC; break; case 4: fl|=UNDERLINE; break; case 5: fl|=BLINK; break; case 7: fl|=INVERSE; break; case 9: fl|=STRIKE; break; case 21: case 22: fl&=~(BOLD|DIM); break; case 23: fl&=~ITALIC; break; case 24: fl&=~UNDERLINE; break; case 25: fl&=~BLINK; break; case 27: fl&=~INVERSE; break; case 29: fl&=~STRIKE; break; case 30: case 31: case 32: case 33: case 34: case 35: case 36: case 37: fg=tok[i]-30; frgb=-1; break; case 38: i++; if (i>ntok) break; if (tok[i]==5 && intok) break; if (tok[i]==5 && i512) /* sanity */ ntok=512; for (unsigned int i=0;i='0'&&ch<='9')) { ch=getchar(); goto csiopt; } ch=getchar(); goto normal; } colorized-logs-2.3/ansi2txt.1000066400000000000000000000011651324563521500161610ustar00rootroot00000000000000.TH ansi2txt 1 2002-09-02 .SH NAME ansi2txt \- ansi to plain text converter .SH SYNOPSIS .B ansi2txt < .I log > .I outfile .SH DESCRIPTION .B ansi2txt can be used to convert text containing ANSI color codes (such as MUD logs, text processed by syntax highlighters, colorgcc, colordiff, ccze, loco, etc) into plain ASCII text. It works as a filter, reading from stdin, removing all ANSI codes and sending the output to stdout. .SH CAVEATS All ANSI codes are simply ignored, including all cursor positioning ones. Sometimes this is what you want, sometimes it isn't. .SH "SEE ALSO" .BR ansi2html , .BR ttyrec2ansi , .BR pipetty . colorized-logs-2.3/ansi2txt.c000066400000000000000000000014461324563521500162450ustar00rootroot00000000000000#include int main(void) { int ch; do { ch=getchar(); while (ch==13) if ((ch=getchar())!=10) putchar(13); /* suppress \r only when followed by \n */ if (ch==27) if ((ch=getchar())=='[') while ((ch=getchar())==';'||(ch>='0'&&ch<='9')||ch=='?'); else if (ch==']'&&(ch=getchar())>=0&&ch<='9') for (;;) { if ((ch=getchar())==EOF||ch==7) break; else if (ch==27) {ch=getchar(); break;} } else if (ch=='%') ch=getchar(); else {} else if (ch!=EOF) putchar(ch); } while (ch!=EOF); return 0; } colorized-logs-2.3/config.h.cmake000066400000000000000000000001701324563521500170130ustar00rootroot00000000000000#ifndef CONFIG_H #define CONFIG_H #cmakedefine HAVE_PTY_H #cmakedefine HAVE_LIBUTIL_H #cmakedefine HAVE_UTIL_H #endif colorized-logs-2.3/pipetty.1000066400000000000000000000014231324563521500161000ustar00rootroot00000000000000.TH pipetty 1 2016-06-25 .SH NAME pipetty \- a tool to pipe from a pseudoterminal .SH SYNOPSIS .B pipetty .I command [ .I args... ] .SH DESCRIPTION .B pipetty lets you make a program unaware that its output is being redirected. This is usually useful for tools that detect whether they're run from a terminal and modify their output, typically colorizing it only when run interactively. .P Usually, disabling colors when redirected is a good thing as most tools can't cope with ANSI codes. Use .B pipetty when you do want colors, like when you're going to feed that output to .B ansi2html or .BR "less -R" . .SH CAVEATS Certain programs, such as .BR bash , don't quite like their stdin to be on a different terminal than stdout. .SH "SEE ALSO" .BR ansi2html , .BR ansi2txt , .BR less . colorized-logs-2.3/pipetty.c000066400000000000000000000034671324563521500161740ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include "config.h" #ifdef HAVE_PTY_H # include #endif #ifdef HAVE_LIBUTIL_H # include #endif #ifdef HAVE_UTIL_H # include #endif #define PN "pipetty" void syserr(const char *msg, ...) { int err=errno; va_list ap; fprintf(stderr, PN ": "); va_start(ap, msg); vfprintf(stderr, msg, ap); va_end(ap); if (err) fprintf(stderr, ": %s", strerror(err)); fprintf(stderr, "\n"); exit(1); } int main(int argc, const char **argv) { if (argc<2) errno=0, syserr("missing command"); int master, slave; if (openpty(&master, &slave, 0, 0/*termios*/, 0/*winsize*/)) syserr("can't allocate a pseudo-terminal"); if (master>31) errno=0, syserr("bad fd from openpty(): %d", master); struct termios ti; if (!tcgetattr(slave, &ti)) { cfmakeraw(&ti); tcsetattr(slave, TCSANOW, &ti); } int pid=fork(); if (pid==-1) syserr("fork failed"); if (!pid) { putenv("PAGER=cat"); close(master); setsid(); dup2(slave, 1); dup2(slave, 2); close(slave); int zero=0; ioctl(1, TIOCSCTTY, &zero); execvp(argv[1], (char*const*)argv+1); syserr("%s", argv[1]); return 127; } close(slave); char buf[16384]; int r; while ((r=read(master, buf, sizeof(buf)))>0) { if (write(1, buf, r)!=r) syserr("error writing to stdout"); } int ret; if (waitpid(pid, &ret, 0)==-1) syserr("waitpid failed"); return WIFEXITED(ret)?WEXITSTATUS(ret):WTERMSIG(ret)+128; } colorized-logs-2.3/tests/000077500000000000000000000000001324563521500154625ustar00rootroot00000000000000colorized-logs-2.3/tests/CMakeLists.txt000066400000000000000000000004161324563521500202230ustar00rootroot00000000000000add_test(ansi2txt ${CMAKE_CURRENT_SOURCE_DIR}/testsuite ansi2txt) add_test(ansi2html ${CMAKE_CURRENT_SOURCE_DIR}/testsuite ansi2html) add_test(pipetty ${CMAKE_CURRENT_SOURCE_DIR}/testsuite pipetty) add_test(ttyrec2ansi ${CMAKE_CURRENT_SOURCE_DIR}/testsuite ttyrec2ansi) colorized-logs-2.3/tests/htmlpre2txt000077500000000000000000000003361324563521500177070ustar00rootroot00000000000000#!/usr/bin/env perl use warnings; # Unfit for general use, of course. undef $/; $_=<>; s|.*.*||s; s,\n
\n,\f,sg; s,<(?:[^>"]|"[^"]*")*>,,g; s,¡,\a,g; s,<,<,g; s,>,>,g; s,&,&,g; print; colorized-logs-2.3/tests/testsuite000077500000000000000000000047251324563521500174510ustar00rootroot00000000000000#!/usr/bin/env perl use warnings; print "Running tests...\n" unless @ARGV; undef $/; my $DATADIR = $0; $DATADIR=~s|testsuite$||; $DATADIR//='.'; my $BINDIR = '.'; -x "$DATADIR/unescape" or $DATADIR="$DATADIR/tests"; -x "$DATADIR/unescape" or die "unescape missing from $DATADIR?\n"; -x "$BINDIR/ansi2html" or $BINDIR="$BINDIR/.."; -x "$BINDIR/ansi2html" or die "ansi2html not yet built in $BINDIR?\n"; my $id = $$; my ($pass,$fail) = (0,0); sub test($$$$) { my ($name, $cmd, $stdin, $stdout) = @_; return if @ARGV && !grep { $name eq $_ } @ARGV; printf '* %-20s ', $name unless @ARGV; open I, '>', "stdin.$id" or die "Can't write to \"stdin.$id\": $!\n"; print I $stdin; close I; open O, '>', "expout.$id" or die "Can't write to \"expout.$id\": $!\n"; print O $stdout; close O; my $pid = fork; unless ($pid) { alarm 20; open STDIN, '<', "stdin.$id" or die "Can't dup stdin: $!\n"; open STDOUT, '>', "stdout.$id" or die "Can't dup stdout: $!\n"; open STDERR, '>', "stderr.$id" or die "Can't dup stderr: $!\n"; exec $cmd; die "Couldn't exec: $!\n"; } waitpid $pid, 0; my $ret = $?; my $bad = system "cmp -s stdout.$id expout.$id" || system "cmp -s stderr.$id /dev/null"; if ($ret || $bad) { $fail++; print "\e[31mFAIL\e[0m\n" unless @ARGV; system "cat stderr.$id"; system "diff -u expout.$id stdout.$id"; print "return value: $ret\n" if $ret; } else { $pass++; print "\e[32mok\e[0m\n" unless @ARGV; } unlink "$_.$id" for qw(stdin expout stdout stderr); } test('ansi2txt', "$DATADIR/unescape|$BINDIR/ansi2txt", <<'IN', <<'OUT'); foo\e[1mbar\e[0m\n \e[?2004l \e]0;title1\e\\ \e]4;16;rgb:0/0/0\e\\ \e]0;title2\a \n IN foobar OUT test('ansi2html', "$DATADIR/unescape|$BINDIR/ansi2html|$DATADIR/htmlpre2txt", <<'IN', <<"OUT"); foo\e[1mbar\e[0m\n \e[?2004l \e]0;title1\e\\ \e]4;16;rgb:0/0/0\e\\ \e]0;title2\a \n \f\n \xff\n IN foobar \f \xff OUT test('pipetty', "$BINDIR/pipetty 0); colorized-logs-2.3/tests/unescape000077500000000000000000000003731324563521500172160ustar00rootroot00000000000000#!/usr/bin/env perl use warnings; while(<>) { chomp; s/^#.*//; s/\\b/\b/g; s/\\n/\n/g; s/\\e/\e/g; s/\\a/\a/g; s/\\f/\f/g; s/\\0/\0/g; s/\\x([0-9a-fA-F]{2})/sprintf('%c',hex "$1")/ge; s/\\\\/\\/g; print; } colorized-logs-2.3/ttyrec2ansi.1000066400000000000000000000006531324563521500166550ustar00rootroot00000000000000.TH ttyrec2ansi 1 2005-03-21 .SH NAME ttyrec2ansi \- ttyrec to ansi converter .SH SYNOPSIS .B ttyrec2ansi < .I log.ttyrec > .I outfile .SH DESCRIPTION .B ttyrec2ansi will strip the timing information from .ttyrec files, retaining all ANSI codes. You can then convert them to plain text using .B ansi2txt or, if they contain nothing but color codes, to HTML using .B ansi2html \&. .SH "SEE ALSO" .BR ansi2txt , .BR ansi2html . colorized-logs-2.3/ttyrec2ansi.c000066400000000000000000000027451324563521500167430ustar00rootroot00000000000000#include #include #include #include #include #include #include #ifdef __ANDROID__ #include #endif #ifdef __sun # include # define LITTLE_ENDIAN 1234 # define BIG_ENDIAN 4321 # ifdef _LITTLE_ENDIAN # define BYTE_ORDER LITTLE_ENDIAN # else # define BYTE_ORDER BIG_ENDIAN # endif #endif #ifndef htole32 #if BYTE_ORDER == LITTLE_ENDIAN #define htole32(x) (x) #else #if BYTE_ORDER == BIG_ENDIAN #define htole32(x) \ ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \ (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24)) #else #error No endianness given. #endif #endif #endif struct ttyrec_header { uint32_t sec; uint32_t usec; uint32_t len; }; #define BUFFER_SIZE 4096 int main(void) { struct ttyrec_header th; char buf[BUFFER_SIZE]; int s, n, r; while (1) { if (fread(&th, 1, 12, stdin)!=12) return 0; n=htole32(th.len); while (n>0) { s=(n>BUFFER_SIZE)?BUFFER_SIZE:n; if ((r=fread(buf, 1, s, stdin))<=0) { fprintf(stderr, "%s\n", r?strerror(errno):"File was truncated"); return 1; } if (fwrite(buf, 1, r, stdout)!=r) { fprintf(stderr, "Write error\n"); return 1; } n-=r; } } return 0; }