File Bundles

August 14, 2015

We begin at the end, with what we want the file bundle to look like. Here’s a bundle that contains three files:

#! /usr/bin/scheme --script
(define (do-file f)
  (define (write-line x) (display x) (newline))
  (with-output-to-file (car f) (lambda ()
    (for-each write-line (cdr f)))))
(for-each do-file '(
("file1"
"This is file one"
)
("file2"
"File two has an \"embedded quote\""
"and contains two lines."
)
("file3"
"Mary had a little lamb."
"It's fleece was white as snow."
"And everywhere that Mary went"
"The lamb was sure to go."
)
))

The first line marks the file as a shell script, and varies with each Scheme implementation. The do-file function writes a single file. Then the for-each loops over the remaining files, writing each. The files are stored in a list of lists, with the filename as the head of each list in the list of lists, followed by the lines of the file. Creating the bundle is just a matter of writing the header then processing the input files one-by-one:

(define (write-header)
  (for-each (lambda (x) (display x) (newline))
    '("#! /usr/bin/petite --script"
      "(define (do-file f)"
      "  (define (write-line x) (display x) (newline))"
      "  (with-output-to-file (car f) (lambda ()"
      "    (for-each write-line (cdr f)))))"
      "(for-each do-file '(")))

(define (do-file f)
  (display "(") (write f) (newline)
  (with-input-from-file f (lambda ()
    (do ((x (read-line) (read-line)))
        ((eof-object? x))
      (write x) (newline))))
  (display ")") (newline))

(define (bundle out-file . in-files)
  (with-output-to-file out-file (lambda ()
    (write-header)
    (for-each do-file in-files)
    (display "))") (newline))))

This gets confusing, because the input and output are intertwined, but if you keep the output firmly in mind you’ll eventually get it.

Our test is performed at the command prompt:

> cat >file1
This is file one
CTRL-D
> cat >file2
File two has an "embedded quote"
and contains two lines.
CTRL-D
> cat >file3
Mary had a little lamb.
It's fleece was white as snow.
And everywhere that Mary went
The lamb was sure to go.
CTRL-D
> scheme
(load "bundle.ss")
(bundle "testbundle" "file1" "file2" "file3")
(exit)
> cat testbundle
#! /usr/bin/scheme --script
(define (do-file f)
  (define (write-line x) (display x) (newline))
  (with-output-to-file (car f) (lambda ()
    (for-each write-line (cdr f)))))
(for-each do-file '(
("file1"
"This is file one"
)
("file2"
"File two has an \"embedded quote\""
"and contains two lines."
)
("file3"
"Mary had a little lamb."
"It's fleece was white as snow."
"And everywhere that Mary went"
"The lamb was sure to go."
)
))
> chmod +x testbundle
> rm file[123]
> ./testbundle
> cat file1
This is file one
> cat file2
File two has an "embedded quote"
and contains two lines.
> cat file3
Mary had a little lamb.
It's fleece was white as snow.
And everywhere that Mary went
The lamb was sure to go.

We used read-line from the Standard Prelude. You can run the program at http://ideone.com/Eyvq5A.

Pages: 1 2

2 Responses to “File Bundles”

  1. Rutger said

    Python is the new shell..

    This creates a self extracting py file:

    code = """\
    def write_file((filename, lines)):
    	out = open(filename, 'w')
    	for line in lines:
    		out.write(line)
    	out.close()
    
    files = []
    """
    
    def make_self_extracting_py(files):
    	out = open('self_extracter.py', 'w')
    	out.write(code)
    	for i,filename in enumerate(files):
    		content = open(filename).readlines()
    		out.write("files.append(('{0}', {1}))".format(filename, content))
    		out.write("\n")
    	out.write("map(write_file, files)")
    	out.close()
    
    make_self_extracting_py(['text1.TXT', 'text2.TXT'])
    

    content of text1.TXT:
    hello world
    bye world

    content of text2.TXT:
    test with ‘qoutes’ and all other “stuf” should get ‘””” “””””IUYE754^!^#!%$#!%#*^ escaped..

    result of above script is:

    def write_file((filename, lines)):
    	out = open(filename, 'w')
    	for line in lines:
    		out.write(line)
    	out.close()
    
    files = []
    files.append(('text1.TXT', ['hello world\n', 'bye world']))
    files.append(('text2.TXT', ['test with \'qoutes\' and all other "stuf" should get \'"\'\'\'\' """""IUYE754^!^#!%$#!%#*^   escaped..']))
    map(write_file, files)
    

    This can be run, result is original text files content again.

  2. mcmillhj said
    #!/usr/bin/env perl
    
    use strict;
    use warnings;
    
    sub make_shell_archive {
       my ($filenames) = @_;
    
       open my $outfile_fh, '>', 'extract_files.sh'
          or die "Unable to open 'extract_files.sh' for writing: $!";
       
       foreach my $filename ( @$filenames ) {
          my $file_contents = do {
             open my $infile_fh, '<:encoding(UTF-8)', $filename 
                or die "Unable to open file '$filename' for reading: $!";
             <$infile_fh>;
          };
    
          print {$outfile_fh} "echo -n '$file_contents' > $filename\n";
       }
       close $outfile_fh;
    
       return;
    }
    make_shell_archive(\@ARGV);
    

    Two sample files: file1 and file2

    file1 contents:
    the quick brown fox jumped over the lazy dog

    file2 contents:
    foo bar baz quux

    Usage:

    perl shell_archive.pl file1 file2
    [hunter@apollo: 08]$ cat extract_files.sh 
    echo -n 'the quick brown fox jumped over the lazy dog' > file1
    echo -n 'foo bar baz quux' > file2
    [hunter@apollo: 08]$ ls
    extract_files.sh  file1  file2  shell_archive.pl  three_hw_qs.sml
    [hunter@apollo: 08]$ rm file*
    [hunter@apollo: 08]$ chmod +x extract_files.sh 
    [hunter@apollo: 08]$ ls 
    extract_files.sh  shell_archive.pl  three_hw_qs.sml
    [hunter@apollo: 08]$ ./extract_files.sh 
    [hunter@apollo: 08]$ ls 
    extract_files.sh  file1  file2  shell_archive.pl  three_hw_qs.sml
    [hunter@apollo: 08]$ cat file1
    the quick brown fox jumped over the lazy dog
    [hunter@apollo: 08]$ cat file2
    foo bar baz quux
    

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: