copy

copy

Robbert Haarman

2009-02-05


Introduction

This program copies a file. It shows how to open files and ensure that they are closed afterwards, and it shows haw to do binary I/O.


Ada

with Ada.Streams.Stream_IO;

procedure Copy is

   procedure Copy(From_File  : String;
                  To_File    : String) is

      use Ada.Streams.Stream_IO;
      package Streams renames Ada.Streams;
      use type Streams.Stream_Element_Offset;

      In_Stream  : File_Type;
      Out_Stream : File_Type;
      Buffer     : Streams.Stream_Element_Array(1 .. 4096);
      Bytes_Read : Streams.Stream_Element_Offset;

   begin
      Open(In_Stream, In_File, From_File);
      Create(Out_Stream, Out_File, To_File);

      loop
         Read(In_Stream, Buffer, Bytes_Read);
         exit when Bytes_Read = 0;
         Write(Out_Stream, Buffer, Count(Bytes_Read));
      end loop;

      Close(In_Stream);
      Close(Out_Stream);

   exception
      when others =>
         if(Is_Open(In_Stream)) then Close(In_Stream); end if;
         if(Is_Open(Out_Stream)) then Close(Out_Stream); end if;
         raise;
   end Copy;

begin
   Copy("copy.adb", "cp.adb");
end Copy;

The program contains 81 words.


C

The C version of the program is displayed below.

#include <stdio.h>

int copy(const char *source, const char *dest) {
	FILE *src, *dst;
	char buf[128];
	int n;

	src = fopen(source, "rb");
	if(!src) return -1;

	dst = fopen(dest, "wb");
	if(!dst) {
		fclose(src);
		return -1;
	}

	while(!feof(src)) {
		n = fread(buf, 1, sizeof(buf), src);
		if(ferror(src)) goto error;
		if(n > 0) {
			if(fwrite(buf, 1, n, dst) != n) goto error;
		}
	}

	fclose(dst);
	fclose(src);
	return 0;

error:
	fclose(dst);
	fclose(src);
	return -1;
}

int main(int argc, char **argv) {
	return copy("copy.c", "cp.c");
}

There are several interesting bits to note about this program. First, the note of const in the definition of copy, signifying that the function won't modify these arguments. Secondly, the way fopen is used: the first argument is the filename, the second the mode: r for reading, w for writing, and b for binary mode.

After every file operation, a check is made to see if an error occured. If so, any open files are closed, and -1 is returned. Since there are several points where both files would have to be closed, this code is put at the end of the function and branched to using goto, if necessary.

The program contains 79 words.


Common Lisp

An implementation in Common Lisp is shown below.

(defun copy (source dest)
  (with-open-file (src source)
     (with-open-file (dst dest :direction :output)
	(loop with buffer = (make-array '(4096))
	      with n = 0
	      do (setf n (read-sequence buffer src))
	         (if (= n 0) (return))
		 (write-sequence buffer dst :end n)))))

(copy "copy.lisp" "cp.lisp")

This program makes use of the with-open-file constructs, which opens a file and makes sure that it is closed when control leaves its body, whether through successful completion or because a condition is signalled.

The buffer for copying the data is created using make-array which is passed a list of sizes, one for every dimension of the array. In this case, the array has only one dimension, so there is only one value in the list.

This program also demonstrates the use of keyword arguments: :direction for the second with-open-file and :end in the call to write-sequence. Note that the value given for :direction is itself a keyword.

There are 41 words in the program.


OCaml

The program can be implemented in OCaml as follows:

let copy source dest =
	let src = open_in_bin source in
		try let dst = open_out_bin dest in
			let len = 4096 in
			let buf = String.create len in
			let more = ref true in
			try while !more do
				let n = input src buf 0 len in
					if n = 0 then more := false
					else output dst buf 0 n
			done with err ->
				close_out_noerr dst;
				raise err;
		close_out dst
		with err ->
			close_in_noerr src;
			raise err;
	close_in src

let _ = copy "copy.ml" "cp.ml"

The most obvious thing about this program is the use of exceptions: the construct

try expr with exn -> handler

evaluates expr and returns its result, unless an exception occurs: in that case, it matches the exception against the with clauses (there can be more than one). In the program above, both try blocks have a single with clause that matches any exception, closes the opened file, and raises the caught exception again.

Another thing to notice is the use of references. let more = ref true … makes more a reference to the boolean true. The reason to use references is that normal bindings are constants in OCaml. In this case, we want to set more to false when the copying is done, so we need something that can be modified. That's what references are for. more := false makes more refer to false. !more returns the value more currently refers to.

The program contains 87 words.


Ruby

For Ruby, we have the following program:

def copy source, dest
	File.open(source, 'rb') do |src|
		File.open(dest, 'wb') do |dst|
			data = src.read 4096
			break unless data
			dst.print data
		end
	end
end

copy 'copy.rb', 'cp.rb'

Again, we see the use of blocks. Calling File.open with a block opens the file and calls the block with the file handle as an argument. It ensures that the file is closed when the block exits (whether normally or through an exception). The 'rb' and 'wb' arguments are just like in C. src.read 4096 reads up to 4096 bytes from src, returning nil when the end has been reached.

This version contains 27 words.

Valid XHTML 1.1! Valid CSS! Viewable with Any Browser