However, instead of cloning and then calling open(), you can clone the File with a filename and open mode. This is the same thing as cloning and then calling open, except shorter and faster. Default open mode is "r" and default mask is 0666.
Alternatively, you can clone a File with "stdin", "stdout" or "stderr" as argument. This will open the specified standard stream.
For the advanced users, you can use the file descriptors of the systems (note: emulated by pike on some systems - like NT). This is only useful for streaming purposes on unix systems. This is not recommended at all if you don't know what you're into. Default mode for this is "rw".
'r' | open file for reading |
'w' | open file for writing |
'a' | open file for append (use with 'w') |
't' | truncate file at open (use with 'w') |
'c' | create file if it doesn't exist (use with 'w') |
'x' | fail if file already exist (use with 'c') |
How should _always_ contain at least one of 'r' or 'w'.
The third argument is protection bits if the file is created. Default is 0666 (all read+write, in octal notation).
If a one is given as second argument to read(), read will not try its best to read as many bytes as you asked it to read, it will merely try to read as many bytes as the system read function will return. This mainly useful with stream devices which can return exactly one row or packet at a time.
If no arguments are given, read will read to the end of the file/stream.
If a one is given as a second argument to read_oob(), only as many bytes of out-of-band data as are currently available will be returned.
If no arguments are given, read_oob will read to the end of the stream.
It is not guaranteed that all out-of-band data sent from the other end will be received. Most streams only allow for a single byte of out-of-band data at a time.
It is not guaranteed that all out-of-band data will be received at the other end. Most streams only allow for a single byte of out-of-band data at a time. Some streams will send the rest of the data as ordinary data.
When out-of-band data arrives on the stream, read_oob_callback will be called with some or all of this data. When the stream allows out-of-band data to be sent, write_oob_callback is called so that you can write out-of-band data to it.
All callbacks will have the id of file as first argument when called.
If no arguments are given, the callbacks are not changed. The stream is just set to nonblocking mode.
/* Redirect stdin to come from the file 'foo' */
object o=Stdio.File();
o->open("foo","r");
o->dup2(Stdio.File("stdin"));
If you give a port number to this function, the socket will be bound to this port locally before connecting anywhere. This is only useful for some silly protocols like FTP. You may also specify an address to bind to if your machine has many IP numbers.
This function returns 1 for success, 0 otherwise. Note that if the socket is in nonblocking mode, you have to wait for a write or close callback before you know if the connection failed or not.
Here is an example of how to use the TCP functions in Stdio.File in blocking mode. This short program takes a URL as first argument, connects to the WWW server, sends a HEAD request and writes the reply to stdout. For clarity, all calls to Stdio.File use File:: even if that is not strictly necessary.
import Stdio;
inherit File;
int main(int argc, array(string) argv)
{
string host;
string path="";
int port=80;
sscanf(argv[1],"http://%s",argv[1]);
sscanf(argv[1],"%s/%s",host,path);
sscanf(host,"%s:%d",host,port);
if(!File::open_socket())
{
perror("Open socket failed");
exit(1);
}
if(!File::connect(host,port))
{
perror("Failed to connect to remote host");
exit(1);
}
File::write(sprintf("HEAD /%s HTTP/1.0\n",path));
stdout::write(File::read());
}
Example:
This example will read lines from standard input for as long as there are more lines to read. Each line will then be written to stdout together with the line number. We could use Stdio.stdout.write instead of just write because they are the same function.int main()
{
int line;
while(string s=Stdio.stdin.gets())
write(sprintf("%5d: %s\n",line++,s));
}
If the optional argument IP is given, bind will try to bind to this IP name (or number).
([ "data" : string recieved data "ip" : string recieved from this ip "port" : int ...and this port ])
([ "data" : string recieved data "ip" : string recieved from this ip "port" : int ...and this port ])
Lookup of terminal information will first be done in the systems terminfo database, and if that fails in the termcap database. If neither database exists, a hardcoded entry for "dumb" will be used.
infd defaults to Stdio.stdout.
interm defaults to Stdio.Terminfo.getTerm().
outfd defaults to infd, unless infd is 0, in which case outfd defaults to Stdio.stdout.
outterm defaults to interm.
Any of headers, from and trailers may be left out by setting them to 0.
Setting offset to -1 means send from the current position in from.
Setting len to -1 means send until from's end of file is reached.
For callback to be called, the backend must be active (ie main() must have returned -1).
In some cases, the backend must also be active for any sending to be performed at all.
This function filters the indata through the UNIX-command /bin/grep and return the result.string grep(string indata, string needle)
{
object out=Stdio.File(),in=Stdio.File();
object process=
Process.create_process(({"/bin/grep",needle}),
(["stdin":out->pipe(),
"stdout":in->pipe()]) );
out->write(indata);
out->close();
process->wait();
return in->read();
}
Usually WWW involves HTML. HTML (Hyper-Text Markup Language) is a way to write documents with embedded pictures and links to other pages. These links are normally displayed underlined and if you click them your WWW- browser will load whatever document that link leads to.
We inherit Stdio.Port into this program so we can bind a TCP socket to accept incoming connection. A socket is simply a number to separate communications to and from different programs on the same computer.#!/usr/local/bin/pike
/* A very small httpd capable of fetching files only. * Written by Fredrik Hübinette as a demonstration of Pike. */
inherit Stdio.Port;
Next are some constants that will affect how uHTTPD will operate. This uses the preprocessor directive #define. The preprocessor is the first stage in the compiling process and can make textual processing of the code before it is compiled. As an example, after the first define below, all occurrences of 'BLOCK' will be replaced with 16060.
A port is a destination for a TCP connection. It is simply a number on the local computer. 1905 is not the standard port for HTTP connections though, which means that if you want to access this WWW server from a browser you need to specify the port like this: http://my.host.my.domain:1905//* Amount of data moved in one operation */
#define BLOCK 16060
/* Where do we have the html files ? */
#define BASE "/usr/local/html/"
/* File to return when we can't find the file requested */
#define NOFILE "/user/local/html/nofile.html"
/* Port to open */
#define PORT 1905
Next we declare a class called output_class. Later we will clone one instance of this class for each incoming HTTP connection.
Our new class inherits Stdio.File twice. To be able to separate them they are then named 'socket' and 'file'.class output_class
{
inherit Stdio.File : socket;
inherit Stdio.File : file;
Then there is a global variable called offset which is initialized to zero. (Each instance of this class will have its own instance of this variable, so it is not truly global, but...) Note that the initialization is done when the class is cloned (or instantiated if you prefer C++ terminology).int offset=0;
Next we define the function write_callback(). Later the program will go into a 'waiting' state, until something is received to process, or until there is buffer space available to write output to. When that happens a callback will be called to do this. The write_callback() is called when there is buffer space available. In the following lines 'void' means that it does not return a value. Write callback will be used further down as a callback and will be called whenever there is room in the socket output buffer.
The following line means: call seek in the inherited program 'file'.void write_callback()
{
int written;
string data;
Move the file pointer to the where we want to the position we want to read from. The file pointer is simply a location in the file, usually it is where the last read() ended and the next will begin. seek() can move this pointer to where we want it though.file::seek(offset);
Read BLOCK (16060) bytes from the file. If there are less that that left to read only that many bytes will be returned.data=file::read(BLOCK);
If we managed to read something...if(strlen(data))
{
... we try to write it to the socket.written=socket::write(data);
Update offset if we managed to write to the socket without errors.if(written >= 0)
{
offset+=written;
return;
}
If something went wrong during writing, or there was nothing left to read we destruct this instance of this class.werror("Error: "+socket::errno()+".\n");
}
That was the end of write_callback()destruct(this_object());
}
Next we need a variable to buffer the input received in. We initialize it to an empty string.
And then we define the function that will be called when there is something in the socket input buffer. The first argument 'id' is declared as mixed, which means that it can contain any type of value. The second argument is the contents of the input buffer.string input="";
Append data to the string input. Then we check if we have received a a complete line yet. If so we parse this and start outputting the file.void read_callback(mixed id,string data)
{
string cmd;
input+=data;
This sscanf is pretty complicated, but in essence it means: put the first word in 'input' in 'cmd' and the second in 'input' and return 2 if successful, 0 otherwise.if(sscanf(input,"%s %s%*[\012\015 \t]",cmd,input)>2)
{
If the first word isn't GET print an error message and terminate this instance of the program. (and thus the connection)if(cmd!="GET")
{
werror("Only method GET is supported.\n");
destruct(this_object());
return;
}
Remove the leading slash.sscanf(input,"%*[/]%s",input);
Combine the requested file with the base of the HTML tree, this gives us a full filename beginning with a slash. The HTML tree is the directory on the server in which the HTML files are located. Normally all files in this directory can be accessed by anybody by using a WWW browser. So if a user requests 'index.html' then that file name is first added to BASE (/home/hubbe/www/html/ in this case) and if that file exists it will be returned to the browser.input=BASE+combine_path("/",input);
Try opening the file in read-only mode. If this fails, try opening NOFILE instead. Opening the file will enable us to read it later.if(!file::open(input,"r"))
{
If this fails too. Write an error message and destruct this object.if(!file::open(NOFILE,"r"))
{
Ok, now we set up the socket so we can write the data back.werror("Couldn't find default file.\n");
destruct(this_object());
return;
}
}
Set the buffer size to 64 kilobytes.socket::set_buffer(65536,"w");
Make it so that write_callback is called when it is time to write more data to the socket.socket::set_nonblocking(0,write_callback,0);
Jump-start the writing.write_callback();
That was the end of read_callback().}
}
This function is called if the connection is closed while we are reading from the socket.
This function is called when the program is instantiated. It is used to set up data the way we want it. Extra arguments to clone() will be sent to this function. In this case it is the object representing the new connection.void selfdestruct() { destruct(this_object()); }
We insert the data from the file f into 'socket'.void create(object f)
{
socket::assign(f);
Then we set up the callback functions and sets the file nonblocking. Nonblocking mode means that read() and write() will rather return that wait for I/O to finish. Then we sit back and wait for read_callback to be called.socket::set_nonblocking(read_callback,0,selfdestruct);
End of create()}
End of the new class.};
Next we define the function called when someone connects.
This creates a local variable of type 'object'. An object variable can contain a clone of any program. Pike does not consider clones of different programs different types. This also means that function calls to objects have to be resolved at run time.void accept_callback()
{
object tmp_output;
The function accept clones a Stdio.File and makes this equal to the newly connected socket.tmp_output=accept();
If it failed we just return.if(!tmp_output) return;
Otherwise we clone an instance of 'output_class' and let it take care of the connection. Each clone of output_class will have its own set of global variables, which will enable many connections to be active at the same time without data being mixed up. Note that the programs will not actually run simultaneously though.output_class(tmp_output);
Destruct the object returned by accept(), output_class has already copied the contents of this object.destruct(tmp_output);
Then there is main, the function that gets it all started.}
Write an encouraging message to stderr.int main(int argc, array(string) argv)
{
werror("Starting minimal httpd\n");
Bind PORT and set it up to call accept_callback as soon as someone connects to it. If the bind() fails we write an error message and return the 17 to indicate failure.if(!bind(PORT, accept_callback))
{
werror("Failed to open socket (already bound?)\n");
return 17;
}
If everything went ok, we return -17, any negative value returned by main() means that the program WON'T exit, it will hang around waiting for events instead. (like someone connecting)return - 17; /* Keep going */
That's it, this simple program can be used as the basis for a simple WWW-server. Note that today most WWW servers are very complicated programs, and the above program can never replace a modern WWW server. However, it is very fast if you only want a couple of web pages and have a slow machine available for the server.}