LinuxQuestions.org
Share your knowledge at the LQ Wiki.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Linux Forums > Linux - Software
User Name
Password
Linux - Software This forum is for Software issues.
Having a problem installing a new program? Want to know which application is best for the job? Post your question in this forum.

Notices


Reply
  Search this Thread
Old 01-22-2012, 01:52 PM   #1
H_TeXMeX_H
LQ Guru
 
Registered: Oct 2005
Location: $RANDOM
Distribution: slackware64
Posts: 12,928
Blog Entries: 2

Rep: Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301
Image viewer that reads stdin and will not write cache files to disk


I guess I should explain a bit of what I'm trying to do.

I have tried truecrypt and I don't like it. I don't like the way it operates, and I don't like its limitations.

I'm trying to make my own form of encryption + plausible deniability. To do this and make it efficient, I need programs that will read from stdin (a pipe that has been unencrypted) and display content WITHOUT writing this data do disk (for obvious reasons). I've found programs that can do this for other media types: movies, text, etc. However, I have NOT yet found a suitable image viewer. I need an image viewer that won't cache to disk.

I've tried 'xv' and 'display', but my studies show that they write cache files to /tmp. I've tried to disable this, but it doesn't see possible.

Thanks.
 
Old 01-22-2012, 02:27 PM   #2
speck
Member
 
Registered: Nov 2001
Location: US
Distribution: Slackware 14.2
Posts: 375

Rep: Reputation: 115Reputation: 115
Have you tried feh yet? I know it can read from stdin and I haven't noticed it leaving any cache/thumbnail droppings on the disk.
 
Old 01-22-2012, 02:49 PM   #3
H_TeXMeX_H
LQ Guru
 
Registered: Oct 2005
Location: $RANDOM
Distribution: slackware64
Posts: 12,928

Original Poster
Blog Entries: 2

Rep: Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301
Well, I have installed 'feh' but I cannot get it to read from stdin. It only reads a file list from stdin.
 
Old 01-22-2012, 03:13 PM   #4
Nominal Animal
Senior Member
 
Registered: Dec 2010
Location: Finland
Distribution: Xubuntu, CentOS, LFS
Posts: 1,723
Blog Entries: 3

Rep: Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948
Why not just disable swap and use tmpfs for /tmp? It will take care of all applications that create temporary files in one fell swoop.

I use tmpfs /tmp on minilaptops with 1.5G or more memory for performance reasons, and I've never had any issues.
 
1 members found this post helpful.
Old 01-22-2012, 03:32 PM   #5
Reuti
Senior Member
 
Registered: Dec 2004
Location: Marburg, Germany
Distribution: openSUSE 15.2
Posts: 1,339

Rep: Reputation: 260Reputation: 260Reputation: 260
What about process substitution:
Code:
$ feh <(cat file.jpg)
The cat you could replace with your decryption routine.
 
Old 01-23-2012, 02:16 AM   #6
H_TeXMeX_H
LQ Guru
 
Registered: Oct 2005
Location: $RANDOM
Distribution: slackware64
Posts: 12,928

Original Poster
Blog Entries: 2

Rep: Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301
Code:
bash-4.1$ feh <(cat glaucom_0001.jpg)
feh - No loadable images specified.
Use feh --help for detailed usage information
Well, that didn't work.

I have thought of using tmpfs, and I will try to get imagemagick to use it.
 
Old 01-23-2012, 02:32 AM   #7
H_TeXMeX_H
LQ Guru
 
Registered: Oct 2005
Location: $RANDOM
Distribution: slackware64
Posts: 12,928

Original Poster
Blog Entries: 2

Rep: Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301
Ok, I guess using 'MAGICK_TMPDIR=/dev/shm' works. I was hoping for a better solution, but this will do. Thanks for the help.
 
Old 01-23-2012, 07:53 PM   #8
Nominal Animal
Senior Member
 
Registered: Dec 2010
Location: Finland
Distribution: Xubuntu, CentOS, LFS
Posts: 1,723
Blog Entries: 3

Rep: Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948
Just for fun, I created a helper program that takes a command as an argument, and a stream of concatenated image files from standard input. It reads the next image, then fork()s and exec()s the desired command, supplying the image data to it via its standard input. After the command exits, the helper repeats the same for the next image.

This program is very crude. I wrote the format parsers from the specs, not by trial-and-error, so the logic should be on a sound basis, barring any typos and thinkos, of course. Consider it only a working prototype. I posted it in case somebody would like to (rewrite and) develop it further. I consider this code CC0 and/or public domain, whichever is applicable in your location.

The GIF and PNG parsers should be okay. GIF and PNG files have very straightforward structures, and are easy to parse and walk through. The JPEG parser I'm not absolutely sure of, although it does seem to work on all images I can throw at it. It does also tell you if it skips data it does not feed to the viewer.

It is my understanding that according to ITU-T.81, in the JPEG scan data, all 0xFFs should be stuffed (followed by a zero or one), or they indicate an RST marker (followed one byte between 0xD0 and 0xD7 inclusive), or they indicate the end of the scan data (being a normal JPEG marker). The parser exploits this, only looking at the 0xFF and following bytes in scan data. For all the non-scan data, it of course relies on the marker structure.

Here it is, per-image.c:
Code:
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>

#define   EXIT_READ     99
#define   EXIT_MEMORY   98
#define   EXIT_PIPE     97
#define   EXIT_FORK     96
#define   EXIT_EXEC     95
#define   EXIT_SIGNAL   94
#define   EXIT_INPUT    93

/* Input buffer.
*/
static unsigned char    *input_data = NULL; /* Input buffer */
static size_t            input_have = 0;    /* Number of bytes available */
static size_t            input_size = 0;    /* Number of bytes allocated */

/* Command to execute.
*/
static const char        *command_file = NULL;
static const char *const *command_args = NULL;

/* Helper function to avoid using stdio: Write to standard error.
*/
static int wrerr(const void *const ptr, const size_t len)
{
    const int         descriptor = STDERR_FILENO;
    const char *const q = (const char *)ptr + len;
    const char       *p = (const char *)ptr;
    ssize_t           n;

    while (p < q) {

        n = write(descriptor, p, (size_t)(q - p));
        if (n > (ssize_t)0)
            p += n;
        else
        if (n != (ssize_t)-1)
            return EIO;
        else
        if (errno != EINTR)
            return errno;

    }

    return 0;
}

static inline int wrerrs(const char *const string)
{
    return (string && *string) ? wrerr(string, strlen(string)) : 0;
}

/* Output a fatal error message, and exit.
*/
static void fatal(const int exitstatus, const int num_strings, ...)
{
    const char *s;
    int         i;
    va_list     a;

    va_start(a, num_strings);
    for (i = 0; i < num_strings; i++) {
        s = va_arg(a, const char *);
        if (s && *s)
            if (wrerr(s, strlen(s)))
                break;
    }
    va_end(a);

    exit(exitstatus);
}

/* Helper to encode one signed integer to fatal error messages.
*/
static const char *errint(const int value)
{
    static unsigned char    buffer[4 + 4*sizeof(int)];
    unsigned char          *q = buffer + sizeof(buffer);
    unsigned int            u = (value < 0) ? (unsigned int)(-value) : value;

    *(--q) = '\0';
    *(--q) = '0' + (u % 10U); u /= 10U;

    while (u) {
        *(--q) = '0' + (u % 10U);
        u /= 10U;
    }

    if (value < 0)
        *(--q) = '-';

    return (const char *)q;
}

/* Close a file descriptor without affecting errno.
*/
static inline int closefd(const int fd)
{
    int saved_errno, result;

    saved_errno = errno;

    do {
        result = close(fd);
    } while (result == -1 && errno == EINTR);
    if (result == -1)
        result = errno;
    else
        result = 0;

    errno = saved_errno;

    return result;
}

/* Move a file descriptor without affecting errno.
*/
static inline int dupfd(const int oldfd, const int newfd)
{
    int saved_errno, result;

    if (oldfd == newfd)
        return 0;

    saved_errno = errno;

    do {
        result = dup2(oldfd, newfd);
    } while (result == -1 && errno == EINTR);
    if (result == -1)
        result = errno;
    else
        result = 0;

    errno = saved_errno;

    return result;
}

/* Fork and execute the command, supplying size bytes from the input buffer.
 * Return values:
 *   0: Success (or child detached)
 *  <0: Killed by signal -(return value)
 *  >0: Exit status from child process.
*/
static int run(const size_t size)
{
    pid_t   child, pid;
    int     pipefd[2];
    int     result;

    do {
        result = pipe(pipefd);
    } while (result == -1 && errno == EINTR);
    if (result == -1)
        fatal(EXIT_PIPE, 3, "Cannot create a pipe: ", strerror(errno), ".\n");

    child = fork();
    if (child == (pid_t)-1)
        fatal(EXIT_FORK, 3, "Cannot fork: ", strerror(errno), ".\n");

    if (!child) {
        /* Child process. */

        closefd(STDIN_FILENO);

        if (dupfd(pipefd[0], STDIN_FILENO))
            exit(EXIT_PIPE);

        if (closefd(pipefd[0]) ||
            closefd(pipefd[1]))
            exit(EXIT_PIPE);

        execvp(command_file, (char *const *)command_args);

        exit(EXIT_EXEC);
    }

    /* Parent process. */
    closefd(pipefd[0]);

    /* Write the data to the write end of the pipe. */
    {   const char       *p = (char *)input_data;
        const char *const q = (char *)input_data + size;
        ssize_t           n;

        while (p < q) {
            n = write(pipefd[1], p, (size_t)(q - p));
            if (n > (ssize_t)0)
                p += n;
            else
            if (n != (ssize_t)-1)
                break;
            else
            if (errno != EINTR)
                break;
        }

        closefd(pipefd[1]);
    }

    /* Wait for the child process to exit. */
    do {
        pid = waitpid(child, &result, 0);
    } while (pid == (pid_t)-1 && errno == EINTR);

    /* If the child detached, assume success. */
    if (pid == (pid_t)-1)
        return 0;

    if (WIFEXITED(result))
        return WEXITSTATUS(result) & 127;

    if (WIFSIGNALED(result))
        return -(WTERMSIG(result) & 127);

    return 0;
}

/* Function to increase an allocation block.
*/
static inline size_t pad(const size_t n)
{
    return (n | (size_t)8191) + (size_t)8129;
}

/* Read more input, until at least limit bytes buffered.
*/
static void need(const size_t limit)
{
    const int    descriptor = STDIN_FILENO;
    ssize_t      n;

    while (input_have < limit) {

        if (input_have >= input_size) {
            const size_t   size = pad(input_have);
            unsigned char *data;

            if (size < input_size)
                fatal(EXIT_MEMORY, 1, "Out of memory.\n");

            data = realloc(input_data, size);
            if (!data)
                fatal(EXIT_MEMORY, 1, "Out of memory.\n");

            input_data = data;
            input_size = size;
        }

        do {
            n = read(descriptor, input_data + input_have, input_size - input_have);
        } while (n == (ssize_t)-1 && errno == EINTR);

        if (n > (ssize_t)0)
            input_have += n;
        else
        if (n != (ssize_t)-1)
            break;
        else
        if (errno != EINTR)
            fatal(EXIT_READ, 3, "Error reading standard input: ", strerror(errno), ".\n");
    }

    if (input_size < limit) {
        const size_t     size = pad(limit);
        unsigned char   *data;

        if (size < input_size)
            fatal(EXIT_MEMORY, 1, "Out of memory.\n");

        data = realloc(input_data, size);
        if (!data)
            fatal(EXIT_MEMORY, 1, "Out of memory.\n");

        input_data = data;
        input_size = size;
    }

    if (input_have < limit)
        memset(input_data + input_have, 0, input_size - input_have);

    return;
}

/* Peek a byte in the input buffer.
 * After the call, all bytes prior to offset are also available.
*/
static inline unsigned int peekb(const size_t offset)
{
    if (offset >= input_have)
        need(offset + 1);

    return (unsigned int)(input_data[offset]);
}

/* Peek a 16-bit word in the input buffer. Offset is in bytes.
 * After the call, all bytes prior to offset are also available.
*/
static inline unsigned int peekw(const size_t offset)
{
    if (offset + 1 >= input_have)
        need(offset + 2);

    return (unsigned int)(input_data[offset + 0]) * 256U
         + (unsigned int)(input_data[offset + 1]);
}

/* Peek a 32-bit word in the input buffer. Offset is in bytes.
 * After the call, all bytes prior to offset are also available.
*/
static inline unsigned long peekl(const size_t offset)
{
    if (offset + 3 >= input_have)
        need(offset + 4);

    return (unsigned long)(input_data[offset + 0]) * 16777216UL
         + (unsigned long)(input_data[offset + 1]) * 65536UL
         + (unsigned long)(input_data[offset + 2]) * 256UL
         + (unsigned long)(input_data[offset + 3]);
}

/* Peek a number of bytes in the input buffer,
 * returning zero if the data matches.
*/
static inline int peek(const size_t offset, const void *const data, const size_t bytes)
{
    if (offset + bytes > input_have)
        need(offset + bytes);

    return memcmp(data, input_data + offset, bytes);
}

/* Discard bytes first bytes of input.
 * Returns nonzero if there is further input available.
*/
static size_t discard(const size_t bytes)
{
    if (bytes >= input_have)
        need(bytes + 1);

    if (bytes < input_have) {
        memmove(input_data, input_data + bytes, input_have - bytes);
        input_have = input_have - bytes;
    } else
        input_have = 0;

    return input_have;
}

/*
 * Image data parsers.
 *
 * Each of the is_format() functions will return 0 if the input buffer
 * does not begin with an image of said format, and a nonzero length
 * otherwise.
*/

/* GIF image parser.
*/
static size_t is_gif(void)
{
    size_t  length = 0, n;
    int     flags;

    /* Verify GIF header */
    if (peek(0, "GIF87a", 6) && peek(0, "GIF89a", 6))
        return 0;

    /* Skip Logical Screen Descriptor */
    flags = peekb(10);
    if (flags & 128)
        length = 13 + (6 << (flags & 7));
    else
        length = 13;

    /* Parse until end of data. */
    while (1) {
        switch (peekb(length)) {

        case 0x3B: /* Trailer */
            return length + 1;

        case 0x21: /* Extension */
            n = 1;
            do {
                length += n + 1;
                n = peekb(length);
            } while (n);
            length++;
            break;

        case 0x2C: /* Image descriptor */
            flags = peekb(length + 9);
            if (flags & 128)
                length += 10 + (6 << (flags & 7));
            else
                length += 10;
            n = 0;
            do {
                length += n + 1;
                n = peekb(length);
            } while (n);
            length++;
            break;

        default: /* Bad data. */
            return 0;
        }
    }
}

/* PNG Image parser.
*/
static size_t is_png(void)
{
    const unsigned char *p;
    size_t               i = 8;
    size_t               n;

    if (peek(0, "\x89PNG\x0D\x0A\x1A\x0A", 8))
        return 0;

    while (1) {
        n = peekl(i);
        if (n > (size_t)0x7FFFFFFFUL || n < (size_t)0)
            return 0;
        peekb(i + n + (size_t)12);

        p = (const unsigned char *)(input_data + i + 4);

        if (!((p[0] >= 'A' && p[0] <= 'Z') || (p[0] >= 'a' && p[0] <= 'z')))
            return 0;
        if (!((p[1] >= 'A' && p[1] <= 'Z') || (p[1] >= 'a' && p[1] <= 'z')))
            return 0;
        if (!((p[2] >= 'A' && p[2] <= 'Z') || (p[2] >= 'a' && p[2] <= 'z')))
            return 0;
        if (!((p[3] >= 'A' && p[3] <= 'Z') || (p[3] >= 'a' && p[3] <= 'z')))
            return 0;
        
        i += n + (size_t)12;

        if (p[0] == 'I' && p[1] == 'E' && p[2] == 'N' && p[3] == 'D')
            return i;
    }

    return 0;
}

/* JPEG image parser.
*/
static size_t is_jpeg(void)
{
    size_t  n, index = 0;
    int     b;

    /* SOI (0xFF+ 0xD8) must be first in stream. */
    b = peekb(index);
    if (b != 0xFF)
        return 0;
    while (b == 0xFF)
        b = peekb(++index);
    if (b != 0xD8)
        return 0;
    index++;

    while (1) {

        /* Marker. */
        b = peekb(index);
        if (b != 0xFF)
            return 0;
        while (b == 0xFF)
            b = peekb(++index);

        /* Invalid? */
        if (b < 0xC0)
            return 0;

        if (b >= 0xD0 && b <= 0xD7) {
            /* RSTn? */
            index++;
            continue;
        } else
        if (b == 0xD8) {
            /* Start of Image? */
            index++;
            continue;
        } else
        if (b == 0xD9) {
            /* End of Image? */
            return index + 1;
        }

        /* Marker has length indicator. */
        n = peekw(++index);
        if (n < (size_t)2)
            return 0;
        index += n;

        /* Not Start of Scan? */
        if (b != 0xDA)
            continue;

        /* Parse scan. */
        do {
            b = peekb(index);
            while (b != 0xFF)
                if (++index <= input_size)
                    b = peekb(index);
                else
                    return 0;
            while (b == 0xFF)
                b = peekb(++index);
        } while (b <= 0x01 || (b >= 0xD0 && b <= 0xD7));
        index--;

        continue;
    }
}

/* Return the number of bytes that can be discarded. Conside
 *      discard(is_none());
 * a replacement for
 *      while (!is_gif() && !is_png() && !is_jpg() && input_have)
 *          discard(1);
*/
size_t is_none(void)
{
    size_t  n = 0;
    int     b;

    do {
        b = peekb(n);

        if (b == 'G')
            if (peekb(n+1) == 'I' &&
                peekb(n+2) == 'F' &&
                peekb(n+3) == '8' &&
                peekb(n+5) == 'a')
                return n;

        if (b == 0x89)
            if (peekb(n+1) == 'P' &&
                peekb(n+2) == 'N' &&
                peekb(n+3) == 'G')
                return n;

        if (b == 0xFF)
            if (peekb(n+1) == 0xD8)
                return n;

    } while (n++ <= input_have);

    return input_have;
}

/*
 * Main
*/

int main(int argc, char *argv[])
{
    size_t  len;
    int     retval;

    if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
        fatal(0, 5,
            "\n"
            "Usage: ", argv[0], " [ -h | --help ]\n"
            "       ", argv[0], " command [ args... ]\n"
            "\n"
            "The command will be run once for each image in standard input.\n"
            "The command will receive the image data from standard input.\n"
            "\n");

    command_file = (const char *)argv[1];
    command_args = (const char *const *)&(argv[1]);

    /* Paranoid check: Verify the arg list ends with a NULL. */
    if (argv[argc])
        argv[argc] = NULL;

    while (1)
        if ((len = is_png())  ||
            (len = is_gif())  ||
            (len = is_jpeg()) ) {

            /* Run the command. */
            retval = run(len);
            if (retval > 0)
                fatal(retval, 5, "'", command_file, "': Nonzero (", errint(retval), ") exit status.\n");
            if (retval < 0)
                fatal(EXIT_SIGNAL, 5, "'", command_file, "': Died by signal (", errint(-retval), ").\n");

            /* Discard image data. */
            if (!discard(len))
                return 0;

        } else {
            /* No data? */
            if (!input_have)
                return 0;

            /* Skip unrecognized input. */
            len = 0;
            while (input_have && !is_png() && !is_gif() && !is_jpeg()) {
                size_t const n = is_none();
                discard(n);
                len += n;
            }

            if (!input_have)
                fatal(0, 3, "Discarded ", errint(len), " trailing garbage bytes.\n");
 
            wrerrs("Skipped ");
            wrerrs(errint(len));
            wrerrs(" bytes of unrecognized input.\n");
            continue;
        }
}
Compile and install using
Code:
gcc per-image.c -Wall -O3 -s -o per-image
sudo install -m 0755 per-image /usr/local/bin/per-image
Run without parameters (or with just -h or --help) to see the usage. For example:
Code:
find / -name '*.jpg' -o -name '*.gif' -o -name '*.png' -exec cat '{}' ';' | per-image xli stdin
find / -name '*.jpg' -o -name '*.gif' -o -name '*.png' -exec cat '{}' ';' | per-image xview /dev/stdin
Hope you find this interesting,
 
1 members found this post helpful.
Old 01-24-2012, 04:36 AM   #9
H_TeXMeX_H
LQ Guru
 
Registered: Oct 2005
Location: $RANDOM
Distribution: slackware64
Posts: 12,928

Original Poster
Blog Entries: 2

Rep: Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301
Thanks, that is interesting code, I will try it.
 
Old 02-10-2012, 01:42 PM   #10
caminati
LQ Newbie
 
Registered: Feb 2012
Posts: 1

Rep: Reputation: Disabled
Even though you flagged this as solved:
  1. Did you try
    Code:
    feh /dev/stdin < img.png
    ?
  2. I recall that xloadimage does this too, although I have no way to try it now. Its development has ceased, however you can find sources and binpack on debian repositories.

After a quick check, I found no tempfiles around with feh. Even if this is the case, you would end up with the risk of huge memory allocations for heavy images.
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
Looking for a fast image viewer that doesn't try to resort my files? Cinematography Linux - Software 7 01-19-2008 05:31 PM
IDE drive reads slower with write cache disabled tothzp Linux - Hardware 0 11-29-2007 10:21 AM
Image Viewer Can't View Image Files. klownska Linux - Software 1 02-27-2006 02:26 AM
How to create a shell script that reads something from stdin? ricky_ds Programming 2 05-17-2005 05:06 AM
Best image viewer for very large image files? andvaranaut Linux - Software 1 02-21-2004 10:01 AM

LinuxQuestions.org > Forums > Linux Forums > Linux - Software

All times are GMT -5. The time now is 02:37 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration