How to read from post param? - post

I need to parse text file. This file is in post param. I have code like this:
upload_file('POST', []) ->
File = Req:post_param("file"),
What should I do next? How to parse it?

What's inside Req:post_param("file") ?
You assume it's a path to a file: have you checked the value of File ?
Anyway, it's Req:post_files/0 you are probably looking for:
[{_, _FileName, TempLocation, _Size}|_] = Req:post_files(),
{ok,Data} = file:read_file(TempLocation),
It's also probably a Bad Idea to leave the file at it's temporary location, you'd better find a more suitable place to store them.
It seems the uploaded_file record has 5 fields now (for 10 months by now).
This is the updated example, with the fifth field:
[{_, _FileName, TempLocation, _Size, _Name}|_] = Req:post_files(),
{ok,Data} = file:read_file(TempLocation),
Oh, and because it's a record, following example should work even if the definition gets updated once again:
[File|_] = Req:post_files(),
{ok,Data} = file:read_file(File#uploaded_file.temp_file),
Another warning: the code above, as any erlanger will see, only deals with the first and, probably most of the times, only uploaded file. Should more files be uploaded at the same time, these would be ignored.

The answer really depend on the content of "File". for example if the file content a string with respecting erlang syntax such as:
[{{20,4},0},
{{10,5},0},
{{24,1},0},
{{22,1},0},
{{10,6},0}].
can be read with this code:
File = Req:post_param("file"),
{ok,B} = file:read_file(File),
{ok,Tokens,_} = erl_scan:string(binary_to_list(B)),
{ok,Term} = erl_parse:parse_term(Tokens),
%% at this point Term = [{{20,4},0},{{10,5},0},{{24,1},0},{{22,1},0},{{10,6},0}]
[Edit]
the Erlang libraries use most of the time tuple as return value. It can help to manage the normal case and error cases. In the previous code, all lines are "pattern matched" to the success case only. That means that it will crash if any of the operation fails. If the surrounding code cath the error you will be able to manage the error case, otherwise the process will simply die reporting a badmatch error.
I chose this implementation because at this level of code, there is nothing that can be done to deal with an error. {{badmatch,{error,enoent}} simply means that the return value of file:read_file(File) is not of the form {ok,B} as expected, but is {error,enoent}, which means that the file File does not exist in the current path.
extract of the documentation
read_file(Filename) -> {ok, Binary} | {error, Reason}
Types:
Filename = name()
Binary = binary()
Reason = posix() | badarg | terminated | system_limit
Returns {ok, Binary}, where Binary is a binary data object that contains the contents of Filename, or {error, Reason} if an error occurs.
Typical error reasons:
enoent
The file does not exist.
eacces
Missing permission for reading the file, or for searching one of the parent directories.
eisdir
The named file is a directory.
enotdir
A component of the file name is not a directory. On some platforms, enoent is returned instead.
enomem
There is not enough memory for the contents of the file.
In my opinion the calling code should manage this case if it is a real use case, for example File comes from a user interface, or let the error unmanaged if this case should not happen. In your case you could do
try File_to_term(Params) % call the above code with significant Params
catch
error:{badmatch,{error,enoent}} -> file_does_not_exist_management();
error:{badmatch,{error,eacces}} -> file_access_management();
error:{badmatch,{error,Error}} -> file_error(Error)
end

Related

"function arguments expected near 'levelc'" when using LOVE

I'm currently trying to make a level loading system for a game.
function love.filedropped(file)
ofile=io.open(file:getFilename(),"r")
io.input(ofile)
file:close
levelc=io.read()
for i=1,levelc do
levels[i]=io.read()
print levels[i]
end
levelc should be the first line of the file, and file:getFilename is the file to open (path included) the project gives an error message on startup, and i've used a similar structure before, but for an output. The error is at line 30, which is the levelc=io.read().
I've tried changing the name of the file pointer (it was "f" before, now "ofile") and i've tried using io.read("*l") instead of io.read() but same result.
EDITS:
-this is a love.filedropped(file)
-i need to open other files from a .txt later and i don't really understand how do do that
The parameter given by love.filedropped is a DroppedFile.
In your case helpful could be File:lines().
For example:
function love.filedropped(file)
-- Open for reading
file:open("r")
-- Iterate over the lines
local i = 0
for line in file:lines() do
i = i + 1
levels[i] = line
print(i, levels[i]) -- Notice the parentheses missing in your code
end
-- Close the file
file:close()
end
Notice that love2d usually only allows reading/writing files within the save or working directory. Dropped files are an exception.
Unrelated to this answer but things I noticed in your code:
Use locals, oFile should be local
file:close() required parentheses as its a function call
Same for the print
The filedropped callback has no end
You mentioned reading other files too, to do so, you can either:
Use love.filesystem.newFile and a similar approach as before
The recommended one-liner love.filesystem.lines

realize parameter change after reboot in ESP wifi (Lua)

I want to change the behaviour of my ESP module if some of my parameter was changed and then was restarted. I mean something like this.
if (????) then
print ("default value") else
print ("modified value") end
First I thought of writing a flag into a file, but it causes error during boot if it is not existing yet.
Any better idea?
If you want to store values beyond reboot you have to store them in some non-volatile memory. So using a file is a good way as you already suggested.
Unfortunately you did not provide the error message you get when it is not existing yet and you did not say if the flag or the file does not exist.
What you have to do is handling the error. So if your file does not exist ask the user to create a new one or create a file with default content from your program.
Same goes with the flag. If the file does not contain a flag yet, use a default value or ask the user to give one.
It's not bad or wrong to get errors as long learn from them or handle them properly.
io.open(filename[,mode]) returns nil plus an error message in case of an error.
So simply do something like:
local fileName = "C:\\superfile.txt"
local fileHandle, errorMsg = io.open(fileName)
if not fileHandle then
print("File access error: ", errorMsg)
-- add some error handling here
end
So in case you don't have that file you'll get
File access error: C:\superfile.txt: No such file or directory

FAKE Fsc task is writing build products to wrong directory

I'm just learning F#, and setting up a FAKE build harness for a hello-world-like application. (Though the phrase "Hell world" does occasionally come to mind... :-) I'm using a Mac and emacs (generally trying to avoid GUI IDEs by preference).
After a bit of fiddling about with documentation, here's how I'm invoking the F# compiler via FAKE:
let buildDir = #"./build-app/" // Where application build products go
Target "CompileApp" (fun _ -> // Compile application source code
!! #"src/app/**/*.fs" // Look for F# source files
|> Seq.toList // Convert FileIncludes to string list
|> Fsc (fun p -> // which is what the Fsc task wants
{p with //
FscTarget = Exe //
Platform = AnyCpu //
Output = (buildDir + "hello-fsharp.exe") }) // *** Writing to . instead of buildDir?
) //
That uses !! to make a FileIncludes of all the sources in the usual way, then uses Seq.toList to change that to a string list of filenames, which is then handed off to the Fsc task. Simple enough, and it even seems to work:
...
Starting Target: CompileApp (==> SetVersions)
FSC with args:[|"-o"; "./build-app/hello-fsharp.exe"; "--target:exe"; "--platform:anycpu";
"/Users/sgr/Documents/laboratory/hello-fsharp/src/app/hello-fsharp.fs"|]
Finished Target: CompileApp
...
However, despite what the console output above says, the actual build products go to the top-level directory, not the build directory. The message above looks like the -o argument is being passed to the compiler with an appropriate filename, but the executable gets put in . instead of ./build-app/.
So, 2 questions:
Is this a reasonable way to be invoking the F# compiler in a FAKE build harness?
What am I misunderstanding that is causing the build products to go to the wrong place?
This, or a very similar problem, was reported in FAKE issue #521 and seems to have been fixed in FAKE pull request #601, which see.
Explanation of the Problem
As is apparently well-known to everyone but me, the F# compiler as implemented in FSharp.Compiler.Service has a practice of skipping its first argument. See FSharp.Compiler.Service/tests/service/FscTests.fs around line 127, where we see the following nicely informative comment:
// fsc parser skips the first argument by default;
// perhaps this shouldn't happen in library code.
Whether it should or should not happen, it's what does happen. Since the -o came first in the arguments generated by FscHelper, it was dutifully ignored (along with its argument, apparently). Thus the assembly went to the default place, not the place specified.
Solutions
The temporary workaround was to specify --out:destinationFile in the OtherParams field of the FscParams setter in addition to the Output field; the latter is the sacrificial lamb to be ignored while the former gets the job done.
The longer term solution is to fix the arguments generated by FscHelper to have an extra throwaway argument at the front; then these 2 problems will annihilate in a puff of greasy black smoke. (It's kind of balletic in its beauty, when you think about it.) This is exactly what was just merged into the master by #forki23:
// Always prepend "fsc.exe" since fsc compiler skips the first argument
let optsArr = Array.append [|"fsc.exe"|] optsArr
So that solution should be in the newest version of FAKE (3.11.0).
The answers to my 2 questions are thus:
Yes, this appears to be a reasonable way to invoke the F# compiler.
I didn't misunderstand anything; it was just a bug and a fix is in the pipeline.
More to the point: the actual misunderstanding was that I should have checked the FAKE issues and pull requests to see if anybody else had reported this sort of thing, and that's what I'll do next time.

Erlang Bit Syntax pattern matching works in shell but not as passed argument

I am trying to make a simple UDP packet decoder.
packet_decoder(Packet)->
<<Opts:8,MobIdLength:8,MobId:64,MobIdType:8,MgeType:8,SeqNum:16,Rest/binary>> = Packet,
io:format("Options:~p~n",Opts),
io:format("MobIdLength:~p~n",MobIdLength),
io:format("MobId:~p~n",MobId),
io:format("MobIdType:~p~n",MobIdType),
io:format("MgeType:~p~n",MgeType),
io:format("SeqNum:~p~n",SeqNum).
Packet is passed by a receive loop:
rcv_loop(Socket) ->
inet:setopts(Socket, [{active, once}, binary]),
io:format("rcvr started:~n"),
receive
{udp, Socket, Host, Port, Bin} ->
packet_decoder(Bin),
rcv_loop(Socket)
end.
I keep getting(following error edited 9/7/12 9:30 EST):
** exception error: no match of right hand side value
<<131,8,53,134,150,4,149,0,80,15,1,2,1,2,0,16,80,71,115,
52,80,71,115,53,24,63,227,197,211,...>>
in function udp_server:packet_decoder/1
called as udp_server:packet_decoder(<<131,8,53,134,150,4,149,0,80,15,
1,2,1,2,0,16,80,71,115,52,80,71,
115,53,24,63,227,197,...>>)
in call from udp_server:rcv_loop/1
in call from udp_server:init/0
If I create the same variable in the Erlang shell as a binary, i.e.
Packet = <<131,8,53,134,150,4,149,0,80,15,1,2,1,2,0,16,80,71,115,52,80,71,115,53,24,63,227,197,211,228,89,72,0,0,0,0,0,0,0,16,0,5,5,32,1,4,255,159,15,18,28,0,34,62,2,0,0,0,0,0,0,0,47,67>>.
<<Opts:8,MobIdLength:8,MobId:64,MobIdType:8,MgeType:8,SeqNum:16,Rest/binary>> = Packet.
It works just fine. Is there some subtlety in passing this to a function that I am missing? I have tried what I think is everything(except the right way). I tried setting the type and size. I also just tried
<<Rest/binary>> = Packet.
To no avail. Any help much appreciated.
The error you are getting when you run your code does not match your code. The error you are getting:
** exception error: no match of right hand side value ...
is a badmatch error and comes from an explicit = match where the pattern does not match the value from the RHS. There is no = in the code for rcv_loop/1. This implies that the loop you are running is not this code. So there are some questions to be asked:
When you have recompiled the module containing rcv_loop/1 have you restarted the loop so you run the new code? This is not done automagically.
Are you sure you are loading/running the code you think you are? I know this question sounds stupid but it is very easy, and not uncommon, to work on one version of the code and load another. You need to get the paths right.
The other things about mentioned about your code would not give this error. The calls to io:format/2 are wrong but would result in errors when you make the actual calls to io:format/2. Using the variable Socket as you do is not an error, it just means that you only want to receive UDP packets from just that socket.
EDIT : the first part of my answer was completely wrong so in order to not mislead, I deleted it.
Like spotted Alexey Kachayev io:format takes as second parameter a list, so :
packet_decoder(Packet)->
<<Opts:8,MobIdLength:8,MobId:64,MobIdType:8,MgeType:8,SeqNum:16,Rest/binary>> = Packet,
io:format("Options:~p~n",[Opts]),
io:format("MobIdLength:~p~n",[MobIdLength]),
io:format("MobId:~p~n",[MobId]),
io:format("MobIdType:~p~n",[MobIdType]),
io:format("MgeType:~p~n",[MgeType]),
io:format("SeqNum:~p~n",[SeqNum]).
I figured it out(kinda). I had been working on this in erlide in eclipse which had worked fine for all of the other parts of the. I tried compiling it from the erl shell and it worked fine. There must be some minor difference in the way eclipse is representing the source or the way it invokes the erlang compiler and shell. I will take it up with erlide.org. Thanks for the help!

Run a command in erlang with a parameter

I'm trying to write a erlang module for ejabberd and can't quite work out the syntax.
What I'm trying to do is when a user sends a off-line message, run a script I've written.
on_offline(_From, To, _Packet) ->
?INFO_MSG("recieved offline packet to ~p", [To]),
?INFO_MSG("Got offline message", []),
osReturn = os:cmd("/opt/ejabberd-2.1.10/newmods/handleoffline.py " ++ To),
?INFO_MSG("Send to handler and got return ~s", [osReturn]),
none.
However when I run it, I get a few errors, if I have ++ To there I get the error
E(<0.423.0>:ejabberd_hooks:294) : {function_clause,
However even if I run it without the ++ I get another error telling me Bad Match.
All I'd like to do is run handleoffline.py with the first part of the user ID, anyone got any suggestions?
From the little information you have provided and assuming that your functions is being called with the ejabberd offline_message_hook I can at least find a problem.
In
osReturn = os:cmd("/opt/ejabberd-2.1.10/newmods/handleoffline.py " ++ To)
you are trying to use the ++ to concatenate the list you provided with To. But the major problem is that To isn't a string (list), and that generates your error. Your function is in the form of
on_offline(From, To, Packet) -> ok
where
To = From = #jid (see jlib)
one way to correct that would be to use
on_offline(_From, To=#jid{user = User, server=Server}, _Packet) ->
...
OsReturn = os:cmd("/opt/ejabberd-2.1.10/newmods/handleoffline.py " ++ User),
?INFO_MSG("Send to handler and got return ~s", [OsReturn]),
none.
because the field user on #jid is a string.
Edit: another error is that you need to chang osReturn to OsReturn because the first is an atom and will raise an error on your code, like selle pointed out
Edit your question and add more information if you have more problems, or to make this one clearer.

Resources