(* * Builtin file operations. * * \begin{doc} * \section{IO functions} * \end{doc} *) include Omake_pos.Make (struct let name = "Omake_builtin_io" end) (* * Table of variables. * * \begin{doc} * \subsection{Standard channels} * * The following variables define the standard channels. * * \var{stdin} * * \begin{verbatim} * stdin : InChannel * \end{verbatim} * * The standard input channel, open for reading. * * \var{stdout} * \begin{verbatim} * stdout : OutChannel * \end{verbatim} * * The standard output channel, open for writing. * * \var{stderr} * \begin{verbatim} * stderr : OutChannel * \end{verbatim} * * The standard error channel, open for writing. * \end{doc} *) (* * \begin{doc} * \fun{open-in-string} * The \verb+open-in-string+ treats a string as if it were a file * and returns a channel for reading. * * \begin{verbatim} * $(open-in-string s) : Channel * s : String * \end{verbatim} * \end{doc} *) let open_in_string venv pos loc args = let pos = string_pos "open-in-string" pos in match args with [arg] -> let s = Omake_value.string_of_value venv pos arg in let fd = Lm_channel.of_string s in let chan = Omake_env.venv_add_channel venv fd in Omake_value_type.ValChannel (Lm_channel.InChannel, chan) | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityExact 1, List.length args))) (* * \begin{doc} * \twofuns{open-out-string}{out-contents} * The \verb+open-out-string+ creates a channel that writes to a * string instead of a file. The string may be retrieved with the * \verb+out-contents+ function. * * \begin{verbatim} * $(open-out-string) : Channel * $(out-contents chan) : String * chan : OutChannel * \end{verbatim} * \end{doc} *) let open_out_string venv pos loc args = let pos = string_pos "open-in-string" pos in match args with | [] -> let fd = Lm_channel.create_string () in let chan = Omake_env.venv_add_channel venv fd in Omake_value_type.ValChannel (Lm_channel.OutChannel, chan) | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityExact 0, List.length args))) let out_contents venv pos loc args = let pos = string_pos "out-contents" pos in match args with [fd] -> let outp = Omake_value.prim_channel_of_value venv pos fd in let outx = Omake_env.venv_find_channel venv pos outp in let s = Lm_channel.to_string outx in Omake_value_type.ValString s | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityExact 1, List.length args))) (* * Open a file. * * \begin{doc} * \fun{fopen} * * The \verb+fopen+ function opens a file for reading or writing. * * \begin{verbatim} * $(fopen file, mode) : Channel * file : File * mode : String * \end{verbatim} * * The \verb+file+ is the name of the file to be opened. * The \verb+mode+ is a combination of the following characters. * \begin{description} * \item[r] Open the file for reading; it is an error if the file does not exist. * \item[w] Open the file for writing; the file is created if it does not exist. * \item[a] Open the file in append mode; the file is created if it does not exist. * \item[+] Open the file for both reading and writing. * \item[t] Open the file in text mode (default). * \item[b] Open the file in binary mode. * \item[n] Open the file in nonblocking mode. * \item[x] Fail if the file already exists. * \end{description} * * Binary mode is not significant on Unix systems, where * text and binary modes are equivalent. * \end{doc} *) let read_mode = 1 let write_mode = 2 let create_mode = 4 let append_mode = 8 let binary_mode = 16 let text_mode = 32 let nonblock_mode = 64 let excl_mode = 128 let fopen_mode pos loc s = let len = String.length s in let rec collect mode i = if i = len then mode else let bit = match s.[i] with ' ' | '\t' -> mode | 'r' -> read_mode | 'w' -> write_mode lor create_mode | 'a' -> append_mode lor write_mode | '+' -> read_mode lor write_mode | 'b' -> binary_mode | 't' -> text_mode | 'n' -> nonblock_mode | 'x' -> excl_mode | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, StringStringError ("illegal file mode", s))) in let mode = mode lor bit in collect mode (succ i) in let mode = collect 0 0 in let () = if (mode land text_mode) <> 0 && (mode land binary_mode) <> 0 then raise (Omake_value_type.OmakeException (loc_pos loc pos, StringStringError ("can't specify both text and binary modes", s))) in let opt = if (mode land append_mode) <> 0 then [Unix.O_APPEND; Unix.O_CREAT] else if (mode land create_mode) <> 0 then [Unix.O_CREAT; Unix.O_TRUNC] else [] in let kind, opt = if (mode land read_mode) <> 0 && (mode land write_mode) <> 0 then Omake_value_type.InOutChannel, Unix.O_RDWR :: opt else if (mode land write_mode) <> 0 then OutChannel, Unix.O_WRONLY :: opt else InChannel, Unix.O_RDONLY :: opt in let opt = if (mode land excl_mode) <> 0 then Unix.O_EXCL :: opt else opt in let opt = if (mode land nonblock_mode) <> 0 then Unix.O_NONBLOCK :: opt else opt in kind, (mode land binary_mode) <> 0, opt let fopen venv pos loc args = let pos = string_pos "fopen" pos in match args with |[node; flags] -> let name = Omake_value.filename_of_value venv pos node in let kind, binary, flags = fopen_mode pos loc (Omake_value.string_of_value venv pos flags) in let fd = try Lm_unix_util.openfile name flags 0o666 with Unix.Unix_error _ as exn -> raise (Omake_value_type.UncaughtException (loc_pos loc pos, exn)) in let chan = Lm_channel.create name Lm_channel.FileChannel kind binary (Some fd) in Omake_value_type.ValChannel (kind, Omake_env.venv_add_channel venv chan) | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityExact 2, List.length args))) (* * Closing file descriptors. * * \begin{doc} * \fun{close} * * \begin{verbatim} * $(close channel...) * channel : Channel * \end{verbatim} * * The \verb+close+ function closes a file that was previously opened * with \verb+fopen+. * \end{doc} *) let close venv pos loc args = let pos = string_pos "close" pos in match args with |[arg] -> let args = Omake_value.values_of_value venv pos arg in List.iter (fun arg -> match Omake_value.eval_prim_value venv pos arg with | ValChannel (_, channel) -> Omake_env.venv_close_channel venv pos channel | arg -> raise (Omake_value_type.OmakeException (loc_pos loc pos, StringValueError ("not a file descriptor", arg)))) args; Omake_value_type.ValNone | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityExact 1, List.length args))) (* * \begin{doc} * \twofuns{read}{input-line} * * \begin{verbatim} * $(read channel, amount) : String * $(input-line channel) : String * channel : InChannel * amount : Int * raises RuntimeException * \end{verbatim} * * The \verb+read+ function reads up to \verb+amount+ * bytes from an input channel, and returns * the data that was read. The \verb+input-line+ function reads a line from the file and returns the line read, without * the line terminator. If an end-of-file condition is reached, both functions raise a \verb+RuntimeException+ * exception. * \end{doc} *) let read venv pos loc args = let pos = string_pos "read" pos in match args with [fd; amount] -> let fd = Omake_value.channel_of_value venv pos fd in let amount = Omake_value.int_of_value venv pos amount in let s = Bytes.make amount '\000' in let count = try Lm_channel.read fd s 0 amount with Sys_error _ | Invalid_argument _ as exn -> raise (Omake_value_type.UncaughtException (pos, exn)) in if count = amount then Omake_value_type.ValData (Bytes.to_string s) else if count = 0 then raise (Omake_value_type.UncaughtException (pos, End_of_file)) else ValData (Bytes.sub_string s 0 count) | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityExact 2, List.length args))) let input_line venv pos loc args = let pos = string_pos "input-line" pos in match args with |[fd] -> let fd = Omake_value.channel_of_value venv pos fd in let s = try Lm_channel.input_line fd with Sys_error _ | End_of_file | Invalid_argument _ as exn -> raise (Omake_value_type.UncaughtException (pos, exn)) in Omake_value_type.ValData s | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityExact 1, List.length args))) (* * \begin{doc} * \fun{write} * * \begin{verbatim} * $(write channel, buffer, offset, amount) : String * channel : OutChannel * buffer : String * offset : Int * amount : Int * $(write channel, buffer) : String * channel : OutChannel * buffer : String * raises RuntimeException * \end{verbatim} * * In the 4-argument form, the \verb+write+ function writes * bytes to the output channel \verb+channel+ from the \verb+buffer+, * starting at position \verb+offset+. Up to \verb+amount+ bytes * are written. The function returns the number of bytes that were * written. * * The 3-argument form is similar, but the \verb+offset+ is 0. * * In the 2-argument form, the \verb+offset+ is 0, and the \verb+amount+ * if the length of the \verb+buffer+. * * If an end-of-file condition is reached, * the function raises a \verb+RuntimeException+ exception. * \end{doc} *) let write venv pos loc args = let pos = string_pos "read" pos in let fd, buf, off, len = match args with | [fd; buf] -> fd, buf, None, None | [fd; buf; len] -> fd, buf, None, Some len | [fd; buf; off; len] -> fd, buf, Some off, Some len | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityRange (2, 4), List.length args))) in let fd = Omake_value.channel_of_value venv pos fd in let buf = Omake_value.string_of_value venv pos buf in let off = match off with Some off -> Omake_value.int_of_value venv pos off | None -> 0 in let len = match len with Some len -> Omake_value.int_of_value venv pos len | None -> String.length buf in let count = try Lm_channel.write fd (Bytes.of_string buf) off len with Sys_error _ | Invalid_argument _ as exn -> raise (Omake_value_type.UncaughtException (pos, exn)) in Omake_value_type.ValInt count (* * \begin{doc} * \fun{lseek} * * \begin{verbatim} * $(lseek channel, offset, whence) : Int * channel : Channel * offset : Int * whence : String * raises RuntimeException * \end{verbatim} * * The \verb+lseek+ function repositions the offset of the * channel \verb+channel+ according to the \verb+whence+ directive, as * follows: * * \begin{description} * \item[SEEK\_SET] The offset is set to \verb+offset+. * \item[SEEK\_CUR] The offset is set to its current position plus \verb+offset+ bytes. * \item[SEEK\_END] The offset is set to the size of the file plus \verb+offset+ bytes. * \end{description} * * The \verb+lseek+ function returns the new position in the file. * \end{doc} *) let lseek venv pos loc args = let pos = string_pos "lseek" pos in match args with | [fd; off; whence] -> let fd = Omake_value.channel_of_value venv pos fd in let off = Omake_value.int_of_value venv pos off in let whence = match String.uppercase_ascii (Omake_value.string_of_value venv pos whence) with "SET" | "SEEK_SET" -> Unix.SEEK_SET | "CUR" | "CURRENT" | "SEEK_CUR" -> Unix.SEEK_CUR | "END" | "SEEK_END" -> Unix.SEEK_END | whence -> raise (Omake_value_type.OmakeException (loc_pos loc pos, StringStringError ("illegal lseek parameter", whence))) in let off = try Lm_channel.seek fd off whence with Unix.Unix_error _ as exn -> raise (Omake_value_type.UncaughtException (pos, exn)) in Omake_value_type.ValInt off | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityExact 3, List.length args))) (* * \begin{doc} * \fun{rewind} * * \begin{verbatim} * rewind(channel...) * channel : Channel * \end{verbatim} * * The \verb+rewind+ function set the current file position to the * beginning of the file. * \end{doc} *) let rewind venv pos loc args = let pos = string_pos "rewind" pos in match args with | [arg] -> let args = Omake_value.values_of_value venv pos arg in let () = try List.iter (fun arg -> let fd = Omake_value.channel_of_value venv pos arg in ignore (Lm_channel.seek fd 0 Unix.SEEK_SET)) args with Unix.Unix_error _ as exn -> raise (Omake_value_type.UncaughtException (pos, exn)) in Omake_value_type.ValNone | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityExact 1, List.length args))) (* * \begin{doc} * \fun{tell} * * \begin{verbatim} * $(tell channel...) : Int... * channel : Channel * raises RuntimeException * \end{verbatim} * * The \verb+tell+ function returns the current position of the \verb+channel+. * \end{doc} *) let tell venv pos loc args = let pos = string_pos "tell" pos in match args with | [arg] -> let args = Omake_value.values_of_value venv pos arg in let args = try List.map (fun arg -> let fd = Omake_value.channel_of_value venv pos arg in Omake_value_type.ValInt (Lm_channel.tell fd)) args with Unix.Unix_error _ as exn -> raise (Omake_value_type.UncaughtException (pos, exn)) in Omake_value.concat_array args | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityExact 1, List.length args))) (* * Flush an output channel. * * \begin{doc} * \fun{flush} * * \begin{verbatim} * $(flush channel...) * channel : OutChannel * \end{verbatim} * * The \verb+flush+ function can be used only on files that are open for writing. * It flushes all pending data to the file. * \end{doc} *) let flush venv pos loc args = let pos = string_pos "flush" pos in match args with | [arg] -> let args = Omake_value.values_of_value venv pos arg in List.iter (fun s -> let fd = Omake_value.channel_of_value venv pos s in Lm_channel.flush fd) args; Omake_value_type.ValNone | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityExact 1, List.length args))) (* * \begin{doc} * \fun{channel-name} * * \begin{verbatim} * $(channel-name channel...) : String * channel : Channel * \end{verbatim} * * The \verb+channel-name+ function returns the name that is associated with the channel. * \end{doc} *) let channel_name venv pos loc args = let pos = string_pos "channel-name" pos in match args with | [arg] -> let fd = Omake_value.channel_of_value venv pos arg in Omake_value_type.ValData (Lm_channel.name fd) | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityExact 1, List.length args))) (* * \begin{doc} * \fun{dup} * * \begin{verbatim} * $(dup channel) : Channel * channel : Channel * raises RuntimeException * \end{verbatim} * * The \verb+dup+ function returns a new channel referencing the * same file as the argument. * \end{doc} *) let dup venv pos loc args = let pos = string_pos "dup" pos in match args with | [arg] -> let channel = Omake_value.channel_of_value venv pos arg in let name = Lm_channel.name channel in let fd = Lm_channel.descr channel in let _, kind, mode, binary = Lm_channel.info channel in let fd = try Unix.dup fd with Unix.Unix_error _ as exn -> raise (Omake_value_type.UncaughtException (pos, exn)) in let chan = Lm_channel.create name kind mode binary (Some fd) in let channel = Omake_env.venv_add_channel venv chan in Omake_value_type.ValChannel (mode, channel) | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityExact 1, List.length args))) (* * \begin{doc} * \fun{dup2} * * \begin{verbatim} * dup2(channel1, channel2) * channel1 : Channel * channel2 : Channel * raises RuntimeException * \end{verbatim} * * The \verb+dup2+ function causes \verb+channel2+ to refer to the same * file as \verb+channel1+. * \end{doc} *) let dup2 venv pos loc args = let pos = string_pos "dup2" pos in match args with | [arg1; arg2] -> let channel1 = Omake_value.channel_of_value venv pos arg1 in let channel2 = Omake_value.channel_of_value venv pos arg2 in let fd1 = Lm_channel.descr channel1 in let fd2 = Lm_channel.descr channel2 in let () = try Unix.dup2 fd1 fd2 with Unix.Unix_error _ as exn -> raise (Omake_value_type.UncaughtException (pos, exn)) in Omake_value_type.ValNone | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityExact 2, List.length args))) (* * \begin{doc} * \fun{set-nonblock} * * \begin{verbatim} * set-nonblock-mode(mode, channel...) * channel : Channel * mode : String * \end{verbatim} * * The \verb+set-nonblock-mode+ function sets the nonblocking flag on the * given channel. When IO is performed on the channel, and the operation * cannot be completed immediately, the operations raises a \verb+RuntimeException+. * \end{doc} *) let set_nonblock_mode venv pos loc args = let pos = string_pos "set_nonblock_mode" pos in match args with [mode; channel] -> let set_mode = if Omake_value.bool_of_value venv pos mode then Unix.set_nonblock else Unix.clear_nonblock in let channels = Omake_value.values_of_value venv pos channel in let () = try List.iter (fun channel -> let channel = Omake_value.channel_of_value venv pos channel in let fd = Lm_channel.descr channel in set_mode fd) channels with Unix.Unix_error _ as exn -> raise (Omake_value_type.UncaughtException (pos, exn)) in Omake_value_type.ValNone | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityExact 2, List.length args))) (* * \begin{doc} * \fun{set-close-on-exec-mode} * * \begin{verbatim} * set-close-on-exec-mode(mode, channel...) * channel : Channel * mode : String * raises RuntimeException * \end{verbatim} * * The \verb+set-close-on-exec-mode+ function sets the close-on-exec * flags for the given channels. If the close-on-exec flag is set, the channel * is not inherited by child processes. Otherwise it is. * \end{doc} *) let set_close_on_exec_mode venv pos loc args = let pos = string_pos "set-close-on-exec-mode" pos in match args with | [mode; channel] -> let set_mode = if Omake_value.bool_of_value venv pos mode then Unix.set_close_on_exec else Unix.clear_close_on_exec in let channels = Omake_value.values_of_value venv pos channel in let () = try List.iter (fun channel -> let channel = Omake_value.channel_of_value venv pos channel in let fd = Lm_channel.descr channel in set_mode fd) channels with Unix.Unix_error _ as exn -> raise (Omake_value_type.UncaughtException (pos, exn)) in Omake_value_type.ValNone | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityExact 2, List.length args))) (* * \begin{doc} * \fun{pipe} * * \begin{verbatim} * $(pipe) : Pipe * raises RuntimeException * \end{verbatim} * * The \verb+pipe+ function creates a \verb+Pipe+ object, which has two * fields. The \verb+read+ field is a channel that is opened for * reading, and the \verb+write+ field is a channel that is opened * for writing. * \end{doc} *) let pipe venv pos loc args = let pos = string_pos "pipe" pos in match args with [] -> let fd_read, fd_write = try Unix.pipe () with Unix.Unix_error _ as exn -> raise (Omake_value_type.UncaughtException (pos, exn)) in let read = Lm_channel.create "<readpipe>" Lm_channel.PipeChannel InChannel false (Some fd_read) in let write = Lm_channel.create "<writepipe>" Lm_channel.PipeChannel OutChannel false (Some fd_write) in let fd_read = Omake_value_type.ValChannel (InChannel, Omake_env.venv_add_channel venv read) in let fd_write = Omake_value_type.ValChannel (OutChannel, Omake_env.venv_add_channel venv write) in let obj = Omake_env.venv_find_object_or_empty venv Omake_var.pipe_object_var in let obj = Omake_env.venv_add_field_internal obj Omake_symbol.read_sym fd_read in let obj = Omake_env.venv_add_field_internal obj Omake_symbol.write_sym fd_write in Omake_value_type.ValObject obj | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityExact 0, List.length args))) (* * \begin{doc} * \fun{mkfifo} * * \begin{verbatim} * mkfifo(mode, node...) * mode : Int * node : Node * \end{verbatim} * * The \verb+mkfifo+ function creates a named pipe. * \end{doc} *) let mkfifo venv pos loc args = let pos = string_pos "mkfifo" pos in match args with | [mode; nodes] -> let mode = Omake_value.int_of_value venv pos mode in let nodes = Omake_value.values_of_value venv pos nodes in let () = try List.iter (fun node -> Unix.mkfifo (Omake_value.filename_of_value venv pos node) mode) nodes with Unix.Unix_error _ as exn -> raise (Omake_value_type.UncaughtException (pos, exn)) in Omake_value_type.ValNone | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityExact 2, List.length args))) (* * \begin{doc} * \fun{select} * * \begin{verbatim} * $(select rfd..., wfd..., wfd..., timeout) : Select * rfd : InChannel * wfd : OutChannel * efd : Channel * timeout : float * raises RuntimeException * \end{verbatim} * * The \verb+select+ function polls for possible IO on a set of channels. * The \verb+rfd+ are a sequence of channels for reading, \verb+wfd+ are a * sequence of channels for writing, and \verb+efd+ are a sequence of * channels to poll for error conditions. The \verb+timeout+ specifies * the maximum amount of time to wait for events. * * On successful return, \verb+select+ returns a \verb+Select+ object, * which has the following fields: * \begin{description} * \item[read] An array of channels available for reading. * \item[write] An array of channels available for writing. * \item[error] An array of channels on which an error has occurred. * \end{description} * \end{doc} *) let select venv pos loc args = let pos = string_pos "select" pos in match args with | [rfd; wfd; efd; timeout] -> let rfd = Omake_value.values_of_value venv pos rfd in let wfd = Omake_value.values_of_value venv pos wfd in let efd = Omake_value.values_of_value venv pos efd in let rfd = List.map (Omake_value.channel_of_value venv pos) rfd in let wfd = List.map (Omake_value.channel_of_value venv pos) wfd in let efd = List.map (Omake_value.channel_of_value venv pos) efd in let timeout = Omake_value.float_of_value venv pos timeout in let rfd, wfd, efd = try Lm_channel.select rfd wfd efd timeout with Unix.Unix_error _ as exn -> raise (Omake_value_type.UncaughtException (pos, exn)) in let reintern_channel fdl = List.map (fun fd -> let fd = Omake_env.venv_find_channel_by_channel venv pos fd in let channel = Omake_env.venv_find_channel venv pos fd in let _, _, mode, _ = Lm_channel.info channel in Omake_value_type.ValChannel (mode, fd)) fdl in let rfd = reintern_channel rfd in let wfd = reintern_channel wfd in let efd = reintern_channel efd in let obj = Omake_env.venv_find_object_or_empty venv Omake_var.select_object_var in let obj = Omake_env.venv_add_field_internal obj Omake_symbol.read_sym (ValArray rfd) in let obj = Omake_env.venv_add_field_internal obj Omake_symbol.write_sym (ValArray wfd) in let obj = Omake_env.venv_add_field_internal obj Omake_symbol.error_sym (ValArray efd) in Omake_value_type.ValObject obj | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityExact 4, List.length args))) (* * \begin{doc} * \fun{lockf} * * \begin{verbatim} * lockf(channel, command, len) * channel : Channel * command : String * len : Int * raises RuntimeException * \end{verbatim} * * The \verb+lockf+ function places a lock on a region of the channel. * The region starts at the current position and extends for \verb+len+ * bytes. * * The possible values for \verb+command+ are the following. * \begin{description} * \item[F\_ULOCK] Unlock a region. * \item[F\_LOCK] Lock a region for writing; block if already locked. * \item[F\_TLOCK] Lock a region for writing; fail if already locked. * \item[F\_TEST] Test a region for other locks. * \item[F\_RLOCK] Lock a region for reading; block if already locked. * \item[F\_TRLOCK] Lock a region for reading; fail is already locked. * \end{description} * \end{doc} *) let lockf venv pos loc args = let pos = string_pos "lockf" pos in match args with [channel; command; len] -> let channel = Omake_value.channel_of_value venv pos channel in let command = Omake_value.string_of_value venv pos command in let len = Omake_value.int_of_value venv pos len in let command = match command with | "F_ULOCK" -> Unix.F_ULOCK | "F_LOCK" -> Unix.F_LOCK | "F_TLOCK" -> Unix.F_TLOCK | "F_TEST" -> Unix.F_TEST | "F_RLOCK" -> Unix.F_RLOCK | "F_TRLOCK" -> Unix.F_TRLOCK | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, StringStringError ("lockf: illegal command", command))) in let fd = Lm_channel.descr channel in let () = try Unix.lockf fd command len with Unix.Unix_error _ as exn -> raise (Omake_value_type.UncaughtException (pos, exn)) in Omake_value_type.ValNone | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityExact 3, List.length args))) (************************************************************************ * Databases. *) let addr_of_value venv pos arg = let host = Omake_value.string_of_value venv pos arg in try Unix.inet_addr_of_string host with Failure _ -> let entry = Unix.gethostbyname host in entry.Unix.h_addr_list.(0) let proto_of_value venv pos arg = let proto = Omake_value.string_of_value venv pos arg in try Unix.getprotobynumber (int_of_string proto) with Failure _ -> Unix.getprotobyname proto (* (* * \begin{doc} * \obj{InetAddr} * * The \verb+InetAddr+ object describes an Internet address. * It contains the following fields. * * \begin{description} * \item[addr] \verb+String+: the Internet address. * \item[port] \verb+Int+: the port number. * \end{description} * * \obj{Host} * * A \verb+Host+ object contains the following fields. * * \begin{description} * \item[name] \verb+String+: the name of the host. * \item[aliases] \verb+String Array+: other names by which the host is known. * \item[addrtype] \verb+String+: the preferred socket domain. * \item[addrs] \verb+InetAddr Array+: an array of Internet addresses belonging to the host. * \end{description} * * \fun{gethostbyname} * * \begin{verbatim} * $(gethostbyname host...) : Host... * host : String * raises RuntimeException * \end{verbatim} * * The \verb+gethostbyname+ function returns a \verb+Host+ object * for the specified host. The \verb+host+ may specify a domain name * or an Internet address. * * \end{doc} *) let gethostbyname venv pos loc args = let pos = string_pos "gethostbyname" pos in match args with [arg] -> let args = Omake_value.values_of_value venv pos arg in let args = try List.map (fun arg -> let host = Omake_value.string_of_value venv pos arg in let entry = try let addr = Unix.inet_addr_of_string host in Unix.gethostbyaddr addr with Failure _ -> Unix.gethostbyname host in make_host_entry venv pos entry) args with Not_found | Unix.Unix_error _ as exn -> raise (Omake_value_type.UncaughtException (pos, exn)) in Omake_value.concat_array args | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityExact 1, List.length args))) (* * \begin{doc} * \obj{Protocol} * * The \verb+Protocol+ object represents a protocol entry. * It has the following fields. * * \begin{description} * \item[name] \verb+String+: the canonical name of the protocol. * \item[aliases] \verb+String Array+: aliases for the protocol. * \item[proto] \verb+Int+: the protocol number. * \end{description} * * \fun{getprotobyname} * * \begin{verbatim} * $(getprotobyname name...) : Protocol... * name : Int or String * raises RuntimeException * \end{verbatim} * * The \verb+getprotobyname+ function returns a \verb+Protocol+ object for the * specified protocol. The \verb+name+ may be a protocol name, or a * protocol number. * \end{doc} *) let getprotobyname venv pos loc args = let pos = string_pos "getprotobyname" pos in match args with [arg] -> let args = Omake_value.values_of_value venv pos arg in let args = try List.map (fun arg -> let proto = Omake_value.string_of_value venv pos arg in let entry = try Unix.getprotobynumber (int_of_string proto) with Failure _ -> Unix.getprotobyname proto in make_proto_entry venv pos entry) args with Not_found | Unix.Unix_error _ as exn -> raise (Omake_value_type.UncaughtException (pos, exn)) in Omake_value.concat_array args | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityExact 1, List.length args))) (* * \begin{doc} * \obj{Service} * * The \verb+Service+ object represents a network service. * It has the following fields. * * \begin{description} * \item[name] \verb+String+: the name of the service. * \item[aliases] \verb+String Array+: aliases for the service. * \item[port] \verb+Int+: the port number of the service. * \item[proto] \verb+Protocol+: the protocol for the service. * \end{description} * * \fun{getservbyname} * * \begin{verbatim} * $(getservbyname service...) : Service... * service : String or Int * raises RuntimeException * \end{verbatim} * * The \verb+getservbyname+ function gets the information for a network service. * The \verb+service+ may be specified as a service name or number. * \end{doc} *) let getprotobyname venv pos loc args = let pos = string_pos "getprotobyname" pos in match args with [arg] -> let args = Omake_value.values_of_value venv pos arg in let args = try List.map (fun arg -> let proto = Omake_value.string_of_value venv pos arg in let entry = try Unix.getservbyprt (int_of_string proto) with Failure _ -> Unix.getservbyname proto in make_serv_entry venv pos entry) args with Not_found | Unix.Unix_error _ as exn -> raise (Omake_value_type.UncaughtException (pos, exn)) in Omake_value.concat_array args | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityExact 1, List.length args))) *) (* * \begin{doc} * \fun{socket} * * \begin{verbatim} * $(socket domain, type, protocol) : Channel * domain : String * type : String * protocol : String * raises RuntimeException * \end{verbatim} * * The \verb+socket+ function creates an unbound socket. * * The possible values for the arguments are as follows. * * The \verb+domain+ may have the following values. * \begin{description} * \item[PF\_UNIX or unix] Unix domain, available only on Unix systems. * \item[PF\_INET or inet] Internet domain, IPv4. * \item[PF\_INET6 or inet6] Internet domain, IPv6. * \end{description} * * The \verb+type+ may have the following values. * \begin{description} * \item[SOCK\_STREAM or stream] Stream socket. * \item[SOCK\_DGRAM or dgram] Datagram socket. * \item[SOCK\_RAW or raw] Raw socket. * \item[SOCK\_SEQPACKET or seqpacket] Sequenced packets socket * \end{description} * * The \verb+protocol+ is an \verb+Int+ or \verb+String+ that specifies * a protocol in the protocols database. * \end{doc} *) let socket venv pos loc args = let pos = string_pos "socket" pos in match args with | [domain; ty; proto] -> let domain = match String.uppercase_ascii (Omake_value.string_of_value venv pos domain) with "PF_UNIX" | "UNIX" -> Unix.PF_UNIX | "PF_INET" | "INET" | "IP" -> Unix.PF_INET (* If you are compiling with OCaml-3.07 or earlier, comment out these lines *) | "PF_INET6" | "INET6" | "IP6" -> Unix.PF_INET6 | domain -> raise (Omake_value_type.OmakeException (loc_pos loc pos, StringStringError ("bad domain", domain))) in let ty = match String.uppercase_ascii (Omake_value.string_of_value venv pos ty) with "SOCK_STREAM" | "STREAM" -> Unix.SOCK_STREAM | "SOCK_DGRAM" | "DGRAM" -> Unix.SOCK_DGRAM | "SOCK_RAW" | "RAW" -> Unix.SOCK_RAW | "SOCK_SEQPACKET" | "SEQPACKET" -> Unix.SOCK_SEQPACKET | ty -> raise (Omake_value_type.OmakeException (loc_pos loc pos, StringStringError ("bad type", ty))) in let proto = proto_of_value venv pos proto in let socket = try Unix.socket domain ty proto.Unix.p_proto with Unix.Unix_error _ as exn -> raise (Omake_value_type.UncaughtException (pos, exn)) in let channel = Lm_channel.create "<socket>" Lm_channel.SocketChannel Lm_channel.InOutChannel false (Some socket) in let channel = Omake_env.venv_add_channel venv channel in Omake_value_type.ValChannel (InOutChannel, channel) | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityExact 3, List.length args))) (* * \begin{doc} * \fun{bind} * * \begin{verbatim} * bind(socket, host, port) * socket : InOutChannel * host : String * port : Int * bind(socket, file) * socket : InOutChannel * file : File * raise RuntimeException * \end{verbatim} * * The \verb+bind+ function binds a socket to an address. * * The 3-argument form specifies an Internet connection, the \verb+host+ specifies a host name * or IP address, and the \verb+port+ is a port number. * * The 2-argument form is for \verb+Unix+ sockets. The \verb+file+ specifies the filename * for the address. * \end{doc} *) let bind venv pos loc args = let pos = string_pos "bind" pos in let socket, addr = match args with [socket; host; port] -> let host = addr_of_value venv pos host in let port = Omake_value.int_of_value venv pos port in let addr = Unix.ADDR_INET (host, port) in socket, addr | [socket; name] -> let name = Omake_value.filename_of_value venv pos name in let addr = Unix.ADDR_UNIX name in socket, addr | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityRange (2, 3), List.length args))) in let socket = Omake_value.channel_of_value venv pos socket in let socket = Lm_channel.descr socket in let () = try Unix.bind socket addr with Unix.Unix_error _ as exn -> raise (Omake_value_type.UncaughtException (pos, exn)) in Omake_value_type.ValNone (* * \begin{doc} * \fun{listen} * * \begin{verbatim} * listen(socket, requests) * socket : InOutChannel * requests : Int * raises RuntimeException * \end{verbatim} * * The \verb+listen+ function sets up the socket for receiving up to \verb+requests+ number * of pending connection requests. * \end{doc} *) let listen venv pos loc args = let pos = string_pos "listen" pos in match args with [socket; requests] -> let socket = Omake_value.channel_of_value venv pos socket in let socket = Lm_channel.descr socket in let requests = Omake_value.int_of_value venv pos requests in let () = try Unix.listen socket requests with Unix.Unix_error _ as exn -> raise (Omake_value_type.UncaughtException (pos, exn)) in Omake_value_type.ValNone | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityExact 2, List.length args))) (* * \begin{doc} * \fun{accept} * * \begin{verbatim} * $(accept socket) : InOutChannel * socket : InOutChannel * raises RuntimeException * \end{verbatim} * * The \verb+accept+ function accepts a connection on a socket. * \end{doc} *) let accept venv pos loc args = let pos = string_pos "accept" pos in match args with | [socket] -> let socket = Omake_value.channel_of_value venv pos socket in let socket = Lm_channel.descr socket in let socket, _ = try Unix.accept socket with Unix.Unix_error _ as exn -> raise (Omake_value_type.UncaughtException (pos, exn)) in let channel = Lm_channel.create "<socket>" Lm_channel.SocketChannel Lm_channel.InOutChannel false (Some socket) in let channel = Omake_env.venv_add_channel venv channel in Omake_value_type.ValChannel (InOutChannel, channel) | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityExact 1, List.length args))) (* * \begin{doc} * \fun{connect} * * \begin{verbatim} * connect(socket, addr, port) * socket : InOutChannel * addr : String * port : int * connect(socket, name) * socket : InOutChannel * name : File * raise RuntimeException * \end{verbatim} * * The \verb+connect+ function connects a socket to a remote address. * * The 3-argument form specifies an Internet connection. * The \verb+addr+ argument is the Internet address of the remote host, * specified as a domain name or IP address. The \verb+port+ argument * is the port number. * * The 2-argument form is for Unix sockets. The \verb+name+ argument * is the filename of the socket. * \end{doc} *) let connect venv pos loc args = let pos = string_pos "connect" pos in let socket, addr = match args with [socket; host; port] -> let host = addr_of_value venv pos host in let port = Omake_value.int_of_value venv pos port in let addr = Unix.ADDR_INET (host, port) in socket, addr | [socket; name] -> let name = Omake_value.filename_of_value venv pos name in let addr = Unix.ADDR_UNIX name in socket, addr | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityRange (2, 3), List.length args))) in let socket = Omake_value.channel_of_value venv pos socket in let socket = Lm_channel.descr socket in let () = try Unix.connect socket addr with Unix.Unix_error _ as exn -> raise (Omake_value_type.UncaughtException (pos, exn)) in Omake_value_type.ValNone (************************************************************************ * Buffered IO. *) (* * Get the next character. * * \begin{doc} * \fun{getchar} * * \begin{verbatim} * $(getc) : String * $(getc file) : String * file : InChannel or File * raises RuntimeException * \end{verbatim} * * The \verb+getc+ function returns the next character of a file. * If the argument is not specified, \verb+stdin+ is used as input. * If the end of file has been reached, the function returns \verb+false+. * \end{doc} *) let getc venv pos loc args = let pos = string_pos "getc" pos in let arg = match args with |[] -> Omake_env.venv_find_var venv pos loc Omake_var.stdin_var | [arg] -> arg | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityRange (0, 1), List.length args))) in let inp, close_flag = Omake_value.in_channel_of_any_value venv pos arg in let inx = Omake_env.venv_find_channel venv pos inp in let s = try String.make 1 (Lm_channel.input_char inx) with End_of_file -> "false" in if close_flag then Omake_env.venv_close_channel venv pos inp; Omake_value_type.ValData s (* * Get the next line. * * \begin{doc} * \fun{gets} * * \begin{verbatim} * $(gets) : String * $(gets channel) : String * channel : InChannel or File * raises RuntimeException * \end{verbatim} * * The \verb+gets+ function returns the next line from a file. * The function returns the empty string if the end of file has been reached. * The line terminator is removed. * \end{doc} *) let gets venv pos loc args = let pos = string_pos "gets" pos in let arg = match args with |[] -> Omake_env.venv_find_var venv pos loc Omake_var.stdin_var | [arg] -> arg | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityRange (0, 1), List.length args))) in let inp, close_flag = Omake_value.in_channel_of_any_value venv pos arg in let inx = Omake_env.venv_find_channel venv pos inp in let s = try Lm_channel.input_line inx with End_of_file -> "" in if close_flag then Omake_env.venv_close_channel venv pos inp; Omake_value_type.ValString s (* * Get the next line. * * \begin{doc} * \fun{fgets} * * \begin{verbatim} * $(fgets) : String * $(fgets channel) : String * channel : InChannel or File * raises RuntimeException * \end{verbatim} * * The \verb+fgets+ function returns the next line from a file that has been * opened for reading with \verb+fopen+. The function returns the empty string * if the end of file has been reached. The returned string is returned as * literal data. The line terminator is not removed. * \end{doc} *) let fgets venv pos loc args = let pos = string_pos "fgets" pos in let arg = match args with [] -> Omake_env.venv_find_var venv pos loc Omake_var.stdin_var | [arg] -> arg | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityRange (0, 1), List.length args))) in let inp, close_flag = Omake_value.in_channel_of_any_value venv pos arg in let inx = Omake_env.venv_find_channel venv pos inp in let s = try Lm_channel.input_entire_line inx with End_of_file -> "" in if close_flag then Omake_env.venv_close_channel venv pos inp; Omake_value_type.ValData s (* * \begin{doc} * \section{Printing functions} * \funref{fprint} * \funref{print} * \funref{eprint} * \funref{fprintln} * \funref{println} * \funref{eprintln} * * Output is printed with the \verb+print+ and \verb+println+ functions. * The \verb+println+ function adds a terminating newline to the value being * printed, the \verb+print+ function does not. * * \begin{verbatim} * fprint(<file>, <string>) * print(<string>) * eprint(<string>) * fprintln(<file>, <string>) * println(<string>) * eprintln(<string>) * \end{verbatim} * * The \verb+fprint+ functions print to a file that has been previously opened with * \verb+fopen+. The \verb+print+ functions print to the standard output channel, and * the \verb+eprint+ functions print to the standard error channel. * \end{doc} *) let print_aux venv pos loc nl args = match args with | [fd; s] -> let outp, close_flag = Omake_value.out_channel_of_any_value venv pos fd in let outx = Omake_env.venv_find_channel venv pos outp in let s = Omake_value.string_of_value venv pos s in Lm_channel.output_string outx s; Lm_channel.output_string outx nl; Lm_channel.flush outx; if close_flag then Omake_env.venv_close_channel venv pos outp; Omake_value_type.ValNone | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityExact 2, List.length args))) let fprint venv pos loc args = let pos = string_pos "fprint" pos in print_aux venv pos loc "" args let print venv pos loc args = let pos = string_pos "print" pos in let stdout_fd = Omake_env.venv_find_var venv pos loc Omake_var.stdout_var in fprint venv pos loc (stdout_fd :: args) let eprint venv pos loc args = let pos = string_pos "eprint" pos in let stderr_fd = Omake_env.venv_find_var venv pos loc Omake_var.stderr_var in fprint venv pos loc (stderr_fd :: args) let fprintln venv pos loc args = let pos = string_pos "fprintln" pos in print_aux venv pos loc "\n" args let println venv pos loc args = let pos = string_pos "println" pos in let stdout_fd = Omake_env.venv_find_var venv pos loc Omake_var.stdout_var in fprintln venv pos loc (stdout_fd :: args) let eprintln venv pos loc args = let pos = string_pos "eprintln" pos in let stderr_fd = Omake_env.venv_find_var venv pos loc Omake_var.stderr_var in fprintln venv pos loc (stderr_fd :: args) (* * \begin{doc} * \section{Value printing functions} * \funref{fprintv} * \funref{printv} * \funref{eprintv} * \funref{fprintvln} * \funref{printvln} * \funref{eprintvln} * * Values can be printed with the \verb+printv+ and \verb+printvln+ functions. * The \verb+printvln+ function adds a terminating newline to the value being * printed, the \verb+printv+ function does not. * * \begin{verbatim} * fprintv(<file>, <string>) * printv(<string>) * eprintv(<string>) * fprintvln(<file>, <string>) * printvln(<string>) * eprintvln(<string>) * \end{verbatim} * * The \verb+fprintv+ functions print to a file that has been previously opened with * \verb+fopen+. The \verb+printv+ functions print to the standard output channel, and * the \verb+eprintv+ functions print to the standard error channel. * \end{doc} *) let printv_aux venv pos loc nl args = match args with [fd; s] -> let outp, close_flag = Omake_value.out_channel_of_any_value venv pos fd in let outx = Omake_env.venv_find_channel venv pos outp in let s = Omake_value_print.pp_print_value Format.str_formatter s; Format.flush_str_formatter () in Lm_channel.output_string outx s; Lm_channel.output_string outx nl; Lm_channel.flush outx; if close_flag then Omake_env.venv_close_channel venv pos outp; Omake_value_type.ValNone | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityExact 2, List.length args))) let fprintv venv pos loc args = let pos = string_pos "fprintv" pos in printv_aux venv pos loc "" args let printv venv pos loc args = let pos = string_pos "printv" pos in let stdout_fd = Omake_env.venv_find_var venv pos loc Omake_var.stdout_var in fprintv venv pos loc (stdout_fd :: args) let eprintv venv pos loc args = let pos = string_pos "eprintv" pos in let stderr_fd = Omake_env.venv_find_var venv pos loc Omake_var.stderr_var in fprintv venv pos loc (stderr_fd :: args) let fprintvln venv pos loc args = let pos = string_pos "fprintvln" pos in printv_aux venv pos loc "\n" args let printvln venv pos loc args = let pos = string_pos "printvln" pos in let stdout_fd = Omake_env.venv_find_var venv pos loc Omake_var.stdout_var in fprintvln venv pos loc (stdout_fd :: args) let eprintvln venv pos loc args = let pos = string_pos "eprintvln" pos in let stderr_fd = Omake_env.venv_find_var venv pos loc Omake_var.stderr_var in fprintvln venv pos loc (stderr_fd :: args) (************************************************************************ * Printf. *) module Args = struct type t = { print_venv : Omake_env.t; print_pos : Omake_value_type.pos; print_loc : Lm_location.t; print_fmt : Format.formatter; print_fd : Omake_value_type.prim_channel; print_channel : Lm_channel.t } type value = Omake_value_type.t (* * Create the buffers and channels. *) let create_channel venv pos loc channel = let fmt = Lm_printf.byte_formatter (Lm_channel.output_buffer channel) (fun () -> Lm_channel.flush channel) in let fd = Omake_env.venv_add_formatter_channel venv fmt in let channel = Omake_env.venv_find_channel venv pos fd in { print_venv = venv; print_pos = pos; print_loc = loc; print_fmt = fmt; print_fd = fd; print_channel = channel } let create_buffer venv pos loc buf = let fmt = Format.formatter_of_buffer buf in let fd = Omake_env.venv_add_formatter_channel venv fmt in let channel = Omake_env.venv_find_channel venv pos fd in { print_venv = venv; print_pos = pos; print_loc = loc; print_fmt = fmt; print_fd = fd; print_channel = channel } (* * When done, close the channels, and get the string. *) let close info = let { print_fd = fd; print_venv = venv; print_pos = pos; print_fmt = fmt; _ } = info in Omake_env.venv_close_channel venv pos fd; Format.pp_print_flush fmt () (* * The printers. *) let print_char info c = Lm_channel.output_char info.print_channel c let print_string info s = Lm_channel.output_string info.print_channel s (* * Formatter flushes the buffer. *) let flush info = Lm_channel.flush info.print_channel let open_box info i = flush info; Format.pp_open_box info.print_fmt i let open_hbox info = flush info; Format.pp_open_hbox info.print_fmt () let open_vbox info i = flush info; Format.pp_open_vbox info.print_fmt i let open_hvbox info i = flush info; Format.pp_open_hvbox info.print_fmt i let open_hovbox info i = flush info; Format.pp_open_hovbox info.print_fmt i let close_box info = flush info; Format.pp_close_box info.print_fmt () let print_cut info = flush info; Format.pp_close_box info.print_fmt () let print_space info = flush info; Format.pp_print_space info.print_fmt () let force_newline info = flush info; Format.pp_force_newline info.print_fmt () let print_break info i j = flush info; Format.pp_print_break info.print_fmt i j let print_flush info = flush info; Format.pp_print_flush info.print_fmt () let print_newline info = flush info; Format.pp_print_newline info.print_fmt () (* * Converters. *) let bool_of_value info v = let { print_venv = venv; print_pos = pos; _ } = info in Omake_value.bool_of_value venv pos v let char_of_value info v = let { print_venv = venv; print_pos = pos; _ } = info in let s = Omake_value.string_of_value venv pos v in if String.length s <> 1 then raise (Omake_value_type.OmakeException (pos, StringStringError ("not a character", s))); s.[0] let int_of_value info v = let { print_venv = venv; print_pos = pos; _ } = info in Omake_value.int_of_value venv pos v let float_of_value info v = let { print_venv = venv; print_pos = pos; _ } = info in Omake_value.float_of_value venv pos v let string_of_value info v = let { print_venv = venv; print_pos = pos; _ } = info in Omake_value.string_of_value venv pos v let print_value info v = flush info; Omake_value_print.pp_print_value info.print_fmt v (* * Applications. *) let apply1 info arg1 = let { print_venv = venv; print_pos = pos; print_loc = loc; print_fd = fd; _ } = info in ignore (Omake_eval.eval_apply venv pos loc arg1 [ValChannel (OutChannel, fd)] []) let apply2 info arg1 arg2 = let { print_venv = venv; print_pos = pos; print_loc = loc; print_fd = fd; _ } = info in ignore (Omake_eval.eval_apply venv pos loc arg1 [ValChannel (OutChannel, fd); arg2] []) (* * Catch too many arguments. *) let exit info args = match args with [] -> Omake_value_type.ValNone | arg :: _ -> let { print_pos = pos; _ } = info in raise (Omake_value_type.OmakeException (pos, StringValueError ("too many arguments to printf", arg))) end module Printf = Omake_printf.MakePrintf (Args);; let fprintf_aux venv pos loc channel fmt args = let fmt = Omake_value.string_of_value venv pos fmt in let buf = Args.create_channel venv pos loc channel in let result = try Printf.fprintf buf fmt args with exn -> Args.close buf; raise exn in Args.close buf; result let printf_fun venv pos loc args = let pos = string_pos "printf" pos in match args with fmt :: args -> let stdout = Omake_env.venv_find_var venv pos loc Omake_var.stdout_var in let stdout = Omake_value.channel_of_value venv pos stdout in fprintf_aux venv pos loc stdout fmt args | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityExact 1, List.length args))) let eprintf_fun venv pos loc args = let pos = string_pos "eprintf" pos in match args with fmt :: args -> let stderr = Omake_env.venv_find_var venv pos loc Omake_var.stderr_var in let stderr = Omake_value.channel_of_value venv pos stderr in fprintf_aux venv pos loc stderr fmt args | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityExact 1, List.length args))) let fprintf_fun venv pos loc args = let pos = string_pos "fprintf" pos in match args with fd :: fmt :: args -> let channel = Omake_value.channel_of_value venv pos fd in fprintf_aux venv pos loc channel fmt args | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityExact 2, List.length args))) let sprintf_fun venv pos loc args = let pos = string_pos "sprintf" pos in match args with |fmt :: args -> let fmt = Omake_value.string_of_value venv pos fmt in let buf = Buffer.create 100 in let info = Args.create_buffer venv pos loc buf in let _ = try Printf.fprintf info fmt args with exn -> Args.close info; raise exn in Args.close info; Omake_value_type.ValData (Buffer.contents buf) | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityExact 1, List.length args))) (* * \begin{doc} * \subsection{Miscellaneous functions} * \subsubsection{set-channel-line} * * \begin{verbatim} * set-channel-line(channel, filename, line) * channel : Channel * filename : File * line : int * \end{verbatim} * * Set the line number information for the channel. * \end{doc} *) let set_channel_line_fun venv pos loc args = let pos = string_pos "set-channel-line" pos in let chan, file, line = match args with [chan; file; line] -> chan, file, line | _ -> raise (Omake_value_type.OmakeException (loc_pos loc pos, ArityMismatch (ArityExact 3, List.length args))) in let chan = Omake_value.channel_of_value venv pos chan in let file = Omake_value.string_of_value venv pos file in let line = Omake_value.int_of_value venv pos line in Lm_channel.set_line chan file line; Omake_value_type.ValNone (************************************************************************ * Tables. *) let () = let builtin_vars = ["nl", (fun _ -> Omake_value_type.ValString "\n"); "stdin", (fun _ -> ValChannel (InChannel, Omake_env.venv_stdin)); "stdout", (fun _ -> ValChannel (OutChannel, Omake_env.venv_stdout)); "stderr", (fun _ -> ValChannel (OutChannel, Omake_env.venv_stderr))] in let builtin_funs = [true, "open-in-string", open_in_string, Omake_ir.ArityExact 1; true, "open-out-string", open_out_string, ArityExact 0; true, "out-contents", out_contents, ArityExact 1; true, "fopen", fopen, ArityExact 2; true, "close", close, ArityExact 1; true, "read", read, ArityExact 2; true, "input-line", input_line, ArityExact 1; true, "write", write, ArityRange (2, 4); true, "lseek", lseek, ArityExact 3; true, "rewind", rewind, ArityExact 1; true, "tell", tell, ArityExact 1; true, "flush", flush, ArityExact 1; true, "channel-name", channel_name, ArityExact 1; true, "dup", dup, ArityExact 1; true, "dup2", dup2, ArityExact 2; true, "set-nonblock-mode", set_nonblock_mode, ArityExact 2; true, "set-close-on-exec", set_close_on_exec_mode, ArityExact 2; true, "pipe", pipe, ArityExact 0; true, "mkfifo", mkfifo, ArityExact 2; true, "select", select, ArityExact 4; true, "lockf", lockf, ArityExact 3; true, "socket", socket, ArityExact 3; true, "bind", bind, ArityRange (2, 3); true, "listen", listen, ArityExact 2; true, "accept", accept, ArityExact 1; true, "connect", connect, ArityExact 1; true, "getc", getc, ArityRange (0, 1); true, "gets", gets, ArityRange (0, 1); true, "fgets", fgets, ArityRange (0, 1); true, "print", print, ArityExact 1; true, "eprint", eprint, ArityExact 1; true, "fprint", fprint, ArityExact 2; true, "println", println, ArityExact 1; true, "eprintln", eprintln, ArityExact 1; true, "fprintln", fprintln, ArityExact 2; true, "printv", printv, ArityExact 1; true, "eprintv", eprintv, ArityExact 1; true, "fprintv", fprintv, ArityExact 2; true, "printvln", printvln, ArityExact 1; true, "eprintvln", eprintvln, ArityExact 1; true, "fprintvln", fprintvln, ArityExact 2; true, "printf", printf_fun, ArityAny; true, "eprintf", eprintf_fun, ArityAny; true, "fprintf", fprintf_fun, ArityAny; true, "sprintf", sprintf_fun, ArityAny; true, "set-channel-line", set_channel_line_fun, ArityExact 3] in let builtin_info = {Omake_builtin_type. builtin_empty with builtin_vars = builtin_vars; builtin_funs = builtin_funs } in Omake_builtin.register_builtin builtin_info