Can an escript invoke another escript function? - erlang

a.escript:
main(_)->
b:read_config().
b.escript:
-module(b).
%% function exported
read_config()->
%% read file
io:format("hh~n").
If I convert the b.escript to b.erl and compile it to b.beam, it works.
But I don't want to compile manually.
Maybe I can insert c:c(b) like this:
main()->
c:c(b),
b:read_config().
Can't auto load the b.escript when useing like b.eam?

Yes, it is possible using compile:file/1
a.escript
%%This First line (i.e. the comment) MUST exist if we would like to call the script using 'escript <filename>', otherwise compilation error will occur. See 'https://www.erlang.org/doc/man/escript.html'
main([]) ->
compile:file(b),
b:read_config().
b.erl
-module(b).
-export([read_config/0]). %%Function MUST be exported, otherwise it will not be recognized from caller (e.g. escript )
read_config()->
%% read file
io:format("hh~n").
Call from Shell
$ escript a.escript
hh

Related

Validate URL in Informix 4GL program

In my Informix 4GL program, I have an input field where the user can insert a URL and the feed is later being sent over to the web via a script.
How can I validate the URL at the time of input, to ensure that it's a live link? Can I make a call and see if I get back any errors?
I4GL checking the URL
There is no built-in function to do that (URLs didn't exist when I4GL was invented, amongst other things).
If you can devise a C method to do that, you can arrange to call that method through the C interface. You'll write the method in native C, and then write an I4GL-callable C interface function using the normal rules. When you build the program with I4GL c-code, you'll link the extra C functions too. If you build the program with I4GL-RDS (p-code), you'll need to build a custom runner with the extra function(s) exposed. All of this is standard technique for I4GL.
In general terms, the C interface code you'll need will look vaguely like this:
#include <fglsys.h>
// Standard interface for I4GL-callable C functions
extern int i4gl_validate_url(int nargs);
// Using obsolescent interface functions
int i4gl_validate_url(int nargs)
{
if (nargs != 1)
fgl_fatal(__FILE__, __LINE__, -1318);
char url[4096];
popstring(url, sizeof(url));
int r = validate_url(url); // Your C function
retint(r);
return 1;
}
You can and should check the manuals but that code, using the 'old style' function names, should compile correctly. The code can be called in I4GL like this:
DEFINE url CHAR(256)
DEFINE rc INTEGER
LET url = "http://www.google.com/"
LET rc = i4gl_validate_url(url)
IF rc != 0 THEN
ERROR "Invalid URL"
ELSE
MESSAGE "URL is OK"
END IF
Or along those general lines. Exactly what values you return depends on your decisions about how to return a status from validate_url(). If need so be, you can return multiple values from the interface function (e.g. error number and text of error message). Etc. This is about the simplest possible design for calling some C code to validate a URL from within an I4GL program.
Modern C interface functions
The function names in the interface library were all changed in the mid-00's, though the old names still exist as macros. The old names were:
popstring(char *buffer, int buflen)
retint(int retval)
fgl_fatal(const char *file, int line, int errnum)
You can find the revised documentation at IBM Informix 4GL v7.50.xC3: Publication library in PDF in the 4GL Reference Manual, and you need Appendix C "Using C with IBM Informix 4GL".
The new names start ibm_lib4gl_:
ibm_libi4gl_popMInt()
ibm_libi4gl_popString()
As to the error reporting function, there is one — it exists — but I don't have access to documentation for it any more. It'll be in the fglsys.h header. It takes an error number as one argument; there's the file name and a line number as the other arguments. And it will, presumably, be ibm_lib4gl_… and there'll be probably be Fatal or perhaps fatal (or maybe Err or err) in the rest of the name.
I4GL running a script that checks the URL
Wouldn't it be easier to write a shell script to get the status code? That might work if I can return the status code or any existing results back to the program into a variable? Can I do that?
Quite possibly. If you want the contents of the URL as a string, though, you'll might end up wanting to call C. It is certainly worth thinking about whether calling a shell script from within I4GL is doable. If so, it will be a lot simpler (RUN "script", IIRC, where the literal string would probably be replaced by a built-up string containing the command and the URL). I believe there are file I/O functions in I4GL now, too, so if you can get the script to write a file (trivial), you can read the data from the file without needing custom C. For a long time, you needed custom C to do that.
I just need to validate the URL before storing it into the database. I was thinking about:
#!/bin/bash
read -p "URL to check: " url
if curl --output /dev/null --silent --head --fail "$url"; then
printf '%s\n' "$url exist"
else
printf '%s\n' "$url does not exist"
fi
but I just need the output instead of /dev/null to be into a variable. I believe the only option is to dump the output into a temp file and read from there.
Instead of having I4GL run the code to validate the URL, have I4GL run a script to validate the URL. Use the exit status of the script and dump the output of curl into /dev/null.
FUNCTION check_url(url)
DEFINE url VARCHAR(255)
DEFINE command_line VARCHAR(255)
DEFINE exit_status INTEGER
LET command_line = "check_url ", url
RUN command_line RETURNING exit_status
RETURN exit_status
END FUNCTION {check_url}
Your calling code can analyze exit_status to see whether it worked. A value of 0 indicates success; non-zero indicates a problem of some sort, which can be deemed 'URL does not work'.
Make sure the check_url script (a) exits with status zero on success and non-zero on any sort of failure, and (b) doesn't write anything to standard output (or standard error) by default. The writing to standard error or output will screw up screen layouts, etc, and you do not want that. (You can obviously have options to the script that enable standard output, or you can invoke the script with options to suppress standard output and standard error, or redirect the outputs to /dev/null; however, when used by the I4GL program, it should be silent.)
Your 'script' (check_url) could be as simple as:
#!/bin/bash
exec curl --output /dev/null --silent --head --fail "${1:-http://www.example.com/"
This passes the first argument to curl, or the non-existent example.com URL if no argument is given, and replaces itself with curl, which generates a zero/non-zero exit status as required. You might add 2>/dev/null to the end of the command line to ensure that error messages are not seen. (Note that it will be hell debugging this if anything goes wrong; make sure you've got provision for debugging.)
The exec is a minor optimization; you could omit it with almost no difference in result. (I could devise a scheme that would probably spot the difference; it involves signalling the curl process, though — kill -9 9999 or similar, where the 9999 is the PID of the curl process — and isn't of practical significance.)
Given that the script is just one line of code that invokes another program, it would be possible to embed all that in the I4GL program. However, having an external shell script (or Perl script, or …) has merits of flexibility; you can edit it to log attempts, for example, without changing the I4GL code at all. One more file to distribute, but better flexibility — keep a separate script, even though it could all be embedded in the I4GL.
As Jonathan said "URLs didn't exist when I4GL was invented, amongst other things". What you will find is that the products that have grown to superceed Informix-4gl such as FourJs Genero will cater for new technologies and other things invented after I4GL.
Using FourJs Genero, the code below will do what you are after using the Informix 4gl syntax you are familiar with
IMPORT com
MAIN
-- Should succeed and display 1
DISPLAY validate_url("http://www.google.com")
DISPLAY validate_url("http://www.4js.com/online_documentation/fjs-fgl-manual-html/index.html#c_fgl_nf.html") -- link to some of the features added to I4GL by Genero
-- Should fail and display 0
DISPLAY validate_url("http://www.google.com/testing")
DISPLAY validate_url("http://www.google2.com")
END MAIN
FUNCTION validate_url(url)
DEFINE url STRING
DEFINE req com.HttpRequest
DEFINE resp com.HttpResponse
-- Returns TRUE if http request to a URL returns 200
TRY
LET req = com.HttpRequest.create(url)
CALL req.doRequest()
LET resp = req.getResponse()
IF resp.getStatusCode() = 200 THEN
RETURN TRUE
END IF
-- May want to handle other HTTP status codes
CATCH
-- May want to capture case if not connected to internet etc
END TRY
RETURN FALSE
END FUNCTION

Erlang escript launching application with start parameters

Currently my Erlang application is started within an escript (TCP server) and all works fine since it uses the default port I provided. Now I want to pass the port via the escript to the application but I have no idea how. (The app runs a supervisor)
script.escript
!/usr/bin/env escript
%% -*- erlang -*-
-export([main/1]).
main([UDPort, TCPort]) ->
U = list_to_integer(UDPort),
T = list_to_integer(TCPort),
app:start(), %% Want to pass T into the startup.
receive
_ -> ok
end;
...
app.erl
-module(app).
-behaviour(application).
-export([start/0, start/2, stop/0, stop/1]).
-define(PORT, 4300).
start () -> application:start(?MODULE). %% This is called by the escript.
stop () -> application:stop(?MODULE).
start (_StartType, _StartArgs) -> supervisor:start(?PORT).
stop (_State) -> ok.
I'm honestly not sure if this is possible with using application but I thought it best to just ask.
The common way is to start things from whatever shell just calling
erl -run foo
But you can also do
erl -appname key value
to set an environment value and then
application:get_env(appname, key)
to get the value you are looking for.
That said...
I like to have service applications be things that don't have to shut down to be (re)configured. I usually include some message protocol like {config, Aspect, Setting} or similar that can alter the basic state of a service on the fly. Because I often do this I usually just wind up having whatever script starts up the application also send a configuration message to it.
So with this in mind, consider this rough conceptual example:
!/usr/bin/env escript
%% -*- erlang -*-
-export([main/1]).
main([UDPort, TCPort]) ->
U = list_to_integer(UDPort),
T = list_to_integer(TCPort),
ok = case whereis(app) of
undefined -> app:start();
_Pid -> ok
end,
ok = set_ports(U, T).
%% Just an illustration.
%% Making this a synchronous gen_server/gen_fsm call is way better.
set_ports(U, T) ->
app ! {config, listen, {tcp, T}},
app ! {config, listen, {udp, U}},
ok.
Now not only is the startup script a startup script, it is also a config script. The point isn't to have a startup script, it is to have a service running on the ports you designated. This isn't a conceptual fit for all tools, of course, but it should give you some ideas. There is also the practice of putting a config file somewhere the application knows to look and just reading terms from it, among other techniques (like including ports in the application specification, etc.).
Edit
I just realized you are doing this in an escript which will spawn a new node every time you call it. To make the technique above work properly you would need to make the escript name a node for the service to run on, and locate it if it already exists.

Assign bash output to variable in .lua script

I would like to assign output of a bash command to a variable in .lua script. Is it possible?
For instance, something similar to:
var = `ps uax | grep myprocess`
Yes, you need to use io.popen for this.
io.popen (prog [, mode])
Starts program prog in a separated process and returns a file handle that you can use to read data from this program (if mode is "r", the default) or to write data to this program (if mode is "w").
This function is system dependent and is not available on all platforms.
Also see How to execute an external command?.
io.popen calls a command but returns a file object so you can read the output of the command, if the second argument is 'r', but you can also pass input to a command with a second argument of 'w'. Unfortunately, you don't get a io.popen2, and you don't get the return code.

Luac don't work with one script

I have embedded lua and I want precompile my script. For that, I call the main of luac (with argc the number of file is 1). My problem is on the function doargs of luac. I don't understand the use of the variable i. Because when I use one script. The result of i after the doargs function is 1. And in the main function we have argc -= i after. And so argc = 0 and I have a error "no file". Any idea ?
luac is meant to be a command line utility for compiling .lua files. This expected usage is the reason why you're getting an error.
When you start an executable the OS passes the name of the program as its first argument (argv[0]). The luac main function assumes it is being called by the OS, so it expects that there will always be at least one argument and its argv[0] will be the name of the executable.
For this reason doargs starts its for loop at 1 and will always ignore that first (0th) argument. It returns how many options it has processed, which is also the offset of the first filename in the argv array. The main function uses this to know where the list of files starts.
If you really want to use the main function to precompile your scripts, then supply an extra dummy argument at the beginning of your argument array and list your files after that. Preferably you should use luac from the command line and supply an output file where the precompiled script will be stored like this:
luac -o outputFile script.lua
Alternatively, take a look at chapter 8 of Programming in Lua (Compilation, Execution, and Errors) for a pure Lua solution, or the the luaL_dofile, luaL_dostring, lua_dump, and lua_load functions in the Reference Manual for a C API solution.

Erlang function does not exist in module?

In lager.elr (the main module of https://github.com/basho/lager) there is no function with name "debug" but I have an application that call debug function from lager module like:
lager:debug(Str, Args)
I am beginner in Erlang but I know when we call a function from a module lile "mymodule:myfunction" there should be a function with name "myfunction" in file mymodule.erl but in this case when I search in lager.erl for function "debug" I can't find it.
The reason you don't see a mention of lager:debug/2 is because lager uses a parse transform. So when compiling the code, it is fed through lagers parse transform and the call to lager:debug/2 is substituted for another call to another module function.
If you compile your code with the correct parse transform option for lager, then the code would work.
"I GIVE CRAP ANSWERS" gave a good explanation of this strange behaviour. I post here a code that should show you what was the code generated in the beam file:
In the shell:
utility:decompile([yourfile.beam]).
%% Author: PCHAPIER
%% Created: 25 mai 2010
-module(utility).
%%
%% Include files
%%
%%
%% Exported Functions
%%
-export([decompile/1, decompdir/1]).
-export([shuffle/1]).
%%
%% API Functions
%%
decompdir(Dir) ->
Cmd = "cd " ++ Dir,
os:cmd(Cmd),
L = os:cmd("dir /B *.beam"),
L1 = re:split(L,"[\t\r\n+]",[{return,list}]),
io:format("decompdir: ~p~n",[L1]),
decompile(L1).
decompile(Beam = [H|_]) when is_integer(H) ->
io:format("decompile: ~p~n",[Beam]),
{ok,{_,[{abstract_code,{_,AC}}]}} = beam_lib:chunks(Beam ++ ".beam",[abstract_code]),
{ok,File} = file:open(Beam ++ ".erl",[write]),
io:fwrite(File,"~s~n", [erl_prettypr:format(erl_syntax:form_list(AC))]),
file:close(File);
decompile([H|T]) ->
io:format("decompile: ~p~n",[[H|T]]),
decompile(removebeam(H)),
decompile(T);
decompile([]) ->
ok.
shuffle(P) ->
Max = length(P)*10000,
{_,R}= lists:unzip(lists:keysort(1,[{random:uniform(Max),X} || X <- P])),
R.
%%
%% Local Functions
%%
removebeam(L) ->
removebeam1(lists:reverse(L)).
removebeam1([$m,$a,$e,$b,$.|T]) ->
lists:reverse(T);
removebeam1(L) ->
lists:reverse(L).
You don't see it in the lager.erl file because it's in the lager.hrl file that's included at the top of lager.erl. Erlang allows you to include a file with the -include("filename.hrl") directive. As a convention the include files end in an hrl extension but it could really be anything.
https://github.com/basho/lager/blob/master/include/lager.hrl

Resources