Warning: this is an htmlized version!
The original is here, and
the conversion rules are here.
-- tinyftp.lua - standalone version
-- by Edrx - version: 2006-03-24
--  Usage:
-- tinyftp.lua host:path/                       lists the dir "path/" in "host"
-- tinyftp.lua host:path/fname                  "cat"s fname
-- tinyftp.lua hst1:pth1/fnam1 hst2:pth2/fnam2  copies fnam1 to fnam2

-- The trick: tinyftp's notion of a "filename" is quite flexible.
-- "Filenames" starting with "ftp://" are remote files, the other ones
-- are not; things like host:foo work because every "filename" is
-- expanded by noralize_fname before being used, and normalize_fname
-- expands some "host:foo" filenames to
-- "ftp://login:passwd@hostlongname/foo". A "~" or a "$VARNAME" at the
-- beginning of a "filename" is also expanded, and "-" is treated as
-- stdin/stdout.

-- I wrote this because tramp wasn't working on the w32 box that I had
-- to use at my real-worldish job. For the full story see
-- <http://angg.twu.net/w32.html>.

-- (find-luasocket20file "src/")
-- (find-luasocket20file "src/ftp.lua")
-- (find-luasocket20file "src/ftp.lua" "function override(t)")
-- (find-luasocket20file "src/compat-5.1r4/compat-5.1.lua" "LUA_PATH")
-- (find-luasocket20file "src/compat-5.1r4/compat-5.1.lua" "LUA_CPATH")
-- (find-es "lua5" "luasocket-on-linux")

LUA_PATH  = LUA_PATH  or os.getenv("LUA_PATH")
                      or os.getenv("HOME") .. "/.lua50/?.lua"
LUA_CPATH = LUA_CPATH or os.getenv("LUA_CPATH")
                      or os.getenv("HOME") .. "/.lua50/?.so"

load_socket = function ()
    if socket then return end
    require "compat-5.1"
    package.cpath = LUA_CPATH
    --
    require "socket"
    ftp = require("socket.ftp")
    url = require("socket.url")
    --
    -- binget = socket.protect(binget_)
    -- binput = socket.protect(binput_)
  end

-- (find-eevfile "eev-dev.el" "defun ee-expand")
ee_expand = function (path)
    path = string.gsub(path, "^~$", "$HOME/", 1)
    path = string.gsub(path, "^~/", "$HOME/", 1)
    path = string.gsub(path, "^%$(%w+)", os.getenv, 1)
    return path
  end

readfile = function (fname)
    local f = assert(io.open(fname, "rb"))
    local bigstr = f:read("*a")
    f:close()
    return bigstr
  end
writefile = function (fname, bigstr)
    local f = assert(io.open(fname, "w+b"))
    f:write(bigstr)
    f:close()
  end

-- Usage: str = trimlines(tounix(str))
-- The other order may not work.
--
tounix = function (str) return (string.gsub(str, "\r", "")) end
trimlines = function (str) return string.gsub(str, "[ \t]+\n", "\n") end

isdir = function (str) return fname_nondir(str) == "" end
isftp = function (str) return (string.find(str, "ftp://")) end

fname_nondir = function (str)
    local _, _, nondir = string.find(str, "([^/]*)$")
    return nondir
  end

-- You should preload a version with your expansions using LUA_INIT
--
normalize_fname = normalize_fname or function (str)
    str = ee_expand(str)
    str = gsub(str, "\\", "/")
    str = gsub(str, "^local:",
                    "ftp://edrx:[email protected]/")
    str = gsub(str, "^host:",
                    "ftp://login:password@hostname/")
    return str
  end

-- 2005-12-12
-- tinyftp and his lower buddies
-- These functions support three kinds of "filenames": "ftp://...",
-- "-" (for stdin/stdout), and "other" (local files).
-- All ftp transfers are done in binary mode.
-- For simplicity, connections are never reused.

tinyftpwrite = function (fname, stuff)
    fname = normalize_fname(fname)
    if fname == "-"     then io.write(stuff);      return "stdout"
    elseif isftp(fname) then ftp.put(fname..";type=i", stuff); return "ftp" 
    else writefile(fname, stuff)                   return "file"
    end
  end

tinyftpread = function (fname)
    fname = normalize_fname(fname)
    if fname == "-"     then return io.read("*a"), "stdout"
    elseif isftp(fname) then return assert(ftp.get(fname..";type=i")), "ftp"
    else                     return readfile(fname), "file"
    end
  end

tinyftpls = function (fname)
    fname = normalize_fname(fname)
    local t = {}
    local p = url.parse(fname)
    p.command = "list"
    p.sink = ltn12.sink.table(t)
    local r, e = ftp.get(p)
    t = tounix(table.concat(t))
    t = string.gsub(t, "(\nd[^\n]*)", "%1/")  -- add slashes to dirs
                -- bug: doesn't add a "/" to a dir on the first line
    return r and t, e
  end

-- (find-alides "edrx/")
-- (find-alides "src/sync_FOBA_bcorp/")

tinyftpcopy = function (arg1, arg2)
    arg1 = normalize_fname(arg1)
    arg2 = normalize_fname(arg2)
    if isdir(arg2) then
      args2 = arg2 .. fname_nondir(arg1)
    end
    local stuff, inputtype = tinyftpread(arg1)
    local outputtype = tinyftpwrite(arg2, stuff)
    return format("%s -> %d bytes -> %s", inputtype, strlen(stuff), outputtype)
  end

tinyftp = function (arg1, arg2)
    if (arg2 or "") == "" then	       -- if we only got one argument
      if isdir(normalize_fname(arg1))    -- if it is an ftp directory
      then print((tinyftpls(arg1)))     -- then list it
      else io.write(tinyftpread(arg1))     -- otherwise cat it
      end
    else        		       -- if we got two arguments
      if arg2 == "-"		         -- if the target is stdout
      then tinyftpcopy(arg1, arg2)	   -- then just cat the input
      else print(tinyftpcopy(arg1, arg2))  -- otherwise print "src -> nbytes -> tgt"
      end
    end
  end

load_socket()
tinyftp(arg[1], arg[2])

-- Missing: support for "file:///" urls