            segment .data
            align   8
    infd    dq      0
    outfd   dq      0
    in_name dq      0
    out_name:
            dq      0
    in_size dq      0
    data    dq      0
            segment .text
            global  main
            extern  open, malloc, lseek, read, write, close, printf
    main:
            push    rbp
            mov     rbp, rsp

    ;       Check the command line parameter count
            cmp     rdi, 3
            jne     .param_error

    ;       Save the input and output file names
            mov     r8, [rsi+8]
            mov     r9, [rsi+16]
            mov     [in_name], r8
            mov     [out_name], r9

    ;       Report the files being copied
            segment .data
    .files_fmt:
            db      "copying %s to %s",0x0a,0
            segment .text
            lea     rdi, [.files_fmt]
            mov     rsi, [in_name]
            mov     rdx, [out_name]
            xor     eax, eax
            call    printf

    ;       Try to open the input file
            mov     rdi, [in_name]
            xor     esi, esi
            call    open
            cmp     rax, 0
            jl      .input_open_failed
            mov     [infd], rax

    ;       Try to open the output file
            mov     rdi, [out_name]
            xor     esi, esi
            call    open
            cmp     rax, 0
            jge     .output_exists

    ;       Try to create the output file
            mov     rdi, [out_name]
            mov     esi, 0x41
            mov     edx, 700o
            call    open
            cmp     rax, 0
            jl      .output_open_failed
            mov     [outfd], rax

    ;       Determine the input file size
            mov     rdi, [infd]
            xor     esi, esi
            mov     edx, 2
            call    lseek
            mov     [in_size], rax
            mov     rdi, [infd]
            xor     esi, esi
            mov     edx, 0
            call    lseek

    ;       Allocate the data array
            mov     rdi, [in_size]
            call    malloc
            cmp     rax, 0
            je      .malloc_failed
            mov     [data], rax

    ;       Read the input file
            mov     rdi, [infd]
            mov     rsi, [data]
            mov     rdx, [in_size]
            call    read

    ;       Write the output file
            mov     rdi, [outfd]
            mov     rsi, [data]
            mov     rdx, [in_size]
            call    write

    ;       Close the input and output files
            mov     rdi, [infd]
            call    close
            mov     rdi, [outfd]
            call    close

            xor     eax, eax
            leave
            ret

            segment .data
    .malloc_failed_fmt:
            db      "malloc failed for %ld bytes",0x0a,0
            segment .text
    .malloc_failed:
            lea     rdi, [.malloc_failed_fmt]
            mov     rsi, [in_size]
            xor     eax, eax
            call    printf
            mov     eax, 1
            leave
            ret

            segment .data
    .output_exist_fmt:
            db      "%s already exists",0x0a,0
            segment .text
    .output_exists:
            lea     rdi, [.output_exist_fmt]
            mov     rsi, [out_name]
            xor     eax, eax
            call    printf
            mov     eax, 1
            leave
            ret

            segment .data
    .output_open_fmt:
            db      "Could not open output_file %s",0x0a,0
            segment .text
    .output_open_failed:
            lea     rdi, [.output_open_fmt]
            mov     rsi, [out_name]
            xor     eax, eax
            call    printf
            mov     eax, 1
            leave
            ret

            segment .data
    .input_open_fmt:
            db      "Could not open input_file %s",0x0a,0
            segment .text
    .input_open_failed:
            lea     rdi, [.input_open_fmt]
            mov     rsi, [in_name]
            xor     eax, eax
            call    printf
            mov     eax, 1
            leave
            ret

            segment .data
    .param_fmt:
            db      "Usage: copy input_file output_file",0x0a,0
            segment .text
    .param_error:
            lea     rdi, [.param_fmt]
            xor     eax, eax
            call    printf
            mov     eax, 1
            leave
            ret