Vely logo install | documentation | examples | changelog
16.10.0 released May 10, 2023
Vely Single-Page Documentation 16.10.0

This page contains all of Vely documentation topics combined into one. It may be easier to search.

123_hello_world
after_request_handler
application_setup
before_request_handler
begin-transaction
building_URL
call-web
CGI
Client_API
close-file
command_line
commit-transaction
connect_apache_tcp_socket
connect_apache_unix_socket
connect_nginx_tcp_socket
connect_nginx_unix_socket
containerize_application
copy-file
copy-string
count-substring
current-row
database_config_file
database_queries
debugging
decode-base64
decode-hex
decode-url
decode-web
decrypt-data
delete-cookie
delete-file
delete-json
delete-mem
delete-query
deploying_application
derive-key
documentation
dot
encode-base64
encode-hex
encode-url
encode-web
encrypt-data
error_code
error_handling
example_cookies
example_create_table
example_docker
example_file_manager
example_form
example_hash
example_hello_world
example_json
example_multitenant_SaaS
example_sendmail
example_shopping
examples
example_stock
example_utility
example_write_report
exec-program
exit-code
exit-request
FastCGI_client
FastCGI
file-position
file_storage
file_uploading
finish-output
flush-output
get-app
get-cookie
get-hash
get-req
get-sys
get-time
getting_URL
global_process_data
global_request_data
hash-string
how_vely_works
inline_code
input-param
json-utf8
license
lock-file
lower-string
manage-memory
match-regex
memory_handling
new-fifo
new-hash
new-json
new-mem
non_request
normalized_URL
on-error
open-file
out-header
output_statement
p-dbl
pf-out
pf-url
pf-web
plain_C_FCGI
p-num
p-out
p-path
prepared_statements
purge-fifo
purge-hash
p-url
p-web
quality_control
query-result
random-crypto
random-string
read-fifo
read-file
read-hash
read-json
read-line
rename-file
rename_files
report-error
request-body
request
request_URL
resize-hash
resize-mem
rewind-fifo
rollback-transaction
run-query
SELinux
send-file
set-app
set-cookie
set-input
set-req
silent-header
split-string
startup_handler
statement_APIs
stat-file
syntax_highlighting
temporary_file
trace-run
trim-string
uniq-file
unlock-file
unused-var
upper-string
utf8-json
vely_architecture
vely_dispatch_request
vely_removal
vf
vv
why_C_and_Vely
write-fifo
write-file
write-hash
write-string
123 hello world

This is a condensed version of Vely Hello World that you can run in minutes. No additional software needs to be installed. For more, see examples.
Step 1: Install Vely
First install Vely.
Step 2: Build it
Create Hello World source file (hello.vely); note it's all one bash command:
echo '#include "vely.h"

void hello()
{
    out-header default
    @Hello World!
}' > hello.vely

Create Hello World application:
sudo vf -i -u $(whoami) helloworld

Make Hello World application:
vv -q

Step 3: Run it
You can run Hello World both as a service and from command line:
Expected result
The end result looks like this (notice in bold the responses from your web application and the command line):


Vely

You have run Hello World with Vely, first by talking directly to a FastCGI server and then from command line. See FastCGI_client for a similar example that uses TCP sockets.
See also
Quick start ( 123_hello_world  )  SEE ALL (documentation)
After request handler

Purpose: Execute your code after a request is handled.

void _after ( ) ...

Every Vely request goes through a request dispatcher (i.e. vely_dispatch_request() function). In order to specify your code to execute after a request is handled, create a source file "_after.vely" and implement a function "void _after()", which will be automatically picked up and compiled with your application.

If no request executes (for example if your application does not handle a given request), after-request handler does not execute either. If you use exit-request to exit current request handling, after-request handler still executes.
Examples
Here is a simple implementation of after-request handler that just outputs "Hi there!!":
#include <vely.h>

void _after()
{
     @Hi there!!
}

See also
Requests ( after_request_handler   before_request_handler   building_URL   getting_URL   global_request_data   non_request   normalized_URL   request   request_URL   startup_handler   vely_dispatch_request  )  SEE ALL (documentation)
Application setup

Initialize application
A Vely application must be initialized first. This means creating a directory structure owned by application owner, which can be any Operating System user. To initialize application <app name> while logged-in as application owner:
sudo vf -i -u $(whoami) <app name>

Setup database(s)
If your application does not use database(s), you can skip this part.

You can setup your database(s) in any way you see fit, and this includes creating the database objects (such as tables or indexes) used by your application; all Vely needs to know is the connection parameters, which include database login information (but can include other things as well). For each database in use, you must provide database_config_file along with your source code. This file contains the database connection parameters - these parameters are database-specific. For example, if your code has statements like:
run-query @mydb = ...

or

begin transaction @sales_db

then you must have files "mydb" and "sales_db" present. For example, MariaDB config file might look like:
[client]
user=velyuser
password=pwd
database=velydb
protocol=TCP
host=127.0.0.1
port=3306

or for PostgreSQL:
user=myuser password=mypwd dbname=mydb

Make application
To compile and link the application that doesn't use database(s):
vv -q

When you have database(s) in use, for instance assuming in above example that "mydb" is MariaDB database, "sales_db" is PostgreSQL, and "contacts" is SQLite database:
vv -q --db="mariadb:mydb postgres:sales_db sqlite:contacts"

See vv for more options.
Start application
Stop the application first in case it was running, then start the application:
vf -m quit <app name>
vf <app name>

See vf for more details.
Running application
You can run your application as FastCGI, CGI or command_line.
See also
Running application ( application_setup   CGI   Client_API   command_line   containerize_application   FastCGI   FastCGI_client   plain_C_FCGI  )  SEE ALL (documentation)
Before request handler

Purpose: Execute your code before a request is handled.

void _before ( ) ...

Every Vely request goes through a request dispatcher (i.e. vely_dispatch_request() function), which is auto-generated. In order to specify your code to execute before a request is handled, create a source file "_before.vely" and implement a function "void _before()", which will be automatically picked up and compiled with your application.

If no request executes (for example if your application does not handle a given request), before-request handler does not execute either.
Examples
Here is a simple implementation of before-request trigger that just outputs "Getting Started!!":
#include <vely.h>

void _before()
{
    out-header default
    @Getting Started!!
}

See also
Requests ( after_request_handler   before_request_handler   building_URL   getting_URL   global_request_data   non_request   normalized_URL   request   request_URL   startup_handler   vely_dispatch_request  )  SEE ALL (documentation)
begin-transaction

Purpose: Begins database transaction.

begin-transaction [ <options> ] [ @<database> ] [ on-error-continue | on-error-exit ] [ error [ define ] <error> ] [ error-text [ define ] <error text> ]

This statement begins a database transaction. <options> is any additional options to database's BEGIN/START you wish to supply and must immediately follow begin-transaction.

Once you start a transaction with begin-transaction, you must either commit it with commit-transaction or rollback with rollback-transaction. If you do neither, your transaction will be rolled back once the request has completed and your program will stop with an error message. This is because opening a transaction and leaving without committing or a rollback is a bug in your program.  

You must use begin-transaction, commit-transaction and rollback-transaction instead of calling the BEGIN/COMMIT/END through run-query.
Database
Optional <database> is specified in "@" clause and is the name of the database_config_file.
Error handling
The error code is available in  <error> variable in optional "error" clause - this code is always "0" if successful. The <error> code may or may not be a number but is always returned as a string value. <error> is allocated memory. In case of error, error text is available in optional "error-text" clause in <error text>, which is allocated memory.

"on-error-continue" clause specifies that request processing will continue in case of error, whereas "on-error-exit" clause specifies that it will exit. This setting overrides database-level on-error for this specific statement only. If you use "on-error-continue", be sure to check the error code.

<error> and <error text> can be created with optional "define".

Note that if database connection was lost, and could not be reestablished, the request will error out (see error_handling).
Examples
begin-transaction @mydb
run-query @mydb="insert into employee (name, dateOfHire) values ('%s', now())" : "Terry" no-loop
commit-transaction @mydb

See also
Database ( begin-transaction   commit-transaction   current-row   database_config_file   database_queries   delete-query   on-error   prepared_statements   query-result   rollback-transaction   run-query  )  SEE ALL (documentation)
Building URL

Building absolute URLs
Use p-path to create the absolute URL path to refer back to your application so you can issue requests to it.

For example, this is a link that specifies request "show-notes":
@<a href="<<p-path>>/show-notes?date=yesterday">Show Notes</a>


If you are building HTML forms, you can add a note with:
@<form action="<<p-path>>/add-note" method="POST">
@<input type="text" name="note" value="">
@</form>

See request_URL for more on URL structure.
See also
Requests ( after_request_handler   before_request_handler   building_URL   getting_URL   global_request_data   non_request   normalized_URL   request   request_URL   startup_handler   vely_dispatch_request  )  SEE ALL (documentation)
call-web

Purpose: Get content of URL resource (call a web address).

call-web <URL> \
    response [ define ] <result> \
    [ response-code [ define ] <response code> ] \
    [ response-headers [ define ] <headers> ] \
    [ status [ define ] <status> ] \
    [ method <request method> ] \
    [ request-headers [ content-type <content type> ] \
        custom <header name>=<header value> [ , ... ] ] \
    [ request-body \
        ( [ fields <field name>=<field value> [ , ... ] ] \
            [ files <file name>=<file location> [ , ... ] ] ) \
        | \
        ( payload <body content> [ payload-length <content length> ] ) \
    ] \
    [ error [ define ] <error> ] \
    [ cert <certificate> | no-cert ] \
    [ cookie-jar <cookie jar> ] \
    [ timeout <timeout> ]

With call-web, you can get the content of any accessible URL resource, for example web page, image, PDF document, XML document, REST API etc. It allows you to programmatically download URL's content, including the header. For instance, you might want to obtain (i.e. download) the source code of a web page and its HTTP headers. You can then save such downloaded items into files, analyze them, or do anything else.

<URL> is the resource locator, for example "https://some.web.page.com" or if you are downloading an image (for instance) it could be "https://web.page.com/image.jpg". Anything you can access from a client (such as web browser), you can also obtain programmatically. You can specify any URL parameters, for example "https://some.web.page.com?par1=val1&par2=val2".
Response and headers
The result is obtained via "response" clause into variable <result>, and the length (in bytes) of such response is obtained via "status" clause in <status> variable. <result> is allocated memory.

The response code (such as 200 for "OK", 404 for "Not Found" etc.) is available via optional "response-code" clause in number variable <response code>; the default value is 0 if response code is unavailable (due to error for instance).

The optional "response-headers" clause allows for retrieval of response headers (such as HTTP headers) in <headers> variable, as a single string variable. <headers> is allocated memory.

Each of <result>, <response code>, <headers> and <status> can be created if they don't exist with the corresponding "define".
Request method
You can specify the request method using "method" clause. <method> has a string value of the request method, such as "GET", "POST", "PUT", "PATCH", "DELETE" or any other.
Status
In case of error, <status> is negative, and has value of VV_ERR_FAILED (typically indicating system issue, such as lack of memory, library or system issue or local permissions), VV_ERR_WEB_CALL (error in accessing URL or obtaining data) - otherwise <status> is the length in bytes of the response (0 or positive). Optionally, you can obtain the error message (if any) via "error" clause in <error> variable (which can also be created with "define" if it doesn't exist). Error is an empty string ("") if there is no error. <error> is allocated memory.
Timeout
If "timeout" clause is specified, call-web will timeout if operation has not completed within <timeout> seconds. If this clause is not specified, the default timeout is 120 seconds. If timeout occurs, <status> will be VV_ERR_WEB_CALL and <error> will indicate timeout. Timeout cannot be negative nor greater than 86400 seconds.
HTTPS and certificates
You can call any valid URL that uses protocol supported by the underlying library (cURL). If you're using https protocol (or any other that requires a SSL/TSL certificate), you can either use the local CA (certificate authority) issued, specify the location of a certificate with "cert" clause, or if you do not want it checked, use "no-cert". By default, the locally installed certificates are used; if the URL you are visiting is not trusted via those certificates, and you still want to visit it, use "no-cert"; and if you do have a no-CA (i.e. self-signed certificate) for that URL, use "cert" to provide it as a file name (either a full path or a name relative to current working directory, see how_vely_works).
Cookies
If you'd like to obtain cookies (for example to maintain session or examine their values), use "cookie-jar" clause. <cookie jar> specifies the location of a file holding cookies. Cookies are read from this file (which can be empty or non-existent to begin with) before making a call-web and any changes to cookies are reflected in this file after the call. This way, multiple calls to the same server maintain cookies the same way browser would do. Make sure the same <cookie jar> file is not used across different application spaces, meaning it should be under the application home directory (see how_vely_works), which is the most likely method of implementation.
Binary result
The result of call-web (which is <result>) can be a text value or a binary value. If it is a binary value (for example if getting "JPG", "PNG", "PDF" or other documents), then <status> is the number of bytes in a buffer that holds the value. If it is a string value (such as if downloading "HTML" document as a text), then <status> is the string length.
Request body, sending files and arbitrary content
In order to include request body, for instance to send files, use "request-body" clause. Request body is typically used with POST, PUT or PATCH methods. Even though not common, you can also use it with GET, DELETE or any other custom method, such as for example if the resource you wish to identify requires binary data; perhaps a disposable image is used to identify the resource.

- Structured content
Use "fields" and/or "files" subclauses to send a structured body request in the form of name/value pairs, the same as sent from an HTML form. To do that, you can specify fields with "fields" subclause in the form of <field name>=<field value> pairs separated by a comma. For instance, here two fields are set (field "source" with value "web" and field "act" with value "setcookie"):
call-web "http://website.com/app_name/some_request" response resp response-code rc status len \
    request-body fields "source"="web","act"="setcookie"

To include files, use "files" subclause in the form of <file name>=<file location> separated by commas. For example, here "file1" is the file name sent to the client (which can be anything), and local file "uploadtest.jpg" is the file whose contents is sent; and "file23" is the file name sent to the client (which can be anything),  and "fileup4.pdf" is the actual local file read and sent to the client. In this case files are in the application home directory (see how_vely_works), but in general you can specify a relative or absolute path:
call-web "http://website.com" response resp response-code rc status len \
    request-body files "file1"="uploadtest.jpg", "file23"="fileup4.pdf"

You can specify both "files" and "fields" fields, for instance (along with getting error text and status):
call-web "http://website.com/app_name/some_request" response resp response-code rc status len
    request-body fields "source"="web","act"="setcookie" \
        files "file1"="uploadtest.jpg", "file23"="fileup4.pdf" \
    status define st error err

You can specify a maximum of 1000 files and fields.

- Non-structured content
To send any arbitrary (non-structured) content in the request body, such as JSON text for example, use "payload" subclause:
call-web "https://website.com" response resp \
    request-headers content-type "application/json" \
    request-body payload "{ \
        \"employee\": { \
            \"name\":       \"sonoo\", \
            \"salary\":      56000, \
            \"married\":    true \
        } \
    }"

Optional "payload-length" subclause can be specified to denote the length of body content:
read-file "somefile" to define file_contents status define file_length
call-web "https://website.com" response resp \
    request-body payload file_contents \
    payload-length file_length

If "payload-length" is not used, then it is assumed to be the length of string <payload>.
Request headers
If your request has a body (i.e. "request-body" clause is used), you can set the content type with "content-type" subclause of a request-headers clause:
call-web "https://<web address>/resource" request-headers \
    content-type "application/json" \
    request-body payload some.json

Note that using "content-type" without the request body may be ignored by the server processing your request or may cause it to consider the request invalid. If "content-type" is not used, the default is generally "multipart/form-data" if "fields" or "files" subclause(s) are used with "body-request" clause, or "application/x-www-form-urlencoded" otherwise. Thus, if you use "payload" subclause to send other types of data, you should set content type explicitly.

You can also specify custom request headers with "request-headers" clause, using "custom" subclause with a list of <header name>=<header value> pairs separated by a comma. For example, here custom header "Vely-header" has value of "Some_ID", and "Another-Header" a value of "New_ID":
call-web "http://website.com/<app name>/<request name>?act=get_file" response resp response-code rc status len \
    request-headers custom "Vely-header"="Some_ID", "Another-Header"="New_ID"

On the receiving side you can get any such custom header by using "header" clause of the get-req statement:
get-req header "Vely-header" to define hvh0
get-req header "Another-Header" to define hvh1

Examples
Get the web page and print it out:
call-web "https://website.com/page.html" response define resp
p-out resp

Get the "JPG" image from the web and save it to a file "pic.jpg":
call-web "https://website.com/images/someimg.jpg" status define wlen response define resp
write-file "pic.jpg" from resp length wlen

See also
Web ( call-web   out-header   send-file   silent-header  )  SEE ALL (documentation)
CGI

You can run Vely application as a CGI (Common Gateway Interface) program, if your web server supports CGI. This is not recommended in general, as CGI programs do not exhibit great performance. However in some cases you may need to use CGI, such as when performance is not of critical importance.

To run your application with CGI, use Vely command_line program. Since Vely applications require running in the security context of the user who owns the application, you must use "suexec" (or similar feature) of your web server.

The following script sets up an application named "func_test" (any kind of application will do) to run as CGI (after it's been compiled with vv) on Apache web server running on Ubuntu 18 and up. For other web servers/distros, consult their documentation on how to setup CGI for a program.
#prep repos
    sudo apt update

#enable CGI on apache
    sudo a2enmod cgid
    sudo service apache2 restart

#Install suexec-custom for Apache
    sudo apt-get -y install apache2-suexec-custom
    sudo a2enmod suexec
    sudo service apache2 restart

#setup a "vv" directory under cgi-bin where your application can run
    sudo mkdir -p /usr/lib/cgi-bin/vv
    sudo chown $(whoami):$(whoami)  /usr/lib/cgi-bin/vv
    sudo sed -i '1c\/usr/lib/cgi-bin/vv' /etc/apache2/suexec/www-data

#copy your program to "vv" directory
    sudo mv /var/lib/vv/bld/func_test/func_test /usr/lib/cgi-bin/vv
    sudo chown $(whoami):$(whoami)  /usr/lib/cgi-bin/vv/func_test
    sudo chmod 700  /usr/lib/cgi-bin/vv/func_test

#add user/group of Vely application user to suexec    
    sudo sed -i "/SuexecUserGroup/d" /etc/apache2/sites-enabled/000-default.conf
    sudo sed -i "s/<\/VirtualHost>/SuexecUserGroup $(whoami) $(whoami)\n<\/VirtualHost>/g" /etc/apache2/sites-enabled/000-default.conf
    sudo service apache2 restart

#the application is at http://127.0.0.1/cgi-bin/vv/func_test?...
#substitute 127.0.0.1 for your web address

See also
Running application ( application_setup   CGI   Client_API   command_line   containerize_application   FastCGI   FastCGI_client   plain_C_FCGI  )  SEE ALL (documentation)
Client API

You can use Vely C API client library to connect to a FastCGI application server, including Vely:
See Examples section below for detailed examples.
Sending a request
int vv_fc_request (vv_fc *req);

All input and output parameters are contained in a single variable of type "vv_fc", the pointer to which is passed to "vv_fc_request()" function that runs a server request. This also has the advantage of forward compatibility because any future changes are contained in this type, and is also simple to use.

A variable of type "vv_fc" must be initialized to zero before using (such as with {0} initialization, "memset()" or "calloc()"), or otherwise some of its members may have random values:
// Define and initialize request variable
vv_fc req = {0};
// You could also do:
// memset ((char*)&req, 0, sizeof(vv_fc));
...
int result = vv_fc_request (&req);

- Return value
The following are possible return values from "vv_fc_request()":

- Server reply
The server replies with data that is split in two. One part is the actual result of processing (called "stdout" or standard output), and the other is the error messages (called "stderr" or standard error).

All Vely output goes to "stdout", except from report-error and pf-out/pf-url/pf-web (with "to-error" clause) which goes to "stderr". The two outputs can be comingled in any way; the client will properly separate the two and present them as two messages in reply.

You can obtain server reply when it's ready in its entirety (likely most often used), or as it comes alone bit by bit (see more about asynchronous hooks futher here).
Request type (vv_fc)
Type "vv_fc" is defined as:
typedef struct {
    const char *fcgi_server; // the IP:port/socket_path to server
    const char *req_method; // request method
    const char *app_path; // application path
    const char *req; // request name
    const char *url_payload; // URL payload (path+query string)
    const char *content_type; // content type
    int content_len; // content len
    const char *req_body; // request body (i.e. content)
    char **env; // environment to pass into request on server side
    int timeout; // timeout for request
    int req_status; // status of request from server
    char *data; // actual response from server
    int data_len; // length of response from server
    char *error; // error message from server
    int error_len; // length of error from server
    // other elements used internally by Vely client
    vv_fc_out_hook out_hook; // hook to get output data as soon as it arrives
    vv_fc_err_hook err_hook; // hook get error data as soon as it arrives
} vv_fc;

Here is the more detailed explanation:

"fcgi_server" represents either a Unix socket or a TCP socket and is:
"req_method" is a request method, such as GET, POST, PUT, DELETE or any other. Request method must be specified.

"app_path" is an application path (see request_URL). By default it's "/<application name>". Application path must be specified.

"req" is the request name preceded by a forward slash, as in "/<request name>" (see request_URL). Request name must be specified.

"url_payload" is the input parameters (as path segments and query string, see request_URL). URL payload can be NULL or empty, in which case it is not used.

"content_type" is the content type of request body (for instance it may be "application/json" or "image/jpg").

"content_len" is the length of request body in bytes.

"req_body" is the request body, which can be any text or binary data of length "content len". A request body is sent only if content type and request body are not NULL and not empty and content length is greater than zero.

"env" is any environment variables that should be passed along to the server. You can access those in Vely via "web-environment" clause of get-sys statement. This is an array of strings, where name/value pairs are specified one after the other, and which always must end with NULL. For example, if you want to use variable "REMOTE_USER" with value "John" and variable "MY_VARIABLE" with value "8000", then it might look like this:
char *env[5];
env[0] = "REMOTE_USER";
env[1] = "John"
env[2] = "MY_VARIABLE";
env[3] = "8000"
env[4] = NULL;

Thus, if you are passing N environment variables to the server, you must size "env" as "char*" array with 2*N+1 elements.

"timeout" is the number of seconds a request should not exceed. For instance if the remote server is taking too long or if the network connection is too slow, you can limit how long a request may take. If there is no timeout, then "timeout" value should be zero. Note that DNS resolution of the host name (in case you are using a TCP socket) is not counted in timeout. Maximum value for timeout is 86400.

"req_status" is the return status of server execution. This is a part of FastCGI specification and the particular server software you are using may or may not return the status. Vely server request returns status by means of exit-code statement.

"data" is the actual response from server as passed to stdout stream. In Vely, any output will go to "data", except when "to-error" clause is used in pf-out, pf-url and pf-web - use these constructs to output errors without stopping the request's execution. Additionaly, the output of report-error will also not go to "data".

"data_len" is the length of "data" response from server in bytes. The response is always null-terminated as a courtesy, and "data_len" does not include the terminating null byte.

"error" is any error messages from a server response, i.e. data passed to stderr stream. It is comprised of any output when "to-error" clause is used in pf-out, pf-url and pf-web, as well as any output from report-error.

"error_len" is the length of "error" response from server in bytes. The response is always null-terminated as a courtesy, and "data_len" does not include the terminating null byte.

Note that stdout and stderr streams can be co-mingled, but the client will obtain them separately. This allows for clean separation of output from any error messages.
Getting data reply (stdout)
See "data" and "data_len" members of "vv_fc" type above.
Getting error reply (stderr)
See "error" and "error_len" members of "vv_fc" type above.
Getting the exit status of request
See "req_status" member of "vv_fc" type above.
Freeing the results of a request
Once you have obtained the results of a request, and when no longer needed, you should free them:
// Declare and initialize request variable
vv_fc req = {0};
// Setup the req variable
...
// Execute request
vv_fc_request (&req);
// Free request output (data and error streams)
free (req.data);
free (req.error);

If you do not free the results of a request, your program may experience a memory leak. If your program exits right after issuing any request(s), you may skip freeing results as that is automatically done on exit by the Operating System. If you wish to save the results of multiple executions and process them later as a group, you can omit freeing them and save "req.data" (and/or "req.error") in some kind of an array for later processing; this way you do not have to copy the results unnecessarily.
Asynchronous hooks
You can obtain the server's reply as it arrives by specifying read hooks. This is useful if the server supplies partial replies over a period of time, and your application can get those partial replies as they become available.

To specify a hook for output data (i.e. from stdout), you must write a C function with the following signature:
typedef void (*vv_fc_out_hook)(char *recv, int recv_len);

To specify a hook for error data (i.e. from stderr), you must write a C function with the following signature:
typedef void (*vv_fc_err_hook)(char *recv, int recv_len);

"recv" is the data received and "recv_len" is its length. To register these functions with "vv_fc_request()" function, assign their pointers to "out_hook" and "err_hook" members of request variable of type "vv_fc". Note that the output hook (i.e. hook function of type "vv_fc_out_hook") will receive empty string ("") in "recv" and "recv_len" will be 0 when the request has completed, meaning all data (including error) has been received.

For example, functions "get_output()" and "get_err()" will capture data as it arrives and print it out:
// Output hook
void get_output(char *d, int l)
{
    printf("Got output of [%.*s] of length [%d]", l, d, l);
}

// Error hook
void get_err(char *d, int l)
{
    printf("Got error of [%.*s] of length [%d]", l, d, l);
}

...

vv_fc req = {0};
...
// Register output and error hooks
req.out_hook = &get_out;
req.err_hook = &get_err;

Multithreading
The FastCGI client is MT-safe, meaning you can use it both in single-threaded and multi-threaded programs. Note that each thread must have its own copy of "vv_fc" request variable, since it provides both input and output parameters to a request call and as such cannot be shared between the threads.
Examples
- Simple example
The following example is a simple demonstration, with minimum of options used. Copy the C code to file "cli.c":
#include "vfcgi.h"

void main ()
{
  // Request type vv_fc - create a request variable and zero it out
  vv_fc req = {0};

  req.fcgi_server = "/var/lib/vv/helloworld/sock/sock"; // Unix socket
  req.req_method = "GET"; // GET HTTP method
  req.app_path = "/helloworld"; // application path
  req.req = "/hello-simple"; // request name

  // Make a request
  int res = vv_fc_request (&req);

  // Check return status, and if there's an error, display error code and the
  // corresponding error message. Otherwise, print out server response.
  if (res != VV_OKAY) printf ("Request failed [%d] [%s]\n", res, req.errm);
  else printf ("%s", req.data);

  // Free up resources so there are no memory leaks
  free (req.data);
  free (req.error);
}

To make this client application:
gcc -o cli cli.c -g $(vv -i)

In this case, you're using a Unix socket to communicate with the FastCGI server. To test with a Vely request handler, copy the following code to "hello_simple.vely" file:
#include "vely.h"
void hello_simple()
{
   silent-header
   out-header default

   @Hi there!
}

Create and make the Vely server application and run it via local Unix socket:
sudo vf -i -u $(whoami) helloworld
vv -q
vf -m quit helloworld
vf -w 1 helloworld

Run the FastCGI client:
./cli

The output is, as expected:
Hi there!

- Example with more options
This example demonstrate using multiple options, including using TCP sockets connecting to a host and port number, environment variables, query string, request body and request execution timeout. It will also show the separation of "data" and "error" (i.e. stdout and stderr) streams from the server.

Copy this to file "cli1.c" - note that in this example FastCGI server will run on localhost (127.0.0.1) and TCP port 2301:
#include "vfcgi.h"

void main ()
{
   // Request type vv_fc - create a request variable
   vv_fc req;

   // Add 3 environment variables (in the form of name, value, name, value, ... , NULL)
   char *env[] = { "REMOTE_USER", "John", "SOME_VAR", "SOME\nVALUE", "NEW_VAR", "1000", NULL };

   // Create a request
   // Environment variables to pass to server request
   req.env = env;
   // Server IP and port
   req.fcgi_server = "127.0.0.1:2301";
   // Request method
   req.req_method = "GET";
   // Application path
   req.app_path = "/helloworld";
   // Request
   req.req = "/hello";
   // URL payload (path and query string)
   req.url_payload = "par1=val1&par2=91";
   // Content type
   req.content_type = "application/json";
   // Content (i.e. request body)
   req.req_body = "This is request body";
   // Content length
   req.content_len = strlen (req.req_body);
   // No timeout (set to 0)
   req.timeout = 0;

   // Make a request
   int res = vv_fc_request (&req);

   // Check return status, and if there's an error, display error code and the
   // corresponding error message
   if (res != VV_OKAY) printf("Request failed [%d] [%s]\n", res, req.errm);
   else {
       // If successful, display request results
       // Exit code from the server processing
       printf("Server status %d\n", req.req_status);
       // Length of reply from server
       printf("Len of data %d\n", req.data_len);
       // Length of any error from server
       printf("Len of error %d\n", req.error_len);
       // Reply from server
       printf("Data [%s]\n", req.data);
       // Any error message from server
       printf("Error [%s]\n", req.error);
   }

   // Free up resources so there are no memory leaks
  free (req.data);
  free (req.error);
}

Note that the URL payload (i.e. "req.url_payload") could have been written as a combination of a path segment and query string (see request_URL):
req.url_payload = "/par1/val1?par2=91";

or just as a path segment:
req.url_payload = "/par1/val1/par2/91";

To make this client application:
gcc -o cli1 cli1.c -g $(vv -i)

To test it, you can create a Vely server application. Copy this to "hello.vely" file in a separate directory:
#include "vely.h"

void hello()
{
    silent-header
    out-header default

    // Get request body
    request-body rb

    // Get environment variables passed on from the client
    get-sys web-environment "REMOTE_USER" to define ruser
    get-sys web-environment "SOME_VAR" to define somev
    get-sys web-environment "NEW_VAR" to define newv

    // Output, print the environment variables, the PID of server process and the request body received from the client
    @Hello World! [<<p-out ruser>>] [<<p-out somev>>] [<<p-out newv>>] [<<p-out newv>>] [<<p-out par1>>] <<pf-out "%d", getpid()>> <<p-out rb>>

    // Output back a number of lines, generally as "Output line #<num of line>"
    // After line #1418, print out "Line 1419 has an error" to stderr
    // After line #4418, report an error and exit
    // This demostrates outputting data to both stdout and stderr
    num i;
    for (i = 0; i < 8000; i++) {
        @Output line #<<p-num i>>
        if (i == 1419) {
            pf-out to-error "Line %lld has an error\n", i
        }
        if (i == 4419) {
            // Exit code of the server execution
            exit-code 82
            report-error "%s", "Some error!"
        }
    }
}

Create and make the Vely server application and run it on local TCP port 2301 to match the client above:
sudo vf -i -u $(whoami) helloworld
vv -q
vf -m quit helloworld
vf -w 1 -p 2301 helloworld

Run the FastCGI client:
./cli1

The output:
Server status 82
Len of data 78517
Len of error 35
Data [Hello World! [John] [SOME
VALUE] [val1] [91] [1000] 11187 This is request body
Output line #0
Output line #1
Output line #2
Output line #3
Output line #4
Output line #5
Output line #6
Output line #7
...
Output line #4413
Output line #4414
Output line #4415
Output line #4416
Output line #4417
Output line #4418
Output line #4419
]
Error [Line 1419 has an error
Some error!
]

The output shows server exit code (82, see exit-code in the Vely code above), length of output data, the output data which includes environment variables passed to the server from client, the PID of server process, the request body from the client, and then the error output. Note that the data output (stdout) and the error output (stderr) are separated, since FastCGI protocol does use separate streams over the same connection. This makes working with the output easy, while the data transfer is fast at the same time.
See also
Running application ( application_setup   CGI   Client_API   command_line   containerize_application   FastCGI   FastCGI_client   plain_C_FCGI  )  SEE ALL (documentation)
close-file

Purpose: Close file.

close-file file-id <file id> \
    [ status [ define ] <status> ]

close-file closes file <file id> previously opened with open-file, where <file id> is an open file identifier.

You can obtain the status of file closing via optional <status> (in "status" clause), which can be created with optional "define". The <status> is VV_OKAY if file is closed, or VV_ERR_CLOSE if could not close file.

If you do not close a file opened with open-file, Vely will automatically close it when the request ends.
Examples
See open-file.
See also
Files ( close-file   copy-file   delete-file   file-position   file_storage   file_uploading   lock-file   open-file   read-file   read-line   rename-file   stat-file   temporary_file   uniq-file   unlock-file   write-file  )  SEE ALL (documentation)
Command line

Command-line programs
A Vely application can run as a web application or a command line program, or both - such as when some requests can be either fulfilled through web interface or the command line. Note that Vely produces two separate executables: a FastCGI server one and a command-line one - they are different because command-line program does not need the FastCGI library and thus is smaller.

The name of the command-line executable is the same as the application name, and its path is (assuming <app name> is the application name):
/var/lib/vv/bld/<app name>/<app name>

Output
A command line program works the same way as a FastCGI executable, and the output is the same, except that it is directed to stdout (standard output) and stderr (standard error).
Exit code
To specify the exit code of a command line program, use exit-code. To exit the program, use exit-request, or otherwise the program will exit when it reaches the end of a request.
Executing a request
A command line program must obtain its request URL via environment variables, for example if the application name is "stock", here is how to execute a request "add-stock" with input-parameters "name" having a value of "ABC" and "price" a value of "300":
export REQUEST_METHOD=GET
export SCRIPT_NAME="/stock"
export PATH_INFO="/add-stock"
export QUERY_STRING="name=ABC&price=300"

/var/lib/vv/bld/stock/stock

Note that you if specify parameters as part of the path, you could write the above the same way as in a URL:
export REQUEST_METHOD=GET
export SCRIPT_NAME="/stock"
export PATH_INFO="/add-stock/name/ABC"
export QUERY_STRING="price=300"

/var/lib/vv/bld/stock/stock

Including a request body
You can include a request body when executing a command-line program. It is always included as the standard input (stdin) to the program.

You must provide the length of this input (as CONTENT_LENGTH environment variable), the type of input (as CONTENT_TYPE), as well as REQUEST_METHOD (such as POST, PUT, PATCH, GET, DELETE or any other).

Here is an example of using a request body to make a POST request on the command line - the application name is "json" and request name is "process". File "prices.json" is sent as request body:
#length of the request body (of file prices.json). Use ${#VAR} for length if the content is in bash variable $VAR. 
export CONTENT_LENGTH=$(stat -c%s prices.json)

#content type (in this case application/json)
export CONTENT_TYPE="application/json"

#state the URL
export SCRIPT_NAME="/json"
export PATH_INFO="/process"
export QUERY_STRING="act=get_total&period=YTD"

#request method (POST, PUT, PATCH, GET, DELETE...)
export REQUEST_METHOD=POST

#provide request body as the standard input to the command-line program by piping a file into it
cat prices.json | /var/lib/vv/bld/ordering/ordering

Note that you can also include any other headers as environment variables by using the "HTTP_" convention, where custom headers are capitalized with use of underscore for dashes and prefixed with "HTTP_", for example header "Vely-Header" would be set as:
export HTTP_VELY_HEADER="some value"

Suppressing HTTP header output for the entire application
If you wish to suppress the output of HTTP headers for all requests, set environment variable VV_SILENT_HEADER to "yes" before executing the program:
export VV_SILENT_HEADER=yes

This will suppress the effect of out-header, or for any other case where headers are output. This has the same effect as silent-header, the only difference is that it applies to the entire application.
URL-encoding the input
Any data in QUERY_STRING or PATH_INFO must be formatted to be a valid URL; for example, data that contains special characters (like "&" or "?") must be URL-encoded, for instance:
export QUERY_STRING="action=show&data=a%3Fb"

In this case, field "data" has value of "a?b", where special character "?" is encoded as "%3F".

To make sure all your input parameters are properly URL-encoded, use Vely's v1 code processor:
$($(vv -l)/v1 -urlencode '<your data>')

For instance, to encode "a?=b" as a parameter:
export QUERY_STRING="act=show&data=$($(vv -l)/v1 -urlencode 'a?=b')"

CGI
You can also use a command line program with CGI (Common Gateway Interface). Note that CGI programs generally exhibit much lower performance; use CGI only when warranted by a specific situation.
See also
Running application ( application_setup   CGI   Client_API   command_line   containerize_application   FastCGI   FastCGI_client   plain_C_FCGI  )  SEE ALL (documentation)
commit-transaction

Purpose: Commits a database transaction.

commit-transaction [ @<database> ] [ on-error-continue | on-error-exit ] [ error [ define ] <error> ] [ error-text [ define ] <error text> ]

Database transaction started with begin-transaction is committed with commit-transaction.
Database
Optional <database> is specified in "@" clause and is the name of the database_config_file.
Error handling
The error code is available in  <error> variable in optional "error" clause - this code is always "0" if successful. The <error> code may or may not be a number but is always returned as a string value. <error> is allocated memory. In case of error, error text is available in optional "error-text" clause in <error text>, which is allocated memory.

"on-error-continue" clause specifies that request processing will continue in case of error, whereas "on-error-exit" clause specifies that it will exit. This setting overrides database-level on-error for this specific statement only. If you use "on-error-continue", be sure to check the error code.

<error> and <error text> can be created with optional "define".

Note that if database connection was lost, and could not be reestablished, the request will error out (see error_handling).
Examples
begin-transaction @mydb
run-query @mydb="insert into employee (name, dateOfHire) values ('Terry', now())"
run-query @mydb="insert into payroll (name, salary) values ('Terry', 100000)"
commit-transaction @mydb

See also
Database ( begin-transaction   commit-transaction   current-row   database_config_file   database_queries   delete-query   on-error   prepared_statements   query-result   rollback-transaction   run-query  )  SEE ALL (documentation)
Connect apache tcp socket

This shows how to connect your application listening at TCP port <port number> (started with "-p" option in vf) to Apache web server.

- Step 1:
To setup Apache as a reverse proxy and connect your application to it, you need to enable FastCGI proxy support, which generally means "proxy" and "proxy_fcgi" modules - this is done only once:
- Step 2:
Edit the Apache configuration file:
Add this to the end of file ("/<app path>" is the application path, see request_URL):
ProxyPass "/<app path>" fcgi://127.0.0.1:<port number>/

- Step 3:
Finally, restart Apache. On Debian systems (like Ubuntu) or OpenSUSE:
sudo systemctl restart apache2

On Fedora systems (like RedHat) and Arch Linux:
sudo systemctl restart httpd

Note: you must not have any other URL resource that starts with "/<app path>" (such as for example "/<app path>.html" or "/<app path>_something" etc.) as the web server will attempt to pass them as a reverse proxy request, and they will likely not work. If you need to, you can change the application path to be different from "/<app path>", see request_URL.
See also
Web servers ( connect_apache_tcp_socket   connect_apache_unix_socket   connect_nginx_tcp_socket   connect_nginx_unix_socket  )  SEE ALL (documentation)
Connect apache unix socket

This shows how to connect your application listening on a Unix socket (started with vf) to Apache web server.

- Step 1:
To setup Apache as a reverse proxy and connect your application to it, you need to enable FastCGI proxy support, which generally means "proxy" and "proxy_fcgi" modules - this is done only once:
- Step 2:
Edit the Apache configuration file:
Add this to the end of file ("/<app path>" is the application path (see request_URL) and "<app name>" is your application name):
ProxyPass "/<app path>" unix:///var/lib/vv/<app name>/sock/sock|fcgi://localhost/<app name>

- Step 3:
Finally, restart Apache. On Debian systems (like Ubuntu) or OpenSUSE:
sudo systemctl restart apache2

On Fedora systems (like RedHat) and Arch Linux:
sudo systemctl restart httpd

Note: you must not have any other URL resource that starts with "/<app path>" (such as for example "/<app path>.html" or "/<app path>_something" etc.) as the web server will attempt to pass them as a reverse proxy request, and they will likely not work. If you need to, you can change the application path to be different from "/<app path>", see request_URL.
See also
Web servers ( connect_apache_tcp_socket   connect_apache_unix_socket   connect_nginx_tcp_socket   connect_nginx_unix_socket  )  SEE ALL (documentation)
Connect nginx tcp socket

This shows how to connect your application listening at TCP port <port number> (started with "-p" option in vf) to Nginx web server.

- Step 1:
You will need to edit the Nginx configuration file. For Ubuntu and similar:
sudo vi /etc/nginx/sites-enabled/default

while on Fedora and other systems it might be at:
sudo vi /etc/nginx/nginx.conf


Add the following in the "server {}" section ("/<app path>" is the application path, see request_URL):
location /<app path> { include /etc/nginx/fastcgi_params; fastcgi_pass  127.0.0.1:<port number>; }

- Step 2:
Finally, restart Nginx:
sudo systemctl restart nginx

Note: you must not have any other URL resource that starts with "/<app path>" (such as for example "/<app path>.html" or "/<app path>_something" etc.) as the web server will attempt to pass them as a reverse proxy request, and they will likely not work. If you need to, you can change the application path to be different from "/<app path>", see request_URL.
See also
Web servers ( connect_apache_tcp_socket   connect_apache_unix_socket   connect_nginx_tcp_socket   connect_nginx_unix_socket  )  SEE ALL (documentation)
Connect nginx unix socket

This shows how to connect your application listening on a Unix socket (started with vf) to Nginx web server.

- Step 1:
You will need to edit the Nginx configuration file. For Ubuntu and similar:
sudo vi /etc/nginx/sites-enabled/default

while on Fedora and other systems it might be at:
sudo vi /etc/nginx/nginx.conf


Add the following in the "server {}" section ("/<app path>" is the application path (see request_URL) and "<app name>" is your application name):
location /<app path> { include /etc/nginx/fastcgi_params; fastcgi_pass  unix:///var/lib/vv/<app name>/sock/sock; }

- Step 2:
Finally, restart Nginx:
sudo systemctl restart nginx

Note: you must not have any other URL resource that starts with "/<app path>" (such as for example "/<app path>.html" or "/<app path>_something" etc.) as the web server will attempt to pass them as a reverse proxy request, and they will likely not work. If you need to, you can change the application path to be different from "/<app path>", see request_URL.
See also
Web servers ( connect_apache_tcp_socket   connect_apache_unix_socket   connect_nginx_tcp_socket   connect_nginx_unix_socket  )  SEE ALL (documentation)
Containerize application

This is a short tutorial on building a base Vely image, and an application image (regardless of what your application does) that can be deployed anywhere, even on non-Linux systems. This is one way to deploy your Vely application. See also example_docker for an example you can run. You can use docker or podman (substitute podman for docker).

The application container has a Vely application installed, and your reverse proxy web server and your databases must be created (see application_setup for more information), as they will likely either run on the host machine, or be containers themselves.

The base image used here is "vely" (created in the previous step). User "vely" is created and assigned limited sudo privileges for Vely only. Source code of your application is in "docker" directory on the host, and copied to the container, where application is compiled and linked, then source code is deleted, making the container ready for deployment (if you wish to keep the source, you certaintly can, just remove the lines that delete it). Finally, the entry point will start application - that's what runs when the container is deployed.

The following shows how to create a deployment image and a subsequent running container. The application name is "velydemo" - change it as needed.
#
#Create base vely image. Install Vely on top of Ubuntu 20.04
#
FROM ubuntu:20.04
ENV TZ=America/Phoenix
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
RUN apt update
RUN apt install -y apt-transport-https ca-certificates wget sudo
RUN wget  -qO - https://vely.dev//pkg/OPENPGP|sudo tee /usr/share/keyrings/vely.asc >/dev/null
ARG arch
RUN sudo bash -c "echo 'deb [signed-by=/usr/share/keyrings/vely.asc] https://vely.dev//pkg/ubuntu_20_$arch/latest ubuntu_20_$arch vely' >/etc/apt/sources.list.d/vely.list"
RUN sudo apt update
RUN sudo apt install -y vely

Create an image with - use --no-cache in order to update the image when new Vely release is available:
sudo docker build --no-cache -t vely .

In some cases your application is stateless, i.e. all the data is through to database(s) and other applications. In other cases, you may want to have file storage that persists even when containers recycle - typically that means persisting Vely directory (which is /var/lib/vv, see how_vely_works). In that case, you can use a container storage option for that purpose; in case of docker, that would be volumes, for instance:
sudo docker volume create velyhome

For example, if your application uploads/downloads files, you may use Vely file_storage (which is under /var/lib/vv); or you may store other files that your application produces. In such cases using a volume (or similar) may be a good idea; in other cases you may want a stateless container and offload such functionality elsewhere (for instance to databases, other applications etc.).

Next is velyapp.dockerfile, which is used to create an image for an application. In this case it is a demo application with source code in "docker" directory (under the current working directory):
#
#Create image that will be used to compile and link an application. Source code is copied from host,
#application is setup, and the source code is deleted (remove this step if you want to ship the source code).
#After that the image is ready to run in a container. Here, the application name is "velydemo"; change it to
#fit your application. The port used in 2300, you can change that as well.
#Customize the ENTRYPOINT command to fit your runtime.
#
FROM vely
#create vely user and give it limited sudo powers
RUN useradd -ms /bin/bash vely && echo "vely:vely" | chpasswd
RUN echo "vely ALL=(ALL) NOPASSWD: /usr/bin/vf" >> /etc/sudoers
#default user
USER vely
#default working dir
WORKDIR /home/vely
EXPOSE 2300
#copy over source code and make app
COPY ./docker/* /home/vely/
#this is to run app with docker run
ENTRYPOINT [ "./runit" ]

The "runit" script will create an application in the container, build it, and start it in the foreground. This script is in the "docker" directory with your application source code:
#!/bin/bash
#Create Vely application and run it in foreground for docker
#demout has a log of execution
sudo vf -i -u $(whoami) velydemo
vv -c
vv -q --db=mariadb:db -s
vv -c
vf -f -w3 -p2300 velydemo > demout

Stop and remove any current containers/images:
sudo docker stop velyapp || true
sudo docker rmi velyapp || true

Build an application image:
sudo docker build --no-cache -t velyapp -f velyapp.dockerfile .

Assuming you have setup web server and any database(s), start the container (using "host" network interface for simplicity):
sudo docker run --init --name velyapp -d  --network="host" --rm velyapp velyapp

If your application uses volumes for data persistence, you might use:
sudo docker run --init --name velyapp -d -v velyhome:/var/lib/vv  --network="host" --rm velyapp

Note that you can persist any storage you want, however at the minimum you would likely persist Vely directory (/var/lib/vv) which is stored on the host under volume "velyhome".

Now you can use your application through web server:
http://127.0.0.1/velydemo/<request name>...

See also
Running application ( application_setup   CGI   Client_API   command_line   containerize_application   FastCGI   FastCGI_client   plain_C_FCGI  )  SEE ALL (documentation)
copy-file

Purpose: Copies one file to another.

copy-file <source file> to <target file> [ status [ define ] <status> ]

File <source file> is copied into <target file>, which is created if it does not exist.

Status can be obtained in <status> variable, which is VV_ERR_OPEN if cannot open source file, VV_ERR_CREATE if cannot create target file, VV_ERR_READ if cannot read source file, VV_ERR_WRITE if cannot write target file, or number of bytes copied (including 0) on success.
Examples
copy-file "/home/user/source_file" to "/home/user/target_file" status define st

See also
Files ( close-file   copy-file   delete-file   file-position   file_storage   file_uploading   lock-file   open-file   read-file   read-line   rename-file   stat-file   temporary_file   uniq-file   unlock-file   write-file  )  SEE ALL (documentation)
copy-string

Purpose: Copies string to another string.

copy-string <source string> to [ define ] <dest string> \
    [ ( length [ define ] <length> ) \
        | ( [ bytes-written [ define ] <bytes written> ) ]

Use copy-string to copy <source string> to <dest string> (which can be created with optional "define"). <dest string> is allocated memory.

Without "length" clause, <source string> is treated as a null-terminated string and the number of bytes to copy is computed as its length. With "length" clause, exactly <length> bytes are copied into <dest string>, regardless of whether there is a null-character within <source string>, i.e. you can copy binary data this way. Regardless of whether "length" clause is used or not, <dest string> will always have a null-character at the end.

If "bytes-written" clause is used, then the number of bytes copied is stored into <bytes written>, which can be created with optional "define". "bytes-written" can be used only if "length" is not used, for obvious reason, as the two are equal otherwise. <bytes written> does not include the null-character placed at the end.

You can copy a string to itself. In this case, the original string remains and the new string points to a copy:
char *str = "original string"; // string to change

char *orig = str; // points to original copy of the string to change

copy-string str to str // make a copy of string to change and assign it to itself

str[0] = 'O'; // change the copy

// Now "str" points to "Original string" 
// and "orig" points to "original string"

Examples
After copy-string below, "my_str" will point to a copy of string "some value", and "bw" will be 10:
char *other_string="some value";
copy-string other_string to define my_str bytes-written define bw

Copy certain number of bytes, the result in "my_str" will be "som":
char *other_string="some value";
copy-string other_string to define my_str length 3

See also
Strings ( copy-string   count-substring   lower-string   split-string   trim-string   upper-string   write-string  )  SEE ALL (documentation)
count-substring

Purpose: Count substrings.

count-substring <substring> in <string> to [ define ] <count> [ case-insensitive [ <case-insensitive> ] ]

count-substring counts the number of occurrences of <substring> in <string> and stores the result in <count> (specified in "to" clause), which can be created with "define" if it does not exist. By default, search is case-sensitive. If you use "case-insensitive" clause without boolean expression <case-insensitive>, or if <case-insensitive> evaluates to true, then the search is case-insensitive.

If <substring> is empty ("") or NULL, <count> is 0.
Examples
In the following example, 1 occurrence will be found after the first count-substring, and 2 after the second (since case insensitive search is used there):
char sub[] = "world";
char str[] = "Hello world and hello World!";

count-substring sub in str to define num_occ
pf-out "Found %lld occurrences!\n", num_occ

count-substring sub in str to num_occ case-insensitive
pf-out "Found %lld occurrences!\n", num_occ

See also
Strings ( copy-string   count-substring   lower-string   split-string   trim-string   upper-string   write-string  )  SEE ALL (documentation)
current-row

Purpose: Get or print out the row number of a current row in the result-set of a query.

current-row [ to [ define ] <current row> ]

Without "to" clause, current-row will print out the current row number. First row is numbered 1. With "to" clause, the row number is stored into variable <current row>, which is created if "define" is used. current-row must be within a run-query loop, and it always refers to the most inner one.
Examples
Display row number before a line with first and last name for each employee:
run-query @mydb="select firstName, lastName from employee"
    @Row #<<current-row>><br/>
    query-result firstName
    @,
    query-result lastName
    @<br/>
end-query

See also
Database ( begin-transaction   commit-transaction   current-row   database_config_file   database_queries   delete-query   on-error   prepared_statements   query-result   rollback-transaction   run-query  )  SEE ALL (documentation)
Database config file

Vely application can use any number of databases, with each specified by a database configuration file. This file provides database name, login and connection settings and preferences.

When making a Vely project, you specify a database vendor and the database configuration file for each database your application uses (see vv), for instance:
vv ... --db="mariadb:db1 postgres:db2 sqlite:db3"  ...

in which case there are three database configuration files (db1, db2 and db3), with db1 being MariaDB, db2 being PostgreSQL and db3 being SQLite database.

You must create a database configuration file for each database your application uses, and this file must be placed with your source code. Such file will be copied to locations specified and used by Vely to connect to database(s) (see how_vely_works).

Each such file contains connection information and authentication to a database, which Vely uses to login. The names of these configuration files are used in queries. There is no limit on how many databases can be used in your application and those from different vendors can be used in the same application.

An example of database configuration file (in this case MariaDB):
[client]
user=mydbuser
password=somepwd
database=mydbname
protocol=TCP
host=127.0.0.1
port=3306

Using in your queries
Database statements that perform queries (such as run-query) must specify the database configuration file used, unless your application uses only a single database. Such configuration is given by "@<database config file>" (for instance in run-query or begin-transaction). For example, in:
run-query @mydb="select name from employees"
...
end-query

the query "myquery" is performed on a database specified by the configuration file "mydb", as in (assuming it's PostgreSQL database):
vv ... --db="postgres:mydb"  ...

You do not need to manually connect to the database; when your application uses it for the first time, a connection is automatically established, and lost connection is automatically re-established when needed.

If a database is the only one used by your application, you can omit it, as in:
run-query ="select name from employees"
...
end-query

Connection settings
The contents of a configuration file depends on the database used:
Substituting environment variables
You can use environment variables in database configuration files by means of substitution, in the form of "${VAR_NAME}". For example in file "mydb":
[client]
user=${DB_USER}
password=${DB_PWD}
database=${DB_NAME}
protocol=TCP
host=127.0.0.1
port=${DB_PORT}

Here, environment variables DB_USER, DB_PWD, DB_NAME and DB_PORT are used. They must be defined in the shell environment prior to calling vv to make your application (if not defined the value will be empty):
#Define environment variables for a configuration file
export DB_USER="my_user"
export DB_PWD="my_password"
export DB_NAME="my_database"
export DB_PORT="3307"

#Make application using the above database configuration file with the environment variables specified
vv -q --db=mariadb:mydb

which results in file /var/lib/vv/<app name>/app/db/mydb:
[client]
user=my_user
password=my_password
database=my_database
protocol=TCP
host=127.0.0.1
port=3307

Besides making application deployment easier, this also adds to its security as the information such as above (including the database password) does not need to be a part of source code and reside in source code control system (such as git).

Your environment variables can have any names, except that they cannot start with an underscore ("_") or be prefixed by "VV_", because those variable names are reserved by Vely.

Note that if your data actually has a dollar sign and is a part of the configuration file, then you can create a variable for it:
export DOLLAR_SIGN='$'

and in the configuration file:
..
database=my${DOLLAR_SIGN}database
..

In this case the database name is "my$database".
See also
Database ( begin-transaction   commit-transaction   current-row   database_config_file   database_queries   delete-query   on-error   prepared_statements   query-result   rollback-transaction   run-query  )  SEE ALL (documentation)
Database queries

Databases
Your application can use any number of databases, specified by database_config_files. If only a single database is used, you may skip specifying a database with "@" clause. You can use different database vendors at the same time.
Query interface, SQL injection
Vely has the same query interface for all databases (see for example run-query). It provides means to execute dynamic as well as static queries; regardless, query input parameters are always separated from the query logic. This separation is in some cases emulated (see run-query), and in others native, such as with prepared statements (see run-query). In either case, Vely provides protection against SQL injections.
Input and output parameters
Input parameters and output resuls are always null-delimited strings. In web applications, more often than not, it is string values that are sought as output or provided as input, and conversion to other types rarely happens; there is surely a number of applications where the opposite may be true, but for the majority of applications this approach may work better. If data conversion is needed, it can be performed pre-query or post-query, depending on the purpose. The placeholder for input parameter is chosen to represent this approach, using single quotes (as text literals are single quoted in SQL) and %s (a C convention for string placeholders), thus '%s' is used for input parameters.
Binary data
Binary data can be handled for storage or retrieval via hexadecimal strings (see encode-hex, decode-hex) or Base64 (see decode-base64, encode-base64); this is reasonable for smaller amounts of data. Note that storing large amounts of binary data in the database is generally less desirable than storing such data (for instance PDF or JPG documents) in the file system (see file_storage), as manipulating and retrieving binary data is generally easier and faster that way.
Prepared statements
Prepared statements typically provide better performance but may not be ideal in every circumstance (see prepared_statements).
Multiple statements
Multiple statements in one query can be executed in PostgreSQL, where only the last statement's result set is available, which is simpler and probably more manageable. MariaDB multiple statement execution is disabled by default, and so is for SQLite. Multiple statement execution isn't ideal as generally the number of statements may not be known in advance and retrieving multiple results sets can be challenging. Using stored procedures that return a single result set, or multiple queries to execute multiple statements are both better approach.

Prepared queries, regardless of the database, can never be multiple statements, which is a limitation that exists in all databases.
Connection persistence and reuse, transactions
A single Vely process (see how_vely_works and vely_architecture) keeps a single connection open to the database, which is shared between all requests a process will handle. Each request will either commit or rollback a transaction; if the transaction is neither committed nor rollback-ed, it will be rollback-ed with error (see begin-transaction). If a database connection is lost, Vely will automatically re-establish it if possible. This allows for very fast query execution and essentially unlimited reuse of prepared statements.
Autocommit
The autocommit feature is enabled by default on all supported databases. Do not disable it, as that would cause unpredictable and inconsistent behavior.  Whenever you need a transactional behavior, use commit-transaction to begin a transaction and commit-transaction/rollback-transaction to end it.  Also, in general, multiple DML statements (such as INSERT) are faster within a transaction block because the data is not flushed with each one, but rather once per block.
See also
Database ( begin-transaction   commit-transaction   current-row   database_config_file   database_queries   delete-query   on-error   prepared_statements   query-result   rollback-transaction   run-query  )  SEE ALL (documentation)
Debugging

Several techniques on debugging Vely applications are discussed here.
Tracing and Backtrace file
To see any errors reported by Vely, use -e option of vv and check backtrace file. For example, to see the last 3 error messages:
vv -e 3

You can use trace-run statement to create run-time traces (see how_vely_works for directory location). To quickly find the location of recently written-to trace files, use -t option of vv, for example for 5 most recently used trace files:
vv -t 5

Use get-req to get the trace file location at run-time from your application.
Output from FastCGI application without web server
Use FastCGI_client to send request and receive reply from your FastCGI server processes from command line. This is useful in debugging issues and automating tests.
Issues in starting vf
vf starts your web application, running as FastCGI processes. If you're having issues with vf, check out its log. Assuming your application name is "app_name", the log file is:
/var/lib/vv/app_name/vflog/log

Web server error log
Check web server's error log, which would store the error messages emitted to the client. Typically, such files are in the following location:
/var/log/<web server>

(for example /var/log/apache2), but the location may vary - consult your web server's documentation.
Using gdb debugger
In order to use gdb debugger, you must make your application with "--debug" flag (see vv). Do not use "--debug" in any other case, because performance will be considerably affected.

Ultimately, you can attach a debugger to a running Vely process. If your application name is "app_name", first find the PID (process ID) of its process(es):
ps -ef|grep app_name.fcgi

Note that you can control the number of worker processes started, and thus have only a single worker process (or the minimum necessary), which will make attaching to the process that actually processes a request easier (see vv).

Use gdb to load your program:
sudo gdb /var/lib/vv/bld/app_name/app_name.fcgi

and then attach to the process (<PID> is the process ID you obtained above):
att <PID>

Once attached, you can break in the request you're debugging:
br <request name>

or in general Vely request dispatcher:
br vely_dispatch_request

which would handle any request to your application.
Error containing "vely_statement_must_be_within_code_block_here"
If you get an error like:
error: ‘_vely_statement_must_be_within_code_block_here_1’ undeclared (first use in this function);

then you are attempting to use a Vely statement in a single-statement implicit block, as in:
if (some condition)
    exec-program ....

Since each Vely statement expands into one or more lines, if a Vely statement is the sole functional code used in an implicit code block, you must place it in an explicit block:
if (some condition)  {
    exec-program ....
}

See example_hello_world for an example in debugging and tracing.
See also
Debugging ( debugging   trace-run  )  SEE ALL (documentation)
decode-base64

Purpose: Base64 decode.

decode-base64 <data> to [ define ] <output data> \
    [ input-length <input length> ] \
    [ output-length [ define ] <output length> ]

decode-base64 will decode string <data> into <output data>, which can be binary data. <output data> is allocated memory.

If "input-length" clause is used, then <input length> is the number of bytes decoded, otherwise <data> is treated as a null-terminated string and its length determined.

The result is stored in <output data> (in "to" clause), which can be created with optional "define". The length of the decoded string can be obtained via "output-length" clause in <output length>, which can be created with optional "define".

<output data> will always have an extra null byte appended - this null byte is not included in length and is appended only as a safety precaution.

Note that the string to decode can have whitespaces before it (such as spaces or tabs), and whitespaces and new lines after it, which will be ignored for the purpose of decoding.
Examples
See encode-base64.
See also
Base64 ( decode-base64   encode-base64  )  SEE ALL (documentation)
decode-hex

Purpose: Decode hexadecimal string into data.

decode-hex <data> to [ define ] <output> \
    [ input-length <input length> ] \
    [ output-length [ define ] <output length> ]

decode-hex will decode hexadecimal string <data> to string <output> given in "to" clause, which may be any binary data. <output> is allocated memory.

<data> must consist of an even number of digits 0-9 and letters A-F or a-f. The length of <data> may be given by optional <input length> in "input-length" clause, otherwise it is assumed to be the string length of <data>. The output string <output> is allocated and its length is given in the optional <output length> in "output-length" clause.

Optional "define" subclause can be used to create <output> string and a number variable <output length> if they do not already exist.
Examples
Get binary data from a hexadecimal string "hexdata". The output string "binout" is created and so is its length "olen" variable of type "num":
char *hexdata = "0041000F414200";
decode-hex hexdata to define binout output-length define olen

The value of "binout" will be binary data equal to this C literal:
char *binout = "\x00""A""\x00""\xF""AB""\x00""\x04";

See also
Hex encoding ( decode-hex   encode-hex  )  SEE ALL (documentation)
decode-url

Purpose: Decode URL-encoded string.

decode-url <string> [ output-length [ define ] <decoded length> ] [ input-length <length> ]

decode-url will decode <string> (created by encode-url or other URL-encoding software) and store the result back into it (i.e. it will be modified). An optional "output-length" clause lets you get the length of decoded string (which is always lesser or equal than the length of the encoded string) in <decoded length>, which can be created with optional "define". <length> in optional "input-length" clause specifies the number of bytes to decode; if omitted or negative, it is the string length of <string>.

All encoded values (starting with %) are decoded, and "+" (plus sign) is converted to space. If there is an error (for example hexadecimal value following % is invalid), the decoding stops and whatever was decoded up to that point is the result, and the length reflects that.

Note that <string> must not be a constant because decode-url will write into it. If it is a constant, make a copy of it first with copy-string. See encode-url.
Examples
Decode URL-encoded string "str", after which it will hold a decoded string. "dec_len" will be the length (in bytes) of that string:
decode-url str output-length define dec_len

See also
URL encoding ( decode-url   encode-url  )  SEE ALL (documentation)
decode-web

Purpose: Decode web(HTML)-encoded string.

decode-web <string> [ output-length [ define ] <decoded length> ] [ input-length <length> ]

decode-web will decode <string> (created by encode-web or other web-encoding software) and store the result back into it (i.e it will be modified). "output-length" clause lets you get the length of the decoded string (which is always lesser or equal than the length of the encoded string) in <decoded length>, which can be created with optional "define". To decode only a number of leading bytes in <string>, use "input-length" clause and specify <length>.

See encode-web.
Examples
Decode web-encoded string "str", after which it will hold a decoded string. "dec_len" will be the length (in bytes) of that string:
decode-web str output-length define dec_len

See also
Web encoding ( decode-web   encode-web  )  SEE ALL (documentation)
decrypt-data

Purpose: Decrypt data.

decrypt-data <data> to [ define ] <result> \
    [ input-length <input length> ] \
    [ output-length [ define ] <output length> ] \
    [ binary [ <binary> ] ] \
    ( password <password> \
        [ salt <salt> [ salt-length <salt length> ] ] \
        [ iterations <iterations> ] \
        [ cipher <cipher algorithm> ] \
        [ digest <digest algorithm> ]
        [ cache ]
        [ clear-cache <clear cache> ) \
    [ init-vector <init vector> ]

decrypt-data will decrypt <data> which must have been encrypted with encrypt-data, or other software using the same algorithms and clauses as specified.

If "input-length" clause is not used, the data to decrypt is considered to be a string, i.e. null-terminated, otherwise, if specified, then exactly <input length> bytes are decrypted. Password used for decryption is <password> (in "password" clause) and it must match the password used in encrypt-data. If "salt" clause is used, then string <salt> must match the salt used in encryption. If "init-vector" clause is used, then string <init vector> must match the IV (initialization vector) used in encryption. If "iterations" clause is used, then <iterations> must match the number used in encryption.

"output-length" clause lets you obtain the number of bytes in decrypted data in <output length>, which can be created with optional "define". The result of decryption is in <result> (in "to" clause) and can be created with optional "define". <result> is allocated memory.

If data was encrypted in binary mode (see encrypt-data), you must decrypt it with the same, and if it wasn't, then you must not use it in decrypt-data either. The reason for this is obvious - binary mode of encryption is encrypted data in its shortest form, and character mode (without "binary" or if <binary> evaluates to false) is the same data converted to a hexadecimal string - thus decryption must first convert such data back to binary before decrypting.

The cipher and digest algorithms (if specified as <cipher algorithm> and <digest algorithm> in "cipher" and "digest" clauses respectively) must match what was used in encrypt-data.

"cache" clause is used to cache the result of key computation, so it is not computed each time decryption takes place, while "clear-cache" allows key to be re-computed every time <clear cache> evaluates to boolean true. For more on "cache" and "clear-cache" clauses, as well as safety of encrypting/decrypting, see "Caching key" and "Safety" in encrypt-data.
Examples
See encrypt-data.
See also
Encryption ( decrypt-data   derive-key   encrypt-data   hash-string   random-crypto   random-string  )  SEE ALL (documentation)
delete-cookie

Purpose: Deletes a cookie.

delete-cookie <cookie name> [ path <cookie path> ] [ status [ define ] <status> ] [ secure <secure> ]

delete-cookie marks a cookie named <cookie name> for deletion, so it is sent back in the reply telling the client (such as browser) to delete it.

Newer client implementations require a cookie deletion to use a secure context if the cookie is considered secure, and it is recommended to use "secure" clause to delete such a cookie. This is the case when either "secure" clause is used without optional boolean expression <secure>, or if <secure> evaluates to true.

<cookie name> is a cookie that was either received from the client as a part of the request, or was added with set-cookie.

A cookie can be deleted before or after sending out a header (see out-header). However a cookie must be deleted prior to outputting any actual response (such as with output_statement or p-out for example), or the program will error out and stop.

<status> (in the optional "status" clause) is the integer variable (which can be created with optional "define") that will be -1 if the cookie did not exist, or 0 or greater if it did.

The same cookie name may be stored under different URL paths. You can use the optional "path" clause to specify <cookie path> to ensure the desired cookie is deleted.
Examples
delete-cookie "my_cookie"
bool is_secure = true;
delete-cookie "my_cookie" path "/path" secure is_secure

See also
Cookies ( delete-cookie   get-cookie   set-cookie  )  SEE ALL (documentation)
delete-file

Purpose: Deletes a file.

delete-file <file location> [ status [ define ] <status var> ]

File specified with <file location> is deleted. <file location> can be given with an absolute path or relative to the application home directory (see vv).

If "status" is specified, the status is stored into <status var>. The status is VV_OKAY on success or if the file did not exist, and VV_ERR_DELETE on failure. If "define" is specified, <status var> variable is created.
Examples
delete-file "/home/user/some_file" status define st

See also
Files ( close-file   copy-file   delete-file   file-position   file_storage   file_uploading   lock-file   open-file   read-file   read-line   rename-file   stat-file   temporary_file   uniq-file   unlock-file   write-file  )  SEE ALL (documentation)
delete-json

Purpose: Delete memory associated with JSON parsing.

delete-json <json>

delete-json will delete memory associated with variable <json> created to parse a JSON string with new-json.

See memory_handling for more on when (not) to delete memory explicitly like this; the same rules apply as for delete-mem.
Examples
delete-json jv

See also
JSON ( delete-json   new-json   read-json  )  SEE ALL (documentation)
delete-mem

Purpose: Free memory.

delete-mem <memory> status [ define ] <status>

delete-mem frees memory allocated by Vely. Do not use it on memory allocated by any C-library functions (such as malloc(), calloc() or realloc()).

delete-mem will attempt to handle memory violations or invalid pointers, and may not error out if there is an issue; if the error is too severe, it will error out. If it doesn't error out, you can obtain the status of freeing memory in variable <status> by using "status" clause (you can create this number variable with "define" if it does not exist). The reason for this is that freeing memory is in many cases unnecessary as Vely will automatically do so at the end of each request and problems in freeing memory will not affect doing so in the end.

If <status> is VV_OKAY, the memory has been freed successfully. If <status> is VV_ERR_MEMORY, there was a problem; most likely <string> was not a valid memory pointer allocated by any of Vely statements; or it was a bad memory pointer for any number of reasons (such as for example it was already freed once).
Examples
Allocate and free memory:
new-mem define mystr size 300
delete-mem mystr status define st
if (st != VV_OKAY) {
    @Error in memory release!
}

Free memory allocated by write-string (consisting of 100 "Hello World" statements):
write-string define ws
    num i;
    for (i = 0; i < 100; i++) {
        @Hello World
    }
end-write-string
delete-mem ws

See also
Memory ( delete-mem   manage-memory   memory_handling   new-mem   resize-mem  )  SEE ALL (documentation)
delete-query

Purpose: Delete memory associated with a query.

delete-query <query name> [ skip-data ]

A named query (see clause "name" in run-query) can be deleted with delete-query by specifying the <query name>. Deletion means that all memory allocated for a query will be freed. It means query results will be freed as well. Do not delete query if its results are still referenced afterwards.

However, if "skip-data" clause is used, then the query results will not be deleted; rather everything else will be. This means the results of query-result statement or "output define" clause in run-query statement will not be freed. This is useful if you want to keep the results of a query and otherwise release all of its resources. Note that the results will remain allocated even if you do not obtain them via "query-result" or "output define" clause, for instance.

Make sure not to delete anything twice. For instance, do not use delete-mem to delete query data such as error message, column name or results, while using delete-query as well. By the same token, if you use "skip-data", you can use delete-mem to free query results later.

See memory_handling for more on when (not) to delete memory explicitly like this; the same rules apply as for delete-mem.
Examples
run-query @db="drop table if exists test" no-loop name drop_query
delete-query drop_query

See also
Database ( begin-transaction   commit-transaction   current-row   database_config_file   database_queries   delete-query   on-error   prepared_statements   query-result   rollback-transaction   run-query  )  SEE ALL (documentation)
Deploying application

You can deploy Vely applications in different ways.
Containers
Using containers (such as with docker or podman) is an easy, reliable and portable way of deploying a Vely application. See  containerize_application.

This method is also applicable for deployment to Internet of Things (IOT) systems, such as for example Fedora IOT or with Raspberry PI Operating Systems often used for devices. For IOT, Vely can be deployed both on servers and devices.
Installing from source
You can also deploy a Vely application by installing it from source; see application_setup. After installation, you may delete the source code and remove generated C code:
vv -c

Installing from binary
The binary for your application can be copied to another computer with the same Operating System and with Vely installed, provided the Operating System has compatible libraries; in practice this means the same major Operating System version and the same or higher minor version. The same is true for Vely version; you must have the same major Vely version and the same or higher minor version.

You must install Vely first on the target machine and then create the application, for instance:
sudo vf -i -u $(whoami) <app name>

Next you must copy the binaries for your application from source to target, to the same location. Assuming application name is <app name>, the binaries are:
#Fast CGI binary:
/var/lib/vv/bld/<app name>.fcgi

#command line and CGI binary:
/var/lib/vv/bld/<app name>

For an application that does not use any database(s) and does not need any additional files, this is enough to deploy the application to a target machine.

If you use database(s), you must install such database(s) and migrate any needed data, and copy the database_config_files, meaning the following directory:
/var/lib/vv/<app name>/app/db

If you use SQLite, you can just copy the database file as it is portable; generally such file would be in the application's home directory. Since the application's home directory is:
/var/lib/vv/<app name>/app

you would copy any custom files and directories you have created and that your application may need. Note that all files and directories copied must be owned by the application owner, i.e. the user used in application creation command above (see vf).
Multi-user and multi-application environment
Each application owner (i.e. the user used in application creation with -u flag, see vf) can have any number of applications. At the same time, different application owners can create any number of applications of their own; the application directory for each application is owned by its respective owner with 700 permissions, meaning different users cannot access application files of other owners (see how_vely_works). Similarly, application servers (i.e. FastCGI servers) run under the application owner permissions, and can only be managed by its owner.
See also
General ( deploying_application   how_vely_works   quality_control   rename_files   SELinux   vely   vely_architecture   vely_removal   vf   vv   why_C_and_Vely  )  SEE ALL (documentation)
derive-key

Purpose: Derive a key.

derive-key [ define ] <key> from <source> length <length> \
    [ binary [ <binary> ] ] \
    [ from-length <source length> ] \
    [ digest <digest algorithm> ] \
    [ salt <salt> [ salt-length <salt length> ] ] \
    [ iterations <iterations> ]

derive-key derives <key> (which can be created with optional "define") from string <source> in "from" clause. <key> is allocated memory. If <source length> in "from-length" clause is specified, exactly <source length> bytes of <source> are used, regardless of null bytes. Otherwise, the length of a null-terminated <source> string is used as the number of bytes.

The length of derived key is given by <length> in "length" clause. The method for key generation is PBKDF2. By default the digest used is "SHA256". You can use a different <digest algorithm> in "digest" clause (for example "SHA3-256"). To see a list of available digests:
#get digests
openssl list -digest-algorithms

The optional salt for key derivation can be given with <salt> in "salt" clause. If "salt-length" clause is not used, then the salt is null-terminated, otherwise its length is <salt length>.

The number of iterations is given by <iterations> in "iterations" clause. The default is 1000 per RFC 8018, though depending on your needs and the quality of <source> you may choose a different value.

By default, the derived key is produced as a null-terminated string in a hexadecimal form, where each byte is encoded as two-character hexadecimal characters, so its length is 2*<length>. If "binary" clause is used without optional boolean expression <binary>, or if <binary> evaluates to true, then the output is a binary string of <length> bytes, and a null byte is placed after it.

Key derivation is often used when storing password-derivatives in the database (with salt), and also for symmetrical key generation.
Examples
Derived key is in variable "mk":
random-string to define rs9 length 16
derive-key define mk from "clave secreta" digest "sha-256" salt rs9 salt-length 10 iterations 2000 length 16

See also
Encryption ( decrypt-data   derive-key   encrypt-data   hash-string   random-crypto   random-string  )  SEE ALL (documentation)
Documentation

Reference
Note: All the topics below are available as a single-page documentation. You can also download it as a .tar.gz file and use it on your computer, host it on your web site etc.


Application information ( get-app   set-app  )

Base64 ( decode-base64   encode-base64  )

Cookies ( delete-cookie   get-cookie   set-cookie  )

Database ( begin-transaction   commit-transaction   current-row   database_config_file   database_queries   delete-query   on-error   prepared_statements   query-result   rollback-transaction   run-query  )

Debugging ( debugging   trace-run  )

Documentation ( documentation  )

Encryption ( decrypt-data   derive-key   encrypt-data   hash-string   random-crypto   random-string  )

Error handling ( error_code   error_handling   on-error   report-error  )

Examples ( example_cookies   example_create_table   example_docker   example_file_manager   example_form   example_hash   example_hello_world   example_json   example_multitenant_SaaS   examples   example_sendmail   example_shopping   example_stock   example_utility   example_write_report  )

FIFO ( new-fifo   purge-fifo   read-fifo   rewind-fifo   write-fifo  )

Files ( close-file   copy-file   delete-file   file-position   file_storage   file_uploading   lock-file   open-file   read-file   read-line   rename-file   stat-file   temporary_file   uniq-file   unlock-file   write-file  )

General ( deploying_application   how_vely_works   quality_control   rename_files   SELinux   vely   vely_architecture   vely_removal   vf   vv   why_C_and_Vely  )

Hash table ( get-hash   new-hash   purge-hash   read-hash   resize-hash   write-hash  )

Hex encoding ( decode-hex   encode-hex  )

JSON ( delete-json   new-json   read-json  )

Language ( dot   inline_code   statement_APIs   syntax_highlighting   unused-var  )

License ( license  )

Memory ( delete-mem   manage-memory   memory_handling   new-mem   resize-mem  )

Output ( finish-output   flush-output   output_statement   p-dbl   pf-out   pf-url   pf-web   p-num   p-out   p-path   p-url   p-web  )

Process ( global_process_data  )

Program execution ( exec-program   exit-code  )

Program flow ( exit-request  )

Quick start ( 123_hello_world  )

Regex ( match-regex  )

Request information ( get-req   input-param   request-body   set-input   set-req  )

Requests ( after_request_handler   before_request_handler   building_URL   getting_URL   global_request_data   non_request   normalized_URL   request   request_URL   startup_handler   vely_dispatch_request  )

Running application ( application_setup   CGI   Client_API   command_line   containerize_application   FastCGI   FastCGI_client   plain_C_FCGI  )

Strings ( copy-string   count-substring   lower-string   split-string   trim-string   upper-string   write-string  )

System information ( get-sys  )

Time ( get-time  )

URL encoding ( decode-url   encode-url  )

UTF8 ( json-utf8   utf8-json  )

Web ( call-web   out-header   send-file   silent-header  )

Web encoding ( decode-web   encode-web  )

Web servers ( connect_apache_tcp_socket   connect_apache_unix_socket   connect_nginx_tcp_socket   connect_nginx_unix_socket  )

Man pages
Vely documentation is available online, and also as man pages (i.e. manual pages). You can take any of the topics above and type it in man, for example
man run-query

man how_vely_works

man example_shopping

The Vely section is '2vv', so in case of other software having conflicting topic names, you can also type
man 2vv run-query

man 2vv how_vely_works

man 2vv example_shopping


Dot

Purpose: Write a single line of C code when conflicting with Vely code.

.<C code>;

If Vely code conflicts with C code, you can use the dot (".") statement to write a single line of C code - so each line of such code must be prefixed with  "." (a dot). Note there doesn't have to be a space after the dot.
Examples
In this case, valid C expression exec-sql would be confused for "exec-sql" Vely statement:
num exec = 3;
num sql = 2;
printf ("%lld\n",
exec-sql);

You can simply prefix the offending code with the dot:
num exec = 3;
num sql = 2;
printf ("%lld\n",
.exec-sql);

See also
Language ( dot   inline_code   statement_APIs   syntax_highlighting   unused-var  )  SEE ALL (documentation)
encode-base64

Purpose: Base64 encode.

encode-base64 <data> to [ define ] <output data> \
    [ input-length <input length> ] \
    [ output-length [ define ] <output length> ]

encode-base64 will encode <data> into base64 string <output data>, which is allocated memory.

If "input-length" clause is used, then <input length> is the number of bytes encoded, otherwise <data> is treated as a null-terminated string and its length determined.

The result is stored in <output data> (in "to" clause), which can be created with optional "define". The length of the encoded string can be obtained via "output-length" clause in <output length>, which can be created with optional "define".

Base64-encoded strings are often used where binary data needs to be in a format that complies with certain text-based protocols, such as in attaching documents in email, or embedding binary documents (such as "JPG" files for example) in web pages, such as with images with "data:image/jpg;base64..." specified, etc.
Examples
Simple example that encodes a string and then decodes, and checks if they match:
// Original string, generally this would be binary data in most cases
char dt[]="  oh well  ";

// Encode in base64
encode-base64 dt to define out_dt

decode-base64 out_dt to define new_dt

if (!strcmp (dt, new_dt)) {
    @Success!
} else {
    @Failure!
}

In the next example, "input-length" and "output-length" clauses are used, and only a partial of the input string is encoded. The length of the final decoded string, as well as the string itself is checked against the original:
 // Original string, generally this would be binary data in most cases
char dt[]="  oh well  ";

// Encode in base64, encode only 6 bytes
encode-base64 dt input-length 6 to define out_dt output-length define out_len

decode-base64 out_dt input-length out_len to define new_dt output-length define new_len

if (new_len != 6) {
    @Failure!
} else {
    @Success!
}

if (!strncmp(dt,new_dt,6)) {
   @Success!
} else {
   @Failure!
}

See also
Base64 ( decode-base64   encode-base64  )  SEE ALL (documentation)
encode-hex

Purpose: Encode data into hexadecimal string.

encode-hex <data> to [ define ] <output> \
    [ input-length <input length> ] \
    [ output-length [ define ] <output length> ] \
    [ prefix <prefix> ]

encode-hex will encode string <data> to hexadecimal string <output> given in "to" clause, which is null-terminated and consists of digits "0"-"9" and letters "a"-"f". <output> is allocated memory.

<data> can contain null-bytes and in general is any binary data, the length of which is given by <input length> in "input-length" clause. The output string <output> is allocated and its length is given in <output length> in "output-length" clause. If you wish to prefix the output with a null-terminated string <prefix>, you can specify it in "prefix" clause.

"output-length" clause is optional, and so is "prefix" (in which case no prefix is prepended). "input-length" clause is also optional, and if not specified, <input length> is taken as the string length of <data>, which is in that case assumed to be null-terminated.

Optional "define" subclause can be used to create <output> string and a number variable <output length> if they do not already exist.
Examples
Create hexadecimal string from binary data "mydata" of length 7, prefixed with string "\\\\x" (which is typically needed for PostgreSQL binary input to queries). The output string "hexout" is created and so is its length "olen" variable of type "num":
char *mydata = "\x00""A""\x00""\xF""AB""\x00""\x04";
encode-hex mydata to define hexout input-length 7 output-length define olen prefix "\\\\x"

The value of "hexout" will be:
\\x0041000F414200

See also
Hex encoding ( decode-hex   encode-hex  )  SEE ALL (documentation)
encode-url

Purpose: URL-encode string.

encode-url <string> to [ define ] <encoded string> \
    [ output-length [ define ] <encoded length> ] \
    [ input-length <length> ]

encode-url URL-encodes <string> and stores the result in <encoded string> which may be created with optional "define". <encoded string> is allocated memory.

Number <length> in the optional "input-length" clause lets you specify the number of bytes in <string> that will be encoded - if not specified or negative, it is the string length. "output-length" clause (also optional) lets you get the length of the encoded string in <encoded length>, which can be created with optional "define".

All bytes except alphanumeric and those from "-._~" (i.e. dash, dot, underscore and tilde) are encoded.
Examples
In this example, a string "str" is URL encoded and the result is in a "result" string variable, and its length is in "len_of_result" variable:
char str[]="  x=y?z&  ";
encode-url str to define result output-length define len_of_result

The "result" is "%20%20x%3Dy%3Fz%26%20%20" and "len_of_result" is 24.
See also
URL encoding ( decode-url   encode-url  )  SEE ALL (documentation)
encode-web

Purpose: Web(HTML)-encode string.

encode-web <string> to [ define ] <encoded string> \
    [ output-length [ define ] <encoded length> ] \
    [ input-length <length> ]

encode-web encodes <string> so it can be used in a HTML-like markup text (such as a web page or an XML/XHTML document), and stores the result in <encoded string> which may be created with optional "define". <encoded string> is allocated memory.

Optional "output-length" clause lets you get the length of the encoded string in <encoded length>, which can be created with optional "define". You can encode only the first <length> bytes, given by an "input-length" clause.
Examples
In this example, a string "str" will be web-encoded and the result is in "result" variable, with its length in "len_of_result" variable:
char str[]="  x<y>z&\"'  ";
encode-web str to define result output-length define len_of_result

The "result" is "   x&lt;y&gt;z&amp;&quot;&apos;  " and "len_of_result" is 33.
See also
Web encoding ( decode-web   encode-web  )  SEE ALL (documentation)
encrypt-data

Purpose: Encrypt data.

encrypt-data <data> to [ define ] <result> \
    [ input-length <input length> ] \
    [ output-length [ define ] <output length> ] \
    [ binary [ <binary> ] ] \
    ( password <password> \
        [ salt <salt> [ salt-length <salt length> ] ] \
        [ iterations <iterations> ] \
        [ cipher <cipher algorithm> ] \
        [ digest <digest algorithm> ]
        [ cache ]
        [ clear-cache <clear cache> ) \
    [ init-vector <init vector> ]

encrypt-data encrypts <data> and stores the ciphertext to <result> specified by "to" clause, which can be created with optional "define".
Cipher and digest
By default, AES-256-CBC encryption and SHA256 hashing is used. You can however specify different cipher and digest algorithms with <cipher algorithm> (in "cipher" clause) and <digest algorithm> (in "digest" clause) as long as OpenSSL supports them, or you have added them to OpenSSL. You can see the available ones by using:
#get list of cipher providers
openssl list -cipher-algorithms

#get list of digest providers
openssl list -digest-algorithms

Note that the default algorithms will typically suffice. If you use different algorithms, you should have a specific reason. If you use a specific cipher and digest for encoding, you must use the same for decoding. The key derivation method is PBKDF2.
Data to be encrypted
If "input-length" clause is missing, then <data> is considered to be a null-terminated string and the number of bytes encrypted is its length. If "input-length" clause is used, then <input length> bytes are encrypted, regardless of whether <data> is a null-terminated string or not.
Password
String <password> (in "password" clause) is the password used to encrypt and it must be a null-terminated string.
Salt
Optional <salt> (in "salt" clause) is the salt used in Key Derivation Function (KDF) when an actual symmetric encryption key is created. If <salt length> (in "salt-length" clause) is not specified, then the salt is null-terminated, otherwise it is a binary value of length <salt length>. See random-string or random-crypto for generating a random salt. If you use the "salt" clause, then you must use the exact same <salt> when data is decrypted with decrypt-data - typically salt values are stored or transmitted unencrypted.
Iterations
The number of iterations used in producing a key is specified in <iterations> in optional "iterations" clause. The default is 1000 per RFC 8018, though depending on your needs and the quality of password you may choose a different value.
Initialization vector (IV)
Different encrypted messages should have a different IV value, which is specified with <init vector> in the "init-vector" clause. See random-string or random-crypto for generating IV values. The decrypting side must use the same IV value to decrypt the message. Just like salt, IV is not a secret and is transmitted in plain text. IV is generally a binary value and each cipher algorithm may require a certain number of bytes.
Encrypted data
The encrypted data is stored in <result> (in "to" clause), which you can create with optional "define". <result> is allocated memory. The encrypted data can be a binary data (if "binary" clause is present without optional boolean expression <binary>, or if <binary> evaluates to true), which is binary-mode encryption; or if not, it will be a null-terminated string, which is character-mode encryption, consisting of hexadecimal characters (i.e. ranging from "0" to "9" and "a" to "f"). Character mode of encryption is convenient if the result of encryption should be a human readable string, or for the purposes of non-binary storage in the database.

If this is a binary-mode encryption, then "output-length" clause can be used to get the length of the binary encrypted data in <output length> , which can be created with optional "define". In any case, if used, <output length> has the length of the encrypted data, which is the exact byte count in binary mode, or the length of encrypted string in character mode (i.e. the number of character bytes excluding the terminating null byte).
Caching key
A key used to actually encrypt/decrypt data is produced by using password, salt, cipher, digest and the number of iterations. Depending on these parameters (especially the number of iterations), computing the key can be a resource intensive and lengthy operation. You can cache the key value and compute it only once (or once in a while) by using "cache" clause. If you need to recompute the key once in a while, use "clear-cache" clause. <clear cache> is a "bool" variable; the key cache is cleared if it is true, and stays if it is false. For example with encrypt-data (the same applies to decrypt-data):
bool clear;
if (q == 0) clear = true; else clear = false;
encrypt-data dt init-vector non password pwd \
    salt rs salt-length 10 iterations iter to \
    define dt_enc cache clear-cache clear

In this case, when "q" is 0, cache will be cleared, with values of password, salt and iterations presumably changed, and the new key is computed and then cached. In all other cases, the last computed key stays the same. Normally, with IV usage (in "init-vector" clause), there is no need to change the key often, or at all.

Note that while "cache" clause is in effect, the values for "password", "salt", "cipher", "digest" and "iterations" clauses can change without any effect. Only when "clear-cache" evaluates to "true" are those values taken into account.
Safety
Unless you are encrypting/decrypting a single message, you should always use IV in "init-vector" clause. Its purpose is to randomize the data encrypted, so that same messages do not produce the same ciphertext.

If you use salt, a random IV is created with each different salt value. However, different salt values without "cache" clause will regenerate the key, which may be computationally intensive, so it may be better to use a different IV instead for each new encryption and keep the salt value the same with the high number of iterations. In practicality this means using "cache" so that key is computed once per process with the salt, and IV changes with each message. If you need to recompute the key occasionally, use "clear-cache".

Each cipher/digest combination carries separate recommendations about the usage of salt, IV and the number of iterations. Please consult their documentation for more details.
Examples
In the following example, the data is encrypted, and then decrypted, producing the very same data:
// Original string to encrypt
char *orig_data="something to encrypt!";

// Encrypted data is in "res" variable
encrypt-data orig_data password "mypass" to define res

// Decrypt what was just encrypted, decrypted data is in "dec_data"
decrypt-data res password "mypass" to define dec_data

// Check that decrypted data matches the original 
if (!strcmp (orig_data, dec_data)) {
    @Success!
} else {
    @Failure!
}

A more involved example below encrypts specific number of bytes (6 in this case). random-string is used to produce salt. The length of data to encrypt is given with "input-length" clause. The encrypted data is specified to be "binary" (meaning not as a human-readable string), so the "output-length" of such binary output is specified. The decryption thus uses "input-length" clause to specify the length of data to decrypt, and also "output-length" to get the length of decrypted data. Finally, the original data is compared with the decrypted data, and the length of such data must be the same as the original (meaning 6):
// Original data (only the first 6 bytes are encrypted)
char *orig_data="something to encrypt!";

// Get 8 random binary bytes to be the salt
random-string to define newsalt length 8 binary

// Encrypt data using salt and produce binary output (meaning it's not a null-terminated character string), with the
// length of such output in "encrypted_len" variable.
encrypt-data orig_data input-length 6 output-length define encrypted_len password "mypass" salt newsalt to define res binary

// Decrypt the data encrypted above. The length of encrypted data is passed in "encrypted_len" variable, and then length of decrypted data
// is obtained in "decrypted_len" variable.
decrypt-data res output-length define decrypted_len password "mypass" salt newsalt to define dec_data input-length encrypted_len binary

// Check if the 6 bytes of the original data matches decrypted data, and if exactly 6 bytes was decrypted
if (!strncmp(orig_data,dec_data, 6) && decrypted_len == 6) {
    @Success!
} else {
    @Failure!
}

An example of using different algorithms:
encrypt-data "some data!" password "mypwd" salt rs1 to encd1 cipher "camellia-256-cfb1" digest "sha3-256"
decrypt-data encd1 password "mypwd" salt rs1 to decd1 cipher "camellia-256-cfb1" digest "sha3-256"

See also
Encryption ( decrypt-data   derive-key   encrypt-data   hash-string   random-crypto   random-string  )  SEE ALL (documentation)
Error code

Many Vely statements return status with VV_ERR_... error codes, which are generally descriptive to a point. Such status is not as detailed as the operating system "errno" variable, however you can use "errno" clause in get-req statement to obtain the last known errno value from aforementioned statements. You should obtain this value as soon as possible after the statement because another statement may set it afterwards.

In the following example, a directory is attempted to be deleted via delete-file, which will fail with VV_ERR_DELETE - however you can get a more specific code via "errno" (which in this case is "21", or "EISDIR", which means that it cannot delete a directory with this statement):
delete-file "some_directory" status define stc
if (stc == VV_ERR_DELETE) {
    get-req errno to define e
    @Cannot delete file
    pf-out "Error %lld\n", e
}

Note that with some VV_ERR_... codes, the "errno" clause in get-req may return 0. This means the error was detected by Vely and not reported by the operating system.
See also
Error handling ( error_code   error_handling   on-error   report-error  )  SEE ALL (documentation)
Error handling

When your program errors out
"Erroring out" means your program has encountered insurmountable difficulty and it will end. For instance, it could be out of memory, or the database is permanently down and connection cannot be re-established. These are errors that cannot be handled. If your program is started with vf, it may be automatically restarted.
When report-error is called
You can report an error in your Vely code with report-error, after which:
When there is a problem in Vely
If there is a fatal internal error (i.e. error in Vely code itself that cannot be handled), it will be caught by Vely, and the program will end. If your program is started with vf, it may be automatically restarted.
Logging the error
Regardless of the type of error and regardless of whether the program exits or not, the error is logged and the program stack with full source code lines (see vv for including debug information) will be written to backtrace file (use -e option of vv to obtain its location).

You can see the list of last N errors (and the location of file containing backtrace for them) by using vv, for instance to see the last 3 errors:
vv -e 3

See also
Error handling ( error_code   error_handling   on-error   report-error  )  SEE ALL (documentation)
Example: cookies

A value is entered in the browser and saved as a cookie, then read back later. This example:
This is the basic mechanism often used in saving web application states and session information.

In a nutshell:  web browser; Apache; Unix sockets; 1 source files, 49 lines of code.
Screenshots of application
A web form is displayed where the user can enter a name. Clicking "Submit" sends it to the server:

Vely

A reply page when the server receives the information and sets the cookie in the browser:

Vely

A request sent to the server will now have the cookie, which the server code will read and display back:

Vely

Setup prerequisites
Install Vely - you can use standard packaging tools such as apt, dnf, pacman or zypper.

Because it is used in this example, you will need to install Apache as a web server.

After installing Vely, turn on syntax highlighting in vim if you're using it:
vv -m

Get the source code
The source code is a part of Vely installation. It is a good idea to create a separate source code directory for each application (and you can name it whatever you like). In this case, unpacking the source code will do that for you:
tar xvf $(vv -o)/examples/cookies.tar.gz
cd cookies

Setup application
The very first step is to create an application. The application will be named "cookies", but you can name it anything (if you do that, change it everywhere). It's simple to do with vf:
sudo vf -i -u $(whoami) cookies

This will create a new application home (which is "/var/lib/vv/cookies") and do the application setup for you. Mostly that means create various subdirectories in the home folder, and assign them privileges. In this case only current user (or the result of "whoami" Linux command) will own those directories with 0700 privileges; it means a secure setup.
Build application
Use vv utility to make the application:
vv -q

Start your application server
To start the application server for your web application use vf FastCGI process manager. The application server will use a Unix socket to communicate with the web server (i.e. a reverse-proxy):
vf -w 3 cookies

This will start 3 daemon processes to serve the incoming requests. You can also start an adaptive server that will increase the number of processes to serve more requests, and gradually reduce the number of processes when they're not needed:
vf cookies

See vf for more options to help you achieve best performance.

If you want to stop your application server:
vf -m quit cookies

Setup web server
This shows how to connect your application listening on a Unix socket (started with vf) to Apache web server.

- Step 1:
To setup Apache as a reverse proxy and connect your application to it, you need to enable FastCGI proxy support, which generally means "proxy" and "proxy_fcgi" modules - this is done only once:
- Step 2:
Edit the Apache configuration file:
Add this to the end of file ("/cookies" is the application path (see request_URL) and "cookies" is your application name):
ProxyPass "/cookies" unix:///var/lib/vv/cookies/sock/sock|fcgi://localhost/cookies

- Step 3:
Finally, restart Apache. On Debian systems (like Ubuntu) or OpenSUSE:
sudo systemctl restart apache2

On Fedora systems (like RedHat) and Arch Linux:
sudo systemctl restart httpd

Note: you must not have any other URL resource that starts with "/cookies" (such as for example "/cookies.html" or "/cookies_something" etc.) as the web server will attempt to pass them as a reverse proxy request, and they will likely not work. If you need to, you can change the application path to be different from "/cookies", see request_URL.

Access application server from the browser
Use the following URL(s) to access your application server from a client like browser (see request_URL). Use actual IP or web address instead of 127.0.0.1 if different.
#Enter cookie 
http://127.0.0.1/cookies/cookies?action=enter_cookie

#Query entered cookie 
http://127.0.0.1/cookies/cookies?action=query_cookie

Note: if your server is on the Internet and it has a firewall, you may need to allow HTTP traffic - see ufw, firewall-cmd etc.
Files
You are now done with the example! What follows are the source files in this project so you can examine how it works:
Working with cookies (cookies.vely)
Usage of cookies is simple - get-cookie is used to obtain the cookie from a web client (i.e. a browser), and set-cookie to set the cookie in the browser when the response is sent back.

Three subrequests (based on "action" input parameter) allow user to enter a value that is sent back to the server (see "enter_cookie" as the "action" parameter), which the server then sends back to the browser in response (see "save_cookie"), and finally with "query_cookie" this value can be obtained and displayed again. This demonstrates how server-side software uses cookies to manipulate and obtain the client state.
// SPDX-License-Identifier: EPL-2.0
// Copyright 2018 DaSoftver LLC.


#include "vely.h"

void cookies()
{

    input-param action

    if (!strcmp (action, "enter_cookie")) {

        // Display a form to get cookie value
        out-header default
        @<h2>Enter your name</h2>
        @<form action="<<p-path>>/cookies" method="POST">
        @    <input type="hidden" name="action" value="save_cookie">
        @    <label for="cookie_value">Your name:</label><br/>
        @    <input type="text" name="cookie_value" value=""><br/>
        @    <br/>
        @    <input type="submit" value="Submit">
        @</form>

    } else if (!strcmp (action, "save_cookie")) {

        // Submittal of form: save the cookie through response to the browser
        input-param cookie_value
        get-time to define cookie_expiration year 1 timezone "GMT"
        set-cookie "customer_name" = cookie_value  expires cookie_expiration  path "/"
        out-header default
        @Cookie sent to browser!
        @<hr/>

    } else if (!strcmp (action, "query_cookie")) {

        // Web request that delivers cookie value back here (to server); display it.
        get-cookie define name="customer_name"
        out-header default
        @Customer name is <<p-web name>>
        @<hr/>

    } else {
        out-header default
        @Unrecognized action<hr/>
    }

}

See also
Examples ( example_cookies   example_create_table   example_docker   example_file_manager   example_form   example_hash   example_hello_world   example_json   example_multitenant_SaaS   examples   example_sendmail   example_shopping   example_stock   example_utility   example_write_report  )  SEE ALL (documentation)
Example: create table

Example of manipulating tables via SQL. Table is

In a nutshell: PostgreSQL; command line; web browser; Nginx; Unix sockets; 2 source files, 44 lines of code.
Screenshots of application
The web output from creating a table, inserting data, selecting and dropping it:

Vely

The same output, only when the program runs from the command line. Set VV_SILENT_HEADER to yes to skip the header output:

Vely

Setup prerequisites
Install Vely - you can use standard packaging tools such as apt, dnf, pacman or zypper.

Because they are used in this example, you will need to install Nginx as a web server and PostgreSQL as a database.

After installing Vely, turn on syntax highlighting in vim if you're using it:
vv -m

Get the source code
The source code is a part of Vely installation. It is a good idea to create a separate source code directory for each application (and you can name it whatever you like). In this case, unpacking the source code will do that for you:
tar xvf $(vv -o)/examples/create_table.tar.gz
cd create_table

Setup application
The very first step is to create an application. The application will be named "create_table", but you can name it anything (if you do that, change it everywhere). It's simple to do with vf:
sudo vf -i -u $(whoami) create_table

This will create a new application home (which is "/var/lib/vv/create_table") and do the application setup for you. Mostly that means create various subdirectories in the home folder, and assign them privileges. In this case only current user (or the result of "whoami" Linux command) will own those directories with 0700 privileges; it means a secure setup.
Setup the database
Before any coding, you need some place to store the information used by the application. First, you will create PostgreSQL database "db_create_table". You can change the database name, but remember to change it everywhere here. And then, you will create database objects in the database.

Note the example here uses peer-authentication, which is the default on all modern PostgreSQL installations - this means the database user name is the same as the Operating System user name.

Execute the following in PostgreSQL database as root (using psql utility):
echo "create user $(whoami);
create database db_create_table with owner=$(whoami);
grant all on database db_create_table to $(whoami);
\q
" | sudo -u postgres psql

Next, login to database db_create_table and create the database objects for the application:
psql -d db_create_table -f setup.sql

Connect Vely to a database
In order to let Vely know where your database is and how to log into it, you will create database_config_file named "db_create_table". This name doesn't have to be "db_create_table", rather it can be anything - this is the name used in actual database statements in source code (like run-query), so if you change it, make sure you change it everywhere. Create it:
echo "user=$(whoami) dbname=db_create_table"  > db_create_table

The above is a standard postgres connection string that describes the login to the database you created. Since Vely uses native PostgreSQL connectivity, you can specify any connection string that your database lets you.
Build application
Use vv utility to make the application:
vv -q --db=postgres:db_create_table

Note usage of --db option to specify PostgreSQL database and the database configuration file name.
Start your application server
To start the application server for your web application use vf FastCGI process manager. The application server will use a Unix socket to communicate with the web server (i.e. a reverse-proxy):
vf -w 3 create_table

This will start 3 daemon processes to serve the incoming requests. You can also start an adaptive server that will increase the number of processes to serve more requests, and gradually reduce the number of processes when they're not needed:
vf create_table

See vf for more options to help you achieve best performance.

If you want to stop your application server:
vf -m quit create_table

Setup web server
This shows how to connect your application listening on a Unix socket (started with vf) to Nginx web server.

- Step 1:
You will need to edit the Nginx configuration file. For Ubuntu and similar:
sudo vi /etc/nginx/sites-enabled/default

while on Fedora and other systems it might be at:
sudo vi /etc/nginx/nginx.conf


Add the following in the "server {}" section ("/create_table" is the application path (see request_URL) and "create_table" is your application name):
location /create_table { include /etc/nginx/fastcgi_params; fastcgi_pass  unix:///var/lib/vv/create_table/sock/sock; }

- Step 2:
Finally, restart Nginx:
sudo systemctl restart nginx

Note: you must not have any other URL resource that starts with "/create_table" (such as for example "/create_table.html" or "/create_table_something" etc.) as the web server will attempt to pass them as a reverse proxy request, and they will likely not work. If you need to, you can change the application path to be different from "/create_table", see request_URL.
Access application server from the browser
Use the following URL(s) to access your application server from a client like browser (see request_URL). Use actual IP or web address instead of 127.0.0.1 if different.
#Create, use and drop table, show output 
http://127.0.0.1/create_table/create_table

Note: if your server is on the Internet and it has a firewall, you may need to allow HTTP traffic - see ufw, firewall-cmd etc.
Access application server from command line
To access your application server from command line (instead through web browser/web server), use "cgi-fcgi" to see the application response:
#Create, use and drop table, show output 
export REQUEST_METHOD=GET
export SCRIPT_NAME='/create_table'
export PATH_INFO='/create_table'
export QUERY_STRING=''
cgi-fcgi -connect /var/lib/vv/create_table/sock/sock /

Note: to suppress output of HTTP headers, add this before running "cgi-fcgi":
export VV_SILENT_HEADER=yes

If you need to, you can also run your application as a CGI program.
Run program from command line
Execute the following to run your application from command line (as a command-line utility):
#Create, use and drop table, show output 
export REQUEST_METHOD=GET
export SCRIPT_NAME='/create_table'
export PATH_INFO='/create_table'
export QUERY_STRING=''
/var/lib/vv/bld/create_table/create_table

Note: to suppress output of HTTP headers, add this before running /var/lib/vv/bld/create_table/create_table program:
export VV_SILENT_HEADER=yes

Note: if running your program as a command-line utility is all you want, you don't need to run an application server.
Files
You are now done with the example! What follows are the source files in this project so you can examine how it works:
Setup database objects (setup.sql)
Create a table used in the example:
create table if not exists my_table (col1 bigint);

Creating, using and dropping a table (create_table.vely)
DDL (Data Definition Language), DML (Data Definition Language) and SELECT SQL is used in this example. A table is dropped (if it exists), created, data inserted, queried, and then table is dropped again:
// SPDX-License-Identifier: EPL-2.0
// Copyright 2018 DaSoftver LLC.

#include "vely.h"

void create_table()
{

    out-header default

    // Drop existing table
    run-query @db_create_table = "drop table if exists my_table"  error define err error-text define err_text no-loop
    if (strcmp (err, "0")) {
        report-error "Trouble dropping table, error [%s], error text [%s]", err, err_text
    }
    @Table dropped!<br/>

    // Create table
    run-query @db_create_table =  "create table if not exists my_table (my_number bigint)"  error err error-text err_text no-loop
    if (strcmp (err, "0")) {
        report-error "Trouble creating table, error [%s], error text [%s]", err, err_text
    }
    @Table created!<br/>

    // Insert data into table
    run-query @db_create_table="insert into my_table (my_number) values ('%s'), ('%s'), ('%s')" : "100", "200", "400" affected-rows define nrows
    end-query
    @Added <<pf-out "%lld", nrows>> rows!<br/>

    // Select data we just inserted
    @Getting data we inserted:<br/>
    run-query @db_create_table="select my_number from my_table" output my_number
        @I got number <<query-result my_number>>!<br/>
    end-query

    // Drop the table again
    run-query @db_create_table =  "drop table if exists my_table" error err error-text err_text no-loop
    if (strcmp (err, "0")) {
        report-error "Trouble creating table, error [%s], error text [%s]", err, err_text
    }
    @Table dropped!<br/>

}

See also
Examples ( example_cookies   example_create_table   example_docker   example_file_manager   example_form   example_hash   example_hello_world   example_json   example_multitenant_SaaS   examples   example_sendmail   example_shopping   example_stock   example_utility   example_write_report  )  SEE ALL (documentation)
Example: docker

The following is a bash script that will create a base Vely image as well as a base image for a Vely application, and setup Apache server and MariaDB database. To run this example, you must have Docker, Apache and MariaDB installed on Ubuntu 20 and up or Red Hat 9 or up. You can use docker or podman (substitute podman for docker).

To run this example on Ubuntu:
tar xvf /usr/share/vely/examples/velydocker.tar.gz
cd velydocker
export DBROOTPWD="<mariadb root database pwd>"
export VV_APACHE_CONFIG="/etc/apache2/apache2.conf"
export VV_APACHE_SERVICE="apache2"
./runvelydocker

DBROOTPWD environment variable should have a MariaDB root database password (or empty if passwordless). VV_APACHE_CONFIG should be the location of Apache configuration file, and VV_APACHE_SERVICE the name of Apache service. You must have sudo privilege to run this example. The settings above are for Ubuntu/Debian, but you can change them for other distros.

The script will create a container with your application installed. You can remove the application source code from the container in "runit" script in order to distribute only the application binaries. You can then run this container on any machine.

Note that Vely demo application source code being containerized is in "docker" directory. You can replace it with your own source code; see application_setup on building application with Vely.

Once the image is built and container started, use the following link(s) to run it, or if you can't, use curl to see the demo application response (the demo is example_stock):
#Add stock ticker 'XYZ' with stock price 450 
http://127.0.0.1/velydemo/docker_stock?action=add&stock_name=XYZ&stock_price=450

#Display list of stock tickers 
http://127.0.0.1/velydemo/docker_stock?action=show


Files
File Dockerfile:
#
#Create base vely image. Install Vely on top of Ubuntu 20.04
#
FROM ubuntu:20.04
ENV TZ=America/Phoenix
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
RUN apt update
RUN apt install -y apt-transport-https ca-certificates wget sudo
RUN wget  -qO - https://vely.dev//pkg/OPENPGP|sudo tee /usr/share/keyrings/vely.asc >/dev/null
ARG arch
RUN sudo bash -c "echo 'deb [signed-by=/usr/share/keyrings/vely.asc] https://vely.dev//pkg/ubuntu_20_$arch/latest ubuntu_20_$arch vely' >/etc/apt/sources.list.d/vely.list"
RUN sudo apt update
RUN sudo apt install -y vely

File velyapp.dockerfile:
#
#Create image that will be used to compile and link an application. Source code is copied from host,
#application is setup, and the source code is deleted (remove this step if you want to ship the source code).
#After that the image is ready to run in a container. Here, the application name is "velydemo"; change it to
#fit your application. The port used in 2300, you can change that as well.
#Customize the ENTRYPOINT command to fit your runtime.
#
FROM vely
#create vely user and give it limited sudo powers
RUN useradd -ms /bin/bash vely && echo "vely:vely" | chpasswd
RUN echo "vely ALL=(ALL) NOPASSWD: /usr/bin/vf" >> /etc/sudoers
#default user
USER vely
#default working dir
WORKDIR /home/vely
EXPOSE 2300
#copy over source code and make app
COPY ./docker/* /home/vely/
#this is to run app with docker run
ENTRYPOINT [ "./runit" ]

File runvelydocker:
#!/bin/bash

#
#Vely Docker example. 
#

#if you want to rebuild all from scratch, switch CACHE to the commented option
#that is useful when installing new Vely version in the base image
CACHE=""
#CACHE="--no-cache"

set -eE -o functrace
trap 'echo "Error: status $?, $(caller), line ${LINENO}"' ERR


#
#Build vely base image
#
sudo docker build --build-arg arch=$(uname -m) $CACHE -t vely .

#
#Create persistent named volume for /var/lib/vv in the container
#
sudo docker volume create velyhome

#
#Stop current container(s) and remove current images
#
sudo docker stop velyapp || true
sudo docker rmi --force velyapp || true

#
#Build a container for your application
#
sudo docker build $CACHE -t velyapp -f velyapp.dockerfile .

#
#Add ProxyPass to Apache configuration on the host to be able to access the application
#Web server is on the host for simplicity in this example, but it can be in its own container
#
sudo sed -i "/^ProxyPass \"\/velydemo\" .*$/d" $VV_APACHE_CONFIG
echo 'ProxyPass "/velydemo" fcgi://127.0.0.1:2300/' | sudo tee -a $VV_APACHE_CONFIG >/dev/null
sudo a2enmod proxy || true
sudo a2enmod proxy_fcgi || true
sudo service $VV_APACHE_SERVICE restart

#
#Create a MariaDB database on the host, setup the user and create database objects (a table in this case).
#Database is on the host for simplicity in this example, but it can be in its own container
#
MKDB=$(echo "create database if not exists velydb; 
create user if not exists velyuser identified by 'pwd';
grant create,alter,drop,select,insert,delete,update on velydb.* to velyuser;
use velydb;
source docker/setup.sql;")

#
#Execute database setup. 
#
if [ "$DBROOTPWD" == "" ]; then
    echo $MKDB | sudo mysql
else
    echo $MKDB | sudo mysql -u root -p$DBROOTPWD
fi

mkdir -p $HOME/libvv
#
#Now that Vely application container is setup, web server and database setup as well, run it in a container.
#Note that "host" network interface is used for simplicity.
sudo docker run --name velyapp -d -v velyhome:/var/lib/vv  --network="host" --rm velyapp


exit 0

File runit:
#!/bin/bash
#Create Vely application and run it in foreground for docker
#demout has a log of execution
sudo vf -i -u $(whoami) velydemo
vv -c
vv -q --db=mariadb:db -s
vv -c
vf -f -w3 -p2300 velydemo > demout

See also
Examples ( example_cookies   example_create_table   example_docker   example_file_manager   example_form   example_hash   example_hello_world   example_json   example_multitenant_SaaS   examples   example_sendmail   example_shopping   example_stock   example_utility   example_write_report  )  SEE ALL (documentation)
Example: file manager

This is a file manager application.
Files metadata is kept in the database (name, path, description, size, extension), while the files themselves are kept in Vely file_storage.

In a nutshell: PostgreSQL; web browser; Apache; TCP sockets; 6 source files, 169 lines of code.
Screenshots of application
This is the page where application user always goes first. Here, your application will display a web link that shows all files kept in the database so far, as well as HTML form to upload a new file where you can enter its description and choose a file from your computer or device. Here's a snapshot of what the page will look like - this is a basic layout and you can change the code to style it however you wish:

Vely

Files that have been saved will be shown in a list. Next to each file name will be its size, and links to show the file (display it or download it if can't be displayed), and a link to delete the file:

Vely

When a file is about to be deleted, a confirmation is asked of user:

Vely

Once confirmed, deletion of a file is carried out:

Vely

Setup prerequisites
Install Vely - you can use standard packaging tools such as apt, dnf, pacman or zypper.

Because they are used in this example, you will need to install Apache as a web server and PostgreSQL as a database.

After installing Vely, turn on syntax highlighting in vim if you're using it:
vv -m

Get the source code
The source code is a part of Vely installation. It is a good idea to create a separate source code directory for each application (and you can name it whatever you like). In this case, unpacking the source code will do that for you:
tar xvf $(vv -o)/examples/file_manager.tar.gz
cd file_manager

Setup application
The very first step is to create an application. The application will be named "file_manager", but you can name it anything (if you do that, change it everywhere). It's simple to do with vf:
sudo vf -i -u $(whoami) file_manager

This will create a new application home (which is "/var/lib/vv/file_manager") and do the application setup for you. Mostly that means create various subdirectories in the home folder, and assign them privileges. In this case only current user (or the result of "whoami" Linux command) will own those directories with 0700 privileges; it means a secure setup.
Setup the database
Before any coding, you need some place to store the information used by the application. First, you will create PostgreSQL database "db_file_manager". You can change the database name, but remember to change it everywhere here. And then, you will create database objects in the database.

Note the example here uses peer-authentication, which is the default on all modern PostgreSQL installations - this means the database user name is the same as the Operating System user name.

Execute the following in PostgreSQL database as root (using psql utility):
echo "create user $(whoami);
create database db_file_manager with owner=$(whoami);
grant all on database db_file_manager to $(whoami);
\q
" | sudo -u postgres psql

Next, login to database db_file_manager and create the database objects for the application:
psql -d db_file_manager -f setup.sql

Connect Vely to a database
In order to let Vely know where your database is and how to log into it, you will create database_config_file named "db_file_manager". This name doesn't have to be "db_file_manager", rather it can be anything - this is the name used in actual database statements in source code (like run-query), so if you change it, make sure you change it everywhere. Create it:
echo "user=$(whoami) dbname=db_file_manager"  > db_file_manager

The above is a standard postgres connection string that describes the login to the database you created. Since Vely uses native PostgreSQL connectivity, you can specify any connection string that your database lets you.
Build application
Use vv utility to make the application:
vv -q --db=postgres:db_file_manager

Note usage of --db option to specify PostgreSQL database and the database configuration file name.
Start your application server
To start the application server for your web application use vf FastCGI process manager. The application server will use a TCP socket 2310 to communicate with the web server (i.e. a reverse-proxy).
vf -p 2310 -w 3 file_manager

This will start 3 daemon processes to serve the incoming requests. You can also start an adaptive server that will increase the number of processes to serve more requests, and gradually reduce the number of processes when they're not needed:
vf -p 2310 file_manager

See vf for more options to help you achieve best performance.

If you want to stop your application server:
vf -m quit file_manager

- SELinux and TCP ports
If you are using SELinux and have it enabled, then you must make the TCP socket accessible to the web server and Vely:
sudo semanage port -a -t vvport_t -p tcp 2310

Without this step, the application will appear to not work.
Setup web server
This shows how to connect your application listening at TCP port 2310 (started with "-p" option in vf) to Apache web server.

- Step 1:
To setup Apache as a reverse proxy and connect your application to it, you need to enable FastCGI proxy support, which generally means "proxy" and "proxy_fcgi" modules - this is done only once:
- Step 2:
Edit the Apache configuration file:
Add this to the end of file ("/file_manager" is the application path, see request_URL):
ProxyPass "/file_manager" fcgi://127.0.0.1:2310/

- Step 3:
Finally, restart Apache. On Debian systems (like Ubuntu) or OpenSUSE:
sudo systemctl restart apache2

On Fedora systems (like RedHat) and Arch Linux:
sudo systemctl restart httpd

Note: you must not have any other URL resource that starts with "/file_manager" (such as for example "/file_manager.html" or "/file_manager_something" etc.) as the web server will attempt to pass them as a reverse proxy request, and they will likely not work. If you need to, you can change the application path to be different from "/file_manager", see request_URL.

Access application server from the browser
Use the following URL(s) to access your application server from a client like browser (see request_URL). Use actual IP or web address instead of 127.0.0.1 if different.
#Start the file management application 
http://127.0.0.1/file_manager/start

Note: if your server is on the Internet and it has a firewall, you may need to allow HTTP traffic - see ufw, firewall-cmd etc.
Files
You are now done with the example! What follows are the source files in this project so you can examine how it works:
Storing file information in the database (setup.sql)
The information about files, such as file names, sizes, extensions, descriptions etc. is stored in the table "files". Where will the files themselves be stored?

Well, you could store them in the database, but that's not the most optimal way. You could store them anywhere on a file system, but Vely has a very simple (and generally transparent and automatic) way of storing files, which is Vely's file_storage. When a file is uploaded, it's automatically stored there; and you can also create files there as well.
create table if not exists files (fileName varchar(100), localPath varchar(300), extension varchar(10), description varchar(200), fileSize int, fileID bigserial primary key);

Application landing page (start.vely)
This code displays the page where application user always goes first. One thing you'll notice often in Vely code is the use of "@" to output text (in this case HTML page text) - "@" is an output_statement, and it makes it easy and readable. In general, Vely does not use traditional API for its functionality, rather it uses statement_APIs.
// SPDX-License-Identifier: EPL-2.0
// Copyright 2018 DaSoftver LLC.
#include "vely.h" // must always be here

// Upload and list/download files
void start ()
{

   out-header default

   @<h2>File Manager</h2>

   @To manage the uploaded files, <a href="<<p-path>>?req=list">click here.</a><br/>
   @<br/>

   // Form to upload a file

   @<form action="<<p-path>>" method="POST" enctype="multipart/form-data">
   @    <input type="hidden" name="req" value="upload">

   @    <label for="file_description">File description:</label><br>
   @    <textarea name="filedesc" rows="3" columns="50"></textarea><br/>
   @    <br/>
   @    <label for="filename">File:</label>
   @    <input type="file" name="file" value=""><br><br>

   @    <input type="submit" value="Submit">
   @</form>

}

Uploading files (upload.vely)
The next source code file is "upload.vely", which will upload a file when a user selects a file and clicks "Submit" button, which is in "start.vely" file. When you upload a file into Vely application, it's just an input-parameter, just like it would be with any GET or POST request parameter. In addition, Vely will automatically store the uploaded file into file directory (see file_storage) which is optimized for speed of access, and you can either keep it there, move it elsewhere or do whatever you want. All of this make is easy to work with files.

The above code will get the information about the file uploaded (such as file description, as well as file name, location, size, extension) - which is provided by Vely as input parameters. Then, use run-query statement to insert such information into the database table you created. From now on, the information about a file is stored in the database (and that includes the location where it was uploaded), and the actual file is in the file system. This allows fast access to files and easier management.

And finally, the message will be sent back to web client (such as a browser) that you've saved the file.
// SPDX-License-Identifier: EPL-2.0
// Copyright 2018 DaSoftver LLC.
#include "vely.h" // must always be here

// Upload the file
void upload ()
{
   out-header default

   // file description from the upload form
   input-param filedesc
   // file name
   input-param file_filename
   // the path to uploaded file
   input-param file_location
   // size in bytes
   input-param file_size
   // the file extension
   input-param file_ext
   VV_UNUSED (file_ext);

   @<h2>Uploading file</h2>

   // insert the information about the file into the database
   run-query @db_file_manager="insert into files (fileName, localPath, extension, description, fileSize) values ('%s', '%s', '%s', '%s', '%s')": file_filename, file_location, file_ext, filedesc, file_size
   end-query

   @File <<p-web file_filename>> of size <<p-web file_size>> is stored on server at <<p-web file_location>>. File description is <<p-web filedesc>>.<hr/>
}

Listing files (list.vely)
For files that have been saved, this code shows them in a list. Next to each file name will be its size, and links to show the file (display it or download it if can't be displayed), and a link to delete the file. To produce the list of files, the table "files" is queried, and the list of files displayed as an HTML table.
// SPDX-License-Identifier: EPL-2.0
// Copyright 2018 DaSoftver LLC.
#include "vely.h" // must always be here

// List files
void list ()
{
   // List current files in the database
   out-header default
   @<h2>List of files</h2>
   @To add a file, <a href="<<p-path>>?req=start">click here</a><br/><br/>
   @<table border="1">
   @<tr>
   @    <td>File</td><td>Description</td><td>Size</td><td>Show</td><td>Delete</td>
   @</tr>

   // get the list of files from the database
   run-query @db_file_manager="select fileName, description, fileSize, fileID from files order by fileSize desc" output fileName, description, fileSize, fileID
       query-result fileName to define file_name
       query-result fileID to define file_ID
       query-result description to define description
       query-result fileSize to define file_size
       // construct table output with links to Show and Delete files
       @<tr>
       @    <td><<p-web file_name>></td><td><<p-web description>><td><<p-web file_size>></td>
       @    <td><a href="<<p-path>>?req=download&amp;file_id=<<p-url file_ID>>">Show</a></td>
       @    <td><a href="<<p-path>>?req=delete&amp;action=confirm&amp;file_id=<<p-url file_ID>>">Delete</a></td>
       @</tr>
   end-query
   @</table>
}

Downloading files (download.vely)
When a link to download a file is clicked in the list of files, the following code is called. The ID of a file requested is queried in table "files", the file path on server is found, and the file is sent to the client using send-file statement.
// Copyright 2018 DaSoftver LLC.
#include "vely.h" // must always be here

// Download a file
void download ()
{
   // Show or download a file (its ID is in the database)
   input-param file_id
   char *local_path=NULL;
   char *ext = NULL;

   // get the local path and extension of the file
   run-query @db_file_manager="select localPath,extension from files where fileID='%s'" output localPath, extension : file_id row-count define num_files
       query-result  localPath to local_path
       query-result  extension to ext
   end-query

   // check we can find the file
   if (num_files != 1) {
       out-header default
       @Cannot find this file!<hr/>
       return;
   }

   // display JPG or PDF files in the browser, or download any other kind
   if (!strcmp (ext, ".jpg")) {
       send-file local_path headers content-type "image/jpg"
   } else if (!strcmp (ext, ".pdf")) {
       send-file local_path headers content-type "application/pdf"
   } else {
       send-file local_path headers content-type "application/octet-stream" download
   }
}

Deleting files (delete.vely)
Finally, deleting a file (by clicking on delete link in the list of files) is handled in the following code.

When a file is about to be deleted, a confirmation is asked of user - input parameter "action" is "confirm" in this case.

Once confirmed, deletion of a file is carried out; input parameter "action" is "delete" then (you can name parameter "action" anything you want).

Note that deletion is done as a transaction, only if both the record in table "files" is deleted and the actual file deleted. If the file cannot be deleted, transaction is rollback-ed.
// SPDX-License-Identifier: EPL-2.0
// Copyright 2018 DaSoftver LLC.
#include "vely.h" // must always be here

// Delete the file
void delete ()
{
   out-header default
   @<h2>Delete a file</h2>
   input-param action
   input-param file_id
   char *file_name = NULL;
   char *desc = NULL;
   char *local_path = NULL;

   // Get file information from the database
   run-query @db_file_manager="select fileName, localPath, description  from files where fileID='%s'" output fileName, localPath, description : file_id
       query-result fileName to file_name
       query-result description to desc
       query-result localPath to local_path
   end-query

   if (!strcmp (action, "confirm")) { // get file information to confirm what will be deleted
       @Are you sure you want to delete file <<p-web file_name>> (<<p-web desc>>)? Click <a href="<<p-path>>?req=delete&amp;action=delete&amp;file_id=<<p-url file_id>>">Delete</a> or click the browser's Back button to go back.<br/>

   } else if (!strcmp (action, "delete")) { // actual delete file, once confirmed
       begin-transaction @db_file_manager
       run-query @db_file_manager= "delete from files where fileID='%s'" : file_id error define err no-loop
       if (atol(err) != 0) {
           @Could not delete the file (error <<p-web err>>)
           rollback-transaction @db_file_manager
       } else {
           delete-file local_path status define st
           if (st == VV_OKAY) {
               commit-transaction @db_file_manager
               @File deleted. Go back to <a href="<<p-path>>?req=start">start page</a>
           } else {
               rollback-transaction @db_file_manager
               @File could not be deleted, error <<pf-web "%lld", st>>
           }
       }
   } else {
       @Unrecognized action <<p-web action>>
   }
}

See also
Examples ( example_cookies   example_create_table   example_docker   example_file_manager   example_form   example_hash   example_hello_world   example_json   example_multitenant_SaaS   examples   example_sendmail   example_shopping   example_stock   example_utility   example_write_report  )  SEE ALL (documentation)
Example: form

This application is a guest tracking database. The user can:

In a nutshell: PostgreSQL; web browser; Apache; Unix sockets; 4 source files, 86 lines of code.
Screenshots of application
Users can sign the guest book via HTML form:

Vely

When a Submit button is clicked, entered data is added to the guest book:

Vely

This shows the entire guest book:

Vely

Setup prerequisites
Install Vely - you can use standard packaging tools such as apt, dnf, pacman or zypper.

Because they are used in this example, you will need to install Apache as a web server and PostgreSQL as a database.

After installing Vely, turn on syntax highlighting in vim if you're using it:
vv -m

Get the source code
The source code is a part of Vely installation. It is a good idea to create a separate source code directory for each application (and you can name it whatever you like). In this case, unpacking the source code will do that for you:
tar xvf $(vv -o)/examples/form.tar.gz
cd form

Setup application
The very first step is to create an application. The application will be named "form", but you can name it anything (if you do that, change it everywhere). It's simple to do with vf:
sudo vf -i -u $(whoami) form

This will create a new application home (which is "/var/lib/vv/form") and do the application setup for you. Mostly that means create various subdirectories in the home folder, and assign them privileges. In this case only current user (or the result of "whoami" Linux command) will own those directories with 0700 privileges; it means a secure setup.
Setup the database
Before any coding, you need some place to store the information used by the application. First, you will create PostgreSQL database "db_form". You can change the database name, but remember to change it everywhere here. And then, you will create database objects in the database.

Note the example here uses peer-authentication, which is the default on all modern PostgreSQL installations - this means the database user name is the same as the Operating System user name.

Execute the following in PostgreSQL database as root (using psql utility):
echo "create user $(whoami);
create database db_form with owner=$(whoami);
grant all on database db_form to $(whoami);
\q
" | sudo -u postgres psql

Next, login to database db_form and create the database objects for the application:
psql -d db_form -f setup.sql

Connect Vely to a database
In order to let Vely know where your database is and how to log into it, you will create database_config_file named "db_form". This name doesn't have to be "db_form", rather it can be anything - this is the name used in actual database statements in source code (like run-query), so if you change it, make sure you change it everywhere. Create it:
echo "user=$(whoami) dbname=db_form"  > db_form

The above is a standard postgres connection string that describes the login to the database you created. Since Vely uses native PostgreSQL connectivity, you can specify any connection string that your database lets you.
Build application
Use vv utility to make the application:
vv -q --db=postgres:db_form

Note usage of --db option to specify PostgreSQL database and the database configuration file name.
Start your application server
To start the application server for your web application use vf FastCGI process manager. The application server will use a Unix socket to communicate with the web server (i.e. a reverse-proxy):
vf -w 3 form

This will start 3 daemon processes to serve the incoming requests. You can also start an adaptive server that will increase the number of processes to serve more requests, and gradually reduce the number of processes when they're not needed:
vf form

See vf for more options to help you achieve best performance.

If you want to stop your application server:
vf -m quit form

Setup web server
This shows how to connect your application listening on a Unix socket (started with vf) to Apache web server.

- Step 1:
To setup Apache as a reverse proxy and connect your application to it, you need to enable FastCGI proxy support, which generally means "proxy" and "proxy_fcgi" modules - this is done only once:
- Step 2:
Edit the Apache configuration file:
Add this to the end of file ("/form" is the application path (see request_URL) and "form" is your application name):
ProxyPass "/form" unix:///var/lib/vv/form/sock/sock|fcgi://localhost/form

- Step 3:
Finally, restart Apache. On Debian systems (like Ubuntu) or OpenSUSE:
sudo systemctl restart apache2

On Fedora systems (like RedHat) and Arch Linux:
sudo systemctl restart httpd

Note: you must not have any other URL resource that starts with "/form" (such as for example "/form.html" or "/form_something" etc.) as the web server will attempt to pass them as a reverse proxy request, and they will likely not work. If you need to, you can change the application path to be different from "/form", see request_URL.

Access application server from the browser
Use the following URL(s) to access your application server from a client like browser (see request_URL). Use actual IP or web address instead of 127.0.0.1 if different.
#Display form 
http://127.0.0.1/form/form_display

#Show data entered 
http://127.0.0.1/form/display_data

Note: if your server is on the Internet and it has a firewall, you may need to allow HTTP traffic - see ufw, firewall-cmd etc.
Files
You are now done with the example! What follows are the source files in this project so you can examine how it works:
Setup database objects (setup.sql)
Create table to hold the first and last name of the guest:
create table if not exists names (firstName varchar(30), lastName varchar(40));

Web form (form_display.vely)
This is a simple HTML web form where first and last name is collected and sent to the server via POST mechanism. Note the path segment after the application path (set with p-path), which is "/form_post", meaning this request will be handled in source file "form_post.vely":
// SPDX-License-Identifier: EPL-2.0
// Copyright 2018 DaSoftver LLC.

#include "vely.h" // must always be here


// 
// Display a web form
//
void form_display ()
{

     out-header default

    @<h2>Enter your name</h2>

    @<form action="<<p-path>>/form_post" method="POST">

    @    <label for="first_name">First name:</label><br>
    @    <input type="text" name="first_name" value=""><br>

    @    <label for="last_name">Last name:</label><br>
    @    <input type="text" name="last_name" value=""><br><br>

    @    <input type="submit" value="Submit">
    @ </form>
}

Submitting web form (form_post.vely)
input-param is used to obtain input parameters sent here from the web form created in "form_display.vely". The data is inserted into the database and the status message output back to web client (i.e. browser):
// SPDX-License-Identifier: EPL-2.0
// Copyright 2018 DaSoftver LLC.

#include "vely.h" // must always be here


// 
// Post input from a web form
//
void form_post ()
{

    out-header default

    input-param first_name
    input-param last_name
    run-query @db_form="insert into names(firstName, lastName) values ('%s','%s')" : first_name, last_name error define err
    end-query

    if (strcmp(err, "0")) {
        @Could not add name, error [<<p-out err>>]
        exit-request
    }

    @Name added to the database<hr/>
}

Display database data in table format in browser (display_data.vely)
This shows typical web output from querying a database. SELECT statement in run-query produces a result set, which is displayed in a tabular format in the browser:
// SPDX-License-Identifier: EPL-2.0
// Copyright 2018 DaSoftver LLC.

#include "vely.h"


// 
// Display table data
//
void display_data ()
{

    out-header default

    @<h2>List of names</h2>

    @<table>
        run-query @db_form="select firstName, lastName from names order by lastName,firstName" output firstName,lastName
    @        <tr>
    @            <td>
                    query-result lastName
    @            </td>
    @            <td>
                    query-result firstName
    @            </td>
    @        </tr>
        end-query
    @</table>

}

See also
Examples ( example_cookies   example_create_table   example_docker   example_file_manager   example_form   example_hash   example_hello_world   example_json   example_multitenant_SaaS   examples   example_sendmail   example_shopping   example_stock   example_utility   example_write_report  )  SEE ALL (documentation)
Example: hash

You will create your own hash server in this example. A REST API will enable end-user to add key/data pairs, query and delete them. The data is in memory-only; a more involved example could be constructed to persist the data in some form. This is an extremely fast, single-process hash server.

In a nutshell:  web browser; Apache; REST API; Unix sockets; 3 source files, 59 lines of code.
Setup prerequisites
Install Vely - you can use standard packaging tools such as apt, dnf, pacman or zypper.

Because it is used in this example, you will need to install Apache as a web server.

After installing Vely, turn on syntax highlighting in vim if you're using it:
vv -m

Get the source code
The source code is a part of Vely installation. It is a good idea to create a separate source code directory for each application (and you can name it whatever you like). In this case, unpacking the source code will do that for you:
tar xvf $(vv -o)/examples/hash.tar.gz
cd hash

Setup application
The very first step is to create an application. The application will be named "hash", but you can name it anything (if you do that, change it everywhere). It's simple to do with vf:
sudo vf -i -u $(whoami) hash

This will create a new application home (which is "/var/lib/vv/hash") and do the application setup for you. Mostly that means create various subdirectories in the home folder, and assign them privileges. In this case only current user (or the result of "whoami" Linux command) will own those directories with 0700 privileges; it means a secure setup.
Build application
Use vv utility to make the application:
vv -q

Start your application server
To start the application server for your web application use vf FastCGI process manager. The application server will use a Unix socket to communicate with the web server (i.e. a reverse-proxy):
vf -w 1 hash

This will start 1 daemon process to serve the incoming requests.

See vf for more options to help you achieve best performance.

If you want to stop your application server:
vf -m quit hash

Setup web server
This shows how to connect your application listening on a Unix socket (started with vf) to Apache web server.

- Step 1:
To setup Apache as a reverse proxy and connect your application to it, you need to enable FastCGI proxy support, which generally means "proxy" and "proxy_fcgi" modules - this is done only once:
- Step 2:
Edit the Apache configuration file:
Add this to the end of file ("/hash" is the application path (see request_URL) and "hash" is your application name):
ProxyPass "/hash" unix:///var/lib/vv/hash/sock/sock|fcgi://localhost/hash

- Step 3:
Finally, restart Apache. On Debian systems (like Ubuntu) or OpenSUSE:
sudo systemctl restart apache2

On Fedora systems (like RedHat) and Arch Linux:
sudo systemctl restart httpd

Note: you must not have any other URL resource that starts with "/hash" (such as for example "/hash.html" or "/hash_something" etc.) as the web server will attempt to pass them as a reverse proxy request, and they will likely not work. If you need to, you can change the application path to be different from "/hash", see request_URL.

Access application server from the browser
Use the following URL(s) to access your application server from a client like browser (see request_URL). Use actual IP or web address instead of 127.0.0.1 if different.

Note: if your server is on the Internet and it has a firewall, you may need to allow HTTP traffic - see ufw, firewall-cmd etc.

- Test hash server with a bash script
To test the hash server, you can use "test_hash" bash script (see source code):
./test_hash

This will insert 1000 key/data value pairs, query them, delete and then query again. The result is:
Keys added
Keys queried
Keys deleted
Keys queried
Hash server test successful


- Use REST API from command line
Here is the REST API for your application.

Substitute the loopback "localhost" with the IP or web address of your server.

Note that in these REST calls, the application path is "/hash", and the request name is "/server", followed by URL payload (see request_URL).

The request method used is based on REST methodology, i.e. POST to create resource, GET to obtain, DELETE to delete it, etc. This is how you can use the API:
Files
You are now done with the example! What follows are the source files in this project so you can examine how it works:
Create hash server (_startup.vely)
Hash itself (i.e. the fast-access structure used to store and search data based on a key) is created on process startup in a startup_handler, which in this case allocates new-hash using unmanaged memory (see memory_handling). It means this hash will remain available across all the requests a process serves, which is exactly what a hash server needs.
#include "vely.h"

void _startup() {

    // Create memory available for the life of process
    manage-memory off
    new-hash define h size 1024
    manage-memory on

    // Register hash as process-wide data
    set-app process-data h

}

Hash server (server.vely)
This is the hash server. Because the data kept in hash needs to exist beyond a single request, we use unmanaged memory (see memory_handling). This way hash data stays allocated and available for the life of the server process.

The first step is to obtain the global_process_data via get-app statement. This global process data is the pointer to the hash data, which has been set in set-app in the startup_handler (_startup.vely).

Next, you get the input parameters  (see input-param); in this case "op" (operation requested), "key" (value of a key) and "data" (value of data). Then, depending on the operation requested, the data is added, deleted and retrieved (i.e. queried). Note that because input parameter values are automatically freed when the request is finished, they are copied before being stored in the hash.
#include "vely.h"

void server() {
    out-header default

    // Unmanaged memory, i.e. not released at the end of the request
    manage-memory off

    // Get hash allocated in _startup.vely
    vely_hash *h;
    get-app process-data to h

    // Get input parameters
    input-param op
    input-param key
    input-param data

    if (!strcmp (op, "add")) {
        // Add data to hash, make a copy as input params are request-scoped
        copy-string key to define c_key
        copy-string data to define c_data
        write-hash h key c_key value c_data
        @Added [<<p-out key>>]
    }
    else if (!strcmp (op, "delete")) {
        // Delete data and obtain the value deleted
        read-hash h key (key) value define val old-key define okey delete status define st
        if (st == VV_ERR_EXIST) {
            @Not found [<<p-out key>>]
        } else {
            // If found, then delete key and value
            @Deleted [<<p-out val>>]
            delete-mem val
            delete-mem okey
        }
    }
    else if (!strcmp (op, "query")) {
        // Query hash based on key value
        read-hash h key (key) value define val status define st
        if (st == VV_ERR_EXIST) {
            @Not found, queried [<<p-out key>>]
        } else {
            @Value [<<p-out val>>]
        }
    }
}

bash testing script (test_hash)
Test hash server by adding 1000 key/data pairs, querying to make sure the data is correct, deleting all of them and then querying to make sure they were all deleted.
#Restart hash server for this test
vf -m restart hash

#Add 1000 key/data pairs. Key value is 0,1,2... and data values are "data_0", "data_1", "data_2" etc.
for i in {1..1000}; do
    if [ "$(curl -s "http://localhost/hash/server/op/add/key/$i/data/data_$i")" != "Added [$i]" ]; then
        echo "Error adding key [$i]"
        exit -1
    fi
done

echo "Keys added"

#Query all 1000 keys and check that values retrieved are the correct ones.
for i in {1..1000}; do
    if [ "$(curl -s "http://localhost/hash/server/op/query/key/$i")" != "Value [data_$i]" ]; then
        echo "Error querying key [$i]"
        exit -1
    fi
done

echo "Keys queried"

#Delete all 1000 keys
ERR="0"
for i in {1..1000}; do
    if [ "$(curl -s "http://localhost/hash/server/op/delete/key/$i")" != "Deleted [data_$i]" ]; then
        echo "Error deleting key [$i]"
        exit -1
    fi
done

echo "Keys deleted"

#Query all 1000 keys and check that values retrieved are the correct ones.
for i in {1..1000}; do
    if [ "$(curl -s "http://localhost/hash/server/op/query/key/$i")" != "Not found, queried [$i]" ]; then
        echo "Error querying key [$i] after deletion."
        exit -1
    fi
done

echo "Keys queried"

echo "Hash server test successful"

See also
Examples ( example_cookies   example_create_table   example_docker   example_file_manager   example_form   example_hash   example_hello_world   example_json   example_multitenant_SaaS   examples   example_sendmail   example_shopping   example_stock   example_utility   example_write_report  )  SEE ALL (documentation)
Example: hello world

This is a simple Hello World example. It explains basics of making applications as well as tracing and debugging them.

In a nutshell:  command line; web browser; Nginx; Unix sockets; 2 source files, 7 lines of code.
Screenshots of application
Hello World output:

Vely

Setup prerequisites
Install Vely - you can use standard packaging tools such as apt, dnf, pacman or zypper.

Because it is used in this example, you will need to install Nginx as a web server.

After installing Vely, turn on syntax highlighting in vim if you're using it:
vv -m

Get the source code
The source code is a part of Vely installation. It is a good idea to create a separate source code directory for each application (and you can name it whatever you like). In this case, unpacking the source code will do that for you:
tar xvf $(vv -o)/examples/hello_world.tar.gz
cd hello_world

Setup application
The very first step is to create an application. The application will be named "hello_world", but you can name it anything (if you do that, change it everywhere). It's simple to do with vf:
sudo vf -i -u $(whoami) hello_world

This will create a new application home (which is "/var/lib/vv/hello_world") and do the application setup for you. Mostly that means create various subdirectories in the home folder, and assign them privileges. In this case only current user (or the result of "whoami" Linux command) will own those directories with 0700 privileges; it means a secure setup.
Build application
Use vv utility to make the application:
vv -q

Start your application server
To start the application server for your web application use vf FastCGI process manager. The application server will use a Unix socket to communicate with the web server (i.e. a reverse-proxy):
vf -w 3 hello_world

This will start 3 daemon processes to serve the incoming requests. You can also start an adaptive server that will increase the number of processes to serve more requests, and gradually reduce the number of processes when they're not needed:
vf hello_world

See vf for more options to help you achieve best performance.

If you want to stop your application server:
vf -m quit hello_world

Setup web server
This shows how to connect your application listening on a Unix socket (started with vf) to Nginx web server.

- Step 1:
You will need to edit the Nginx configuration file. For Ubuntu and similar:
sudo vi /etc/nginx/sites-enabled/default

while on Fedora and other systems it might be at:
sudo vi /etc/nginx/nginx.conf


Add the following in the "server {}" section ("/hello_world" is the application path (see request_URL) and "hello_world" is your application name):
location /hello_world { include /etc/nginx/fastcgi_params; fastcgi_pass  unix:///var/lib/vv/hello_world/sock/sock; }

- Step 2:
Finally, restart Nginx:
sudo systemctl restart nginx

Note: you must not have any other URL resource that starts with "/hello_world" (such as for example "/hello_world.html" or "/hello_world_something" etc.) as the web server will attempt to pass them as a reverse proxy request, and they will likely not work. If you need to, you can change the application path to be different from "/hello_world", see request_URL.
Access application server from the browser
Use the following URL(s) to access your application server from a client like browser (see request_URL). Use actual IP or web address instead of 127.0.0.1 if different.
#Hello world 
http://127.0.0.1/hello_world/hello

Note: if your server is on the Internet and it has a firewall, you may need to allow HTTP traffic - see ufw, firewall-cmd etc.
Access application server from command line
To access your application server from command line (instead through web browser/web server), use "cgi-fcgi" to see the application response:
#Hello world 
export REQUEST_METHOD=GET
export SCRIPT_NAME='/hello_world'
export PATH_INFO='/hello'
export QUERY_STRING=''
cgi-fcgi -connect /var/lib/vv/hello_world/sock/sock /

Note: to suppress output of HTTP headers, add this before running "cgi-fcgi":
export VV_SILENT_HEADER=yes

If you need to, you can also run your application as a CGI program.
Run program from command line
Execute the following to run your application from command line (as a command-line utility):
#Hello world 
export REQUEST_METHOD=GET
export SCRIPT_NAME='/hello_world'
export PATH_INFO='/hello'
export QUERY_STRING=''
/var/lib/vv/bld/hello_world/hello_world

Note: to suppress output of HTTP headers, add this before running /var/lib/vv/bld/hello_world/hello_world program:
export VV_SILENT_HEADER=yes

Note: if running your program as a command-line utility is all you want, you don't need to run an application server.
Files
You are now done with the example! What follows are the source files in this project so you can examine how it works:
Hello World (hello.vely)
Hello World example outputs HTTP header and a simple message back to the browser.

The file name ("hello.vely") must match the function name implemented in it, so the function's signature is "void hello()". Call it with a URL like:
http://your-web-site/hello_world/hello

Note "/hello_world" path segment - that's the application path and by default the same as application name (see how_vely_works and request_URL). The following path segment is "/hello", which is the request name, and it must match the function name that implements it.

Note the "@" is used to do the output, it's the output_statement. You'll probably use that one a lot.

In this case there's only one request file. In a real world application, there'd likely be quite a few. Vely will pick up all .vely files and automatically make them into an application. While at it, it will generate a simple request dispatcher that routes an incoming request to the appropriate code; in this case a "hello" request will be routed to your "void hello()" function.
#include "vely.h"

void hello()
{
   out-header default
   @Hello World!
}

Changing your code
When you change your source code and recompile, vf will automatically pick up that the executable has changed and reload (see vf if you wish to change this behavior), making your web application live instantly. For example, change hello.vely to this:
#include "vely.h"

void hello()
{
   out-header default
   @Hello World! And a good day to you too!
}

and then recompile:
vv -q

Now try again from the browser:
http://127.0.0.1/hello_world/hello

The result:

Vely

Generated C code, errors, tracing and debugging
Generated C code is located at:
/var/lib/vv/bld/hello_world/__hello.o.c

It's useful if you'd like to see it or debug your code.

If you are using your Vely application as a command line program, you can debug it simply by using gdb.

Note that in order to see the debugging symbols (such as variable names), you should compile your application with --debug flag:
vv -q --debug

For FastCGI processes, you'd want to start your application as a single process, generally as few as possible, for example, stop your application first and then start as a single process:
vf -m quit hello_world
vf -w 1 hello_world

First, find out the PID of your process:
ps -ef|grep hello_world.fcgi

Then you can load gdb with your application and attach to it - assuming the PID is 12345:
sudo gdb /var/lib/vv/bld/hello_world/hello_world.fcgi
... att 12345

To break in your code (in this case in function hello()), do this in gdb:
br hello

If you'd like to break in Vely's request dispatcher, i.e. just before any request is handled:
br vely_dispatch_request

From then on, you can set breakpoints, continue, stop and do anything else to debug your program.

When you debug Vely applications in gdb, each Vely statement is a single execution unit. This is very useful when reporting errors, since they are reported referencing lines in .vely file, in this case hello.vely. Sometimes you'd want to use line numbers of the generated C file, in which case you'd compile with --c-lines flag:
vv -q --c-lines

Another way to debug your application is to use tracing (see trace-run). To enable tracing, compile your application with --trace flag:
vv -q --debug --trace

Trace files are in the trace directory, see how_vely_works. In our case, trace file would be in this directory:
/var/lib/vv/hello_world/app/trace

and trace file name for the process you're debugging would be (with timestamp being of the time it's written):
trace-12345-2022-05-17-22-46-54

In addition to this, learn more about debugging Vely applications.
See also
Examples ( example_cookies   example_create_table   example_docker   example_file_manager   example_form   example_hash   example_hello_world   example_json   example_multitenant_SaaS   examples   example_sendmail   example_shopping   example_stock   example_utility   example_write_report  )  SEE ALL (documentation)
Example: json

This example demonstrates parsing JSON text. The text parsed contains information about cities. JSON document includes an array of countries, which includes an array of states, each having an array of cities. The JSON data is also not fixed - some array members contain data that others don't. So the example is fairly involved.

Use of UTF8 Unicode data is also shown - some cities have such characters in their names.

JSON text comes from two different kinds of client requests: from an HTML form and also from Javascript/fetch(). The first one uses URL-query POST from a form. The second one demonstrates a request with "application/json" content type and use of request-body to get the HTTP request body.

Each JSON data node is retrieved using built-in hash statements (see new-json).

The JSON parsing demonstrates two ways to do it:

In a nutshell:  web browser; Nginx; Unix sockets; 5 source files, 160 lines of code.
Screenshots of application
This is the form where you can enter JSON text to be parsed:

Vely

This is the result of submitting as 'Extract all data', where all data nodes are shown, along with their normalized names (that includes hierarchy and arrays), their values and types:

Vely

The following is the output of pushing 'Extract specific data' button. Specific data nodes are found, taking into account the hierarchy of data, and their names and values displayed:

Vely

You can call Vely code from Javascript fetch() - this is the output from post.html (see Access application... on how to run it):

Vely

Setup prerequisites
Install Vely - you can use standard packaging tools such as apt, dnf, pacman or zypper.

Because it is used in this example, you will need to install Nginx as a web server.

After installing Vely, turn on syntax highlighting in vim if you're using it:
vv -m

Get the source code
The source code is a part of Vely installation. It is a good idea to create a separate source code directory for each application (and you can name it whatever you like). In this case, unpacking the source code will do that for you:
tar xvf $(vv -o)/examples/json.tar.gz
cd json

Setup application
The very first step is to create an application. The application will be named "json", but you can name it anything (if you do that, change it everywhere). It's simple to do with vf:
sudo vf -i -u $(whoami) json

This will create a new application home (which is "/var/lib/vv/json") and do the application setup for you. Mostly that means create various subdirectories in the home folder, and assign them privileges. In this case only current user (or the result of "whoami" Linux command) will own those directories with 0700 privileges; it means a secure setup.
Build application
Use vv utility to make the application:
vv -q

Start your application server
To start the application server for your web application use vf FastCGI process manager. The application server will use a Unix socket to communicate with the web server (i.e. a reverse-proxy):
vf -w 3 json

This will start 3 daemon processes to serve the incoming requests. You can also start an adaptive server that will increase the number of processes to serve more requests, and gradually reduce the number of processes when they're not needed:
vf json

See vf for more options to help you achieve best performance.

If you want to stop your application server:
vf -m quit json

Setup web server
This shows how to connect your application listening on a Unix socket (started with vf) to Nginx web server.

- Step 1:
You will need to edit the Nginx configuration file. For Ubuntu and similar:
sudo vi /etc/nginx/sites-enabled/default

while on Fedora and other systems it might be at:
sudo vi /etc/nginx/nginx.conf


Add the following in the "server {}" section ("/json" is the application path (see request_URL) and "json" is your application name):
location /json { include /etc/nginx/fastcgi_params; fastcgi_pass  unix:///var/lib/vv/json/sock/sock; }

- Step 2:
Finally, restart Nginx:
sudo systemctl restart nginx

Note: you must not have any other URL resource that starts with "/json" (such as for example "/json.html" or "/json_something" etc.) as the web server will attempt to pass them as a reverse proxy request, and they will likely not work. If you need to, you can change the application path to be different from "/json", see request_URL.
Access application server from the browser
Use the following URL(s) to access your application server from a client like browser (see request_URL). Use actual IP or web address instead of 127.0.0.1 if different.
#Enter JSON and send for parsing 
http://127.0.0.1/json/json_form

Note: if your server is on the Internet and it has a firewall, you may need to allow HTTP traffic - see ufw, firewall-cmd etc.
Copy the contents of file cities.json (see under FILES) to a text area in the form.

- Run from Javascript via fetch method
You can also test JSON parsing via Javascript/fetch mechanism. First, copy the file to your web server in a separate directory:
Files
You are now done with the example! What follows are the source files in this project so you can examine how it works:
JSON document (cities.json)
This is the JSON document that you enter in the form for parsing. It contains countries, states and cities along with their population.
{ "country": [
    {
        "name": "USA",
        "state": [
            {
                "name": "Arizona",
                "city": [
                    {
                        "name" : "Phoenix",
                        "population": 5000000
                    } ,
                    {
                        "name" : "Tuscon",
                        "population": 1000000
                    }

                ]
            } ,
            {
                "name": "California",
                "city": [
                    {
                        "name" : "Los Angeles",
                        "population": 19000000
                    },
                    {
                        "name" : "Irvine"
                    }
                ]
            }
        ]
    } ,
    {
        "name": "Mexico",
        "state": [
            {
                "name": "Veracruz",
                "city": [
                    {
                        "name" : "Xalapa-Enríquez",
                        "population": 8000000
                    },
                    {
                        "name" : "C\u00F3rdoba",
                        "population": 220000
                    }
                ]
            } ,
            {
                "name": "Sinaloa",
                "city": [
                    {
                        "name" : "Culiac\u00E1n Rosales",
                        "population": 3000000
                    }
                ]
            }
        ]
    }
    ]
}

Call from Javascript (post.html)
You can call your Vely code from Javascript via fetch(). This HTML file will do that as soon as it loads (no buttons to push), you can of course change it to fit your needs. This also demonstrates the use of POST method with Content-Type of "application/json" to talk to your server-side Vely code.
<!DOCTYPE html>
<html lang="en">
<head>
    <title>Vely + JavaScript/Fetch + POST/PUT/PATCH + JSON</title>
</head>
<body>
    <h1 class="align">Example: Vely + JavaScript/Fetch + POST/PUT/PATCH + JSON</h1>
    <script>
        fetch('/json/json_process',{
            method: 'POST',
            headers: {'content-type': 'application/json'},
            body:  '{ "country": [ \
    {  \
        "name": "USA", \
        "state": [ \
            {  \
                "name": "Arizona", \
                "city": [ \
                    { \
                        "name" : "Phoenix", \
                        "population": "5000000" \
                    } , \
                    { \
                        "name" : "Tuscon", \
                        "population": "1000000" \
                    }  \
 \
                ] \
            } , \
            {  \
                "name": "California", \
                "city": [ \
                    { \
                        "name" : "Los Angeles", \
                        "population": "4000000" \
                    }, \
                    { \
                        "name" : "Irvine" \
                    } \
                ] \
            }  \
        ]  \
    } , \
    {  \
        "name": "Mexico", \
        "state": [ \
            {  \
                "name": "Veracruz", \
                "city": [ \
                    { \
                        "name" : "Xalapa-Enríquez", \
                        "population": "8000000" \
                    }, \
                    { \
                        "name" : "C\u00F3rdoba", \
                        "population": "220000" \
                    } \
                ] \
            } , \
            {  \
                "name": "Sinaloa", \
                "city": [ \
                    { \
                        "name" : "Culiac\u00E1n Rosales", \
                        "population": "3000000" \
                    } \
                ] \
            }  \
        ]  \
    } \
    ] \
}'
        })
        .then((result) => { return result.text(); })
        .then((content) => { document.getElementById("json_output").innerHTML = content; });
    </script>
    <div id='json_output'></div>
</body>
</html>

Enter JSON (json_form.vely)
This is a simple HTML form where you can enter JSON document. Since the code in json_process.vely and json_all.vely parses a list of cities as described, the text to enter is given in cities.json file.
#include "vely.h"

void json_form () {

    out-header default

    @<h2>Enter JSON</h2>

    @<form action="<<p-path>>/json_process" method="POST">

    @    <label for="json_text">JSON text:</label><br>
    @    <textarea name="json_text" rows="8" columns="70"></textarea><br/>

    @    <button type="submit">Extract specific data</button>
    @    <button type="submit" formaction="<<p-path>>/json_all">Extract all data</button>
    @ </form>

}

Parse all JSON data in a loop (json_all.vely)
This parses the document that you may not know the structure of. Each data node is obtained you can examine it in your code.
#include "vely.h"

void json_all() {
    out-header default

    input-param json_text

    // Parse json text and display any error and the position of it
    new-json define json from json_text status define st error-text define etext error-position define epos
    if (st != VV_OKAY) {
        @Could not parse JSON! Error [<<p-out etext>>] at position <<p-num epos>>.
        exit-request
    }

    // Traverse JSON document, node by node, display as a table of all data nodes
    read-json json traverse begin
    @<table border='1'>
    while (1)
    {
        read-json json traverse key define k value define v type define t status define s
        if (s != VV_OKAY) break;
        // Display name, value and type (ignore boolean and type since we don't have them)
        @<tr>
            @<td><<p-out k>></td> <td><<p-out v>></td>
            @<td><<p-out t==VV_JSON_TYPE_NUMBER?"Number":(t==VV_JSON_TYPE_STRING?"String":"Other")>></td>
        @</tr>
    }
    @</table>

}

Parse JSON by looking up specific elements (json_process.vely)
Parse the JSON document. This shows parsing the document that you know a structure of, but does not have a fixed structure, so each element is retrieved based on its normalized name (see read-json).
#include "vely.h"

void json_process() {
    out-header default

    // If JSON data sent via URL-encoded GET or POST
    input-param json_text
    // If JSON data sent in the request body (application/json), use that JSON data 
    request-body json_body
    get-req content-type to define ctype
    if (!strcmp(ctype, "application/json")) json_text=json_body;

    // Parse json text and display any error and the position of it
    new-json define json from json_text status define st error-text define etext error-position define epos
    if (st != VV_OKAY) {
        @Could not parse JSON! Error [<<p-out etext>>] at position <<p-num epos>>.
        exit-request
    }

    @Cities found<hr/>

    num country_count;
    num state_count;
    num city_count;

    // Start displaying a list
    @<ul>
    // Look for countries, states and then cities
    // Data is organized in hashed arrays, for example
    // country[0].state[1].city[0]
    // and each can have sub-nodes, such as
    // country[0].name
    // etc.
    for (country_count = 0; ; country_count++) {

        // First, build key prefix for a country
        (( define json_key_country
        @"country"[<<p-num country_count>>]
        ))

        // Search for a country name
        (( define json_key_country_name
        @<<p-out json_key_country>>."name"
        ))

        // Search for a country name under index [country_count]
        read-json json key json_key_country_name value define country_name  status st
        if (st != VV_OKAY) break;

        // Country found
        @<li>Country: <<p-out country_name>><br/>
        @<ul>

        // Look for states under this country
        for (state_count = 0; ; state_count++) {

            // Build key prefix for a state
            (( define json_key_state
            @<<p-out json_key_country>>."state"[<<p-num state_count>>]
            ))

            // Search for state name
            (( define json_key_state_name
            @<<p-out json_key_state>>."name"
            ))

            // Search for a state name as: country[countr_count].state[state_count]
            read-json json key json_key_state_name value define state_name  status st
            if (st != VV_OKAY) break;

            // State found
            @<li>State: <<p-out state_name>><br/>
            @<ul>

            // Look for cities under state
            for (city_count = 0; ; city_count++) {

                // Build key prefix for city
                (( define json_key_city
                @<<p-out json_key_state>>."city"[<<p-num city_count>>]
                ))

                // Search for city name
                (( define json_key_city_name
                @<<p-out json_key_city>>."name"
                ))

                // Search for a city name as: country[countr_count].state[state_count].city[city_count]
                read-json json key json_key_city_name value define city_name  status st
                if (st != VV_OKAY) break;

                // Found city, get its population by building a key for it
                (( define json_key_city_population
                @<<p-out json_key_city>>."population"
                ))

                // Get city population
                read-json json key json_key_city_population value define city_population  status st
                if (st != VV_OKAY) city_population="unknown";

                // Display city name and its population
                @<li>City:<<p-out city_name>> (<<p-out city_population>>)</li>
            }
            @</ul>
            @</li>
        }
        @</ul>
        @</li>
    }
    @</ul>

}

See also
Examples ( example_cookies   example_create_table   example_docker   example_file_manager   example_form   example_hash   example_hello_world   example_json   example_multitenant_SaaS   examples   example_sendmail   example_shopping   example_stock   example_utility   example_write_report  )  SEE ALL (documentation)
Example: multitenant SaaS

This is a multi-tenant web application that you can run on the Internet as Software-as-a-Service (SaaS). Each user has a completely separate data space from any other.

This web application will let user sign up for Notes service - a place where a user can create notes, and then view and delete them.

This example shows how to change application path (see request_URL) and use REST-like URLs for your API/application.

In a nutshell: MariaDB; web browser; Apache; Application path; Unix sockets; 7 source files, 312 lines of code.
Screenshots of application
Create new user by specifying an email address and password (you can style these anyway you like, such as with CSS):

Vely

Verifying user's email:

Vely

User logs in with own user name and password:

Vely

Adding a note once the user logged in:

Vely

Listing notes:

Vely

Ask for confirmation to delete a note:

Vely

After user confirms, delete a note:

Vely

Setup prerequisites
Install Vely - you can use standard packaging tools such as apt, dnf, pacman or zypper.

Because they are used in this example, you will need to install Apache as a web server and MariaDB as a database.

After installing Vely, turn on syntax highlighting in vim if you're using it:
vv -m

Get the source code
The source code is a part of Vely installation. It is a good idea to create a separate source code directory for each application (and you can name it whatever you like). In this case, unpacking the source code will do that for you:
tar xvf $(vv -o)/examples/multitenant_SaaS.tar.gz
cd multitenant_SaaS

Setup application
The very first step is to create an application. The application will be named "multitenant_SaaS", but you can name it anything (if you do that, change it everywhere). It's simple to do with vf:
sudo vf -i -u $(whoami) multitenant_SaaS

This will create a new application home (which is "/var/lib/vv/multitenant_SaaS") and do the application setup for you. Mostly that means create various subdirectories in the home folder, and assign them privileges. In this case only current user (or the result of "whoami" Linux command) will own those directories with 0700 privileges; it means a secure setup.
Setup the database
Before any coding, you need some place to store the information used by the application. First, you will create MariaDB database "db_multitenant_SaaS" owned by user "vely" with password "your_password". You can change any of these names, but remember to change them everywhere here. And then, you will create database objects in the database.

Execute the following logged in as root in mysql utility:
--Create velydb database hosting application data (if it doesn't exist):
create database if not exists db_multitenant_SaaS;
create user if not exists vely identified by 'your_password';
grant create,alter,drop,select,insert,delete,update on db_multitenant_SaaS.* to vely;
-- Create database objects needed for the application (eg. tables, indexes):
use db_multitenant_SaaS;
source setup.sql;
exit

Connect Vely to a database
In order to let Vely know where your database is and how to log into it, you will create database_config_file named "db_multitenant_SaaS". This name doesn't have to be "db_multitenant_SaaS", rather it can be anything - this is the name used in actual database statements in source code (like run-query), so if you change it, make sure you change it everywhere. Create it:
echo '[client]
user=vely
password=your_password
database=db_multitenant_SaaS
protocol=TCP
host=127.0.0.1
port=3306' > db_multitenant_SaaS

The above is a standard mariadb client options file. Vely uses native MariaDB database connectivity, so you can specify any options that a given database lets you.
Build application
Use vv utility to make the application:
vv -q --db=mariadb:db_multitenant_SaaS --path="/api/v2/multitenant_SaaS"

Note usage of --db option to specify MariaDB database and the database configuration file name.

--path is used to specify the application path, see request_URL.
Start your application server
To start the application server for your web application use vf FastCGI process manager. The application server will use a Unix socket to communicate with the web server (i.e. a reverse-proxy):
vf -w 3 multitenant_SaaS

This will start 3 daemon processes to serve the incoming requests. You can also start an adaptive server that will increase the number of processes to serve more requests, and gradually reduce the number of processes when they're not needed:
vf multitenant_SaaS

See vf for more options to help you achieve best performance.

If you want to stop your application server:
vf -m quit multitenant_SaaS

Setup web server
This shows how to connect your application listening on a Unix socket (started with vf) to Apache web server.

- Step 1:
To setup Apache as a reverse proxy and connect your application to it, you need to enable FastCGI proxy support, which generally means "proxy" and "proxy_fcgi" modules - this is done only once:
- Step 2:
Edit the Apache configuration file:
Add this to the end of file ("/api/v2/multitenant_SaaS" is the application path (see request_URL) and "multitenant_SaaS" is your application name):
ProxyPass "/api/v2/multitenant_SaaS" unix:///var/lib/vv/multitenant_SaaS/sock/sock|fcgi://localhost/multitenant_SaaS

- Step 3:
Finally, restart Apache. On Debian systems (like Ubuntu) or OpenSUSE:
sudo systemctl restart apache2

On Fedora systems (like RedHat) and Arch Linux:
sudo systemctl restart httpd

Note: you must not have any other URL resource that starts with "/api/v2/multitenant_SaaS" (such as for example "/api/v2/multitenant_SaaS.html" or "/api/v2/multitenant_SaaS_something" etc.) as the web server will attempt to pass them as a reverse proxy request, and they will likely not work. If you need to, you can change the application path to be different from "/api/v2/multitenant_SaaS", see request_URL.

Setup local mail
This example uses email as a part of its function. If your server already has capability to send email, you can skip this.

Otherwise, you can use local mail, and that means email addresses such as "myuser@localhost". To do that, install postfix (or sendmail). On Debian systems (like Ubuntu):
sudo apt install postfix
sudo systemctl start postfix

and on Fedora systems (like RedHat):
sudo dnf install postfix
sudo systemctl start postfix

When the application sends an email to a local user, such as <OS user>@localhost, then you can see the email sent at:
sudo vi /var/mail/<OS user>

Access application server from the browser
Use the following URL(s) to access your application server from a client like browser (see request_URL). Use actual IP or web address instead of 127.0.0.1 if different.
#Get started 
http://127.0.0.1/api/v2/multitenant_SaaS/notes/action/begin

Note: if your server is on the Internet and it has a firewall, you may need to allow HTTP traffic - see ufw, firewall-cmd etc.
Files
You are now done with the example! What follows are the source files in this project so you can examine how it works:
SQL setup (setup.sql)
The two tables created are: "users", which contains information about each user; and "notes" which contains notes entered by the user.

Each user in "users" table has its own unique ID ("userId" column) along with other information such as email address and whether it's verified. There's also a hashed password - an actual password is never stored in plain text (or otherwise), rather a one-way hash is used to check the password.

The "notes" table contains the notes, each along with "userId" column that states which user owns them. The "userId" column's value matches the namesake column from "users" table. This way, every note clearly belongs to a single user.
create table if not exists notes (dateOf datetime, noteId bigint auto_increment primary key, userId bigint, note varchar(1000));
create table if not exists users (userId bigint auto_increment primary key, email varchar(100), hashed_pwd varchar(100), verified smallint, verify_token varchar(30), session varchar(100));
create unique index if not exists users1 on users (email);

Run-time data (login.h)

In order to properly display the Login, Sign Up and Logout links, you will need some flags that are available anywhere in the application. Also, the application uses cookies to maintain a session, so this needs to be available anywhere, for example to verify that the session is valid. Every request sent to the application is verified that way. Only requests that come with cookies we can verify are permitted.

So to that effect, you will have a global_request_data type "reqdata" (request data) and in it there's "sess_userId" (ID of user) and "sess_id" (user's current session ID). You'll also have rather self-explanatory flags that help render pages.
#ifndef _VV_LOGIN
#define _VV_LOGIN

typedef struct s_reqdata {
    bool displayed_logout; // true if Logout link displayed
    bool is_logged_in; // true if session verified logged-in
    char *sess_userId; // user ID of current session
    char *sess_id; // session ID
} reqdata;

void login_or_signup ();

#endif

Session checking and session data (_before.vely)

Vely has a notion of a before_request_handler. It's the code you write that executes before any other code that handles a request. To do this, all you need is to write this code in file that's named "_before.vely" and the rest will be automatically handled.

That's very useful here. Anything that a SaaS application does, such as handling requests sent to an application, must be validated for security. This way, the application knows if the caller has permissions needed to perform an action.

So, this checking of permission will be done here in a before-request handler. That way, whatever other code you have handling a request, you will have the information about session already provided.

To keep session data (like session ID and user ID) available anywhere in your code, you'll use global_request_data. It's just a generic pointer (void*) to memory that any code that handles a request can access. This is perfect for handling sessions.
#include "vely.h"
#include "login.h"

// _before() is a before-request-handler. It always executes before
// any other code that handles a request. It's a good place for any
// kind of request-wide setting or data initialization
void _before() {
    // Output HTTP header
    out-header default
    reqdata *rd; // this is global request data, see login.h
    // allocate memory for global request data, will be automatically deallocated
    // at the end of request
    new-mem rd size sizeof(reqdata)
    // initialize flags
    rd->displayed_logout = false;
    rd->is_logged_in = false;
    // set the data we created to be global request data, accessible
    // from any code that handles a request
    set-req data rd
    // check if session exists (based on cookies from the client)
    // this executes before any other request-handling code, making it
    // easier to just have session information ready
    _check_session ();
}

Checking if session is valid (_check_session.vely)

One of the most important tasks in a multi-tenant SaaS application is to check (as soon as possible) if the session is valid. This means to check if a user is logged in. It's done by getting the session ID and user ID cookies from the client (i.e. web browser), and checking these against the database where sessions are stored.
#include "vely.h"
#include "login.h"


// Check if session is valid
void _check_session () {
    // Get global request data
    reqdata *rd;
    get-req data to rd
    // Get cookies from user browser
    get-cookie rd->sess_userId="sess_userId"
    get-cookie rd->sess_id="sess_id"
    if (rd->sess_id[0] != 0) {
        // Check if session ID is correct for given user ID
        char *email;
        run-query @db_multitenant_SaaS = "select email from users where userId='%s' and session='%s'" output email : rd->sess_userId, rd->sess_id row-count define rcount
            query-result email to email
        end-query
        if (rcount == 1) {
            // if correct, set logged-in flag
            rd->is_logged_in = true;
            // if Logout link not display, then display it
            if (rd->displayed_logout == false) {
                get-app path to define upath
                @Hi <<p-out email>>! <a href="<<p-out upath>>/login/actions/logout">Logout</a><br/>
                rd->displayed_logout = true;
            }
        } else rd->is_logged_in = false;
    }
}

Signing up, Logging in, Logging out (login.vely)
The basis of any multi-tenant system is the ability for a user to sign up, and once signed up, to log in and log out.

Typically, signing up involves verifying email address, and more often than not, the very same email address is used as a user name. That will be the case here.

There are several subrequests implemented here that are necessary to perform the functionality. Each has its own "URL request signature".
#include "vely.h"
#include "login.h"

// Handle session maintenance, login, logout, session verification
// for any multitenant Cloud application
void login () {
    // Get URL input parameter "actions"
    input-param actions

    // Get global request data, we record session information in it, so it's handy
    reqdata *rd;
    get-req data to rd

    // If session is already established, the only reason why we won't proceed to
    // application home is if we're logging out
    if (rd->is_logged_in) {
        if (strcmp(actions, "logout")) {
            _show_home();
            exit-request
        }
    }

    // Application screen to get started. Show links to login or signup and show
    // home screen appropriate for this
    if (!strcmp (actions, "begin")) {
        _show_home();
        exit-request

    // Start creating new user. Ask for email and password, then proceed to create user
    // when this form is submitted.
    } else if (!strcmp (actions, "newuser")) {
        @Create New User<hr/>
        @<form action="<<p-path>>/login/actions/createuser" method="POST">
        @<input name="email" type="text" value="" size="50" maxlength="50" required autofocus placeholder="Email">
        @<input name="pwd" type="password" value="" size="50" maxlength="50" required placeholder="Password">
        @<input type="submit" value="Sign Up">
        @</form>

    // Verify code sent to email by user. The code must match, thus verifying email address    
    } else if (!strcmp (actions, "verify")) {
        input-param code
        input-param email
        // Get verify token based on email
        run-query @db_multitenant_SaaS = "select verify_token from users where email='%s'" output db_verify : email
            query-result db_verify to define db_verify
            // Compare token recorded in database with what user provided
            if (!strcmp (code, db_verify)) {
                @Your email has been verifed. Please <a href="<<p-path>>/login">Login</a>.
                // If matches, update user info to indicate it's verified
                run-query @db_multitenant_SaaS no-loop = "update users set verified=1 where email='%s'" : email
                exit-request
            }
        end-query
        @Could not verify the code. Please try <a href="<<p-path>>/login">again</a>.
        exit-request

    // Create user - this runs when user submits form with email and password to create a user     
    } else if (!strcmp (actions, "createuser")) {
        input-param email
        input-param pwd
        // create hashed (one-way) password
        hash-string pwd to define hashed_pwd
        // generate random 5 digit string for verify code
        random-string to define verify length 5 number
        // create user: insert email, hashed password, verification token. Current verify status is 0, or not verified
        begin-transaction @db_multitenant_SaaS
        run-query @db_multitenant_SaaS no-loop = "insert into users (email, hashed_pwd, verified, verify_token, session) values ('%s', '%s', '0', '%s', '')" : email, hashed_pwd, verify affected-rows define arows error define err on-error-continue
        if (strcmp (err, "0") || arows != 1) {
            // if cannot add user, it probably doesn't exist. Either way, we can't proceed.
            login_or_signup();
            @User with this email already exists.
            rollback-transaction @db_multitenant_SaaS
        } else {
            // Create email with verification code and email it to user
            write-string define msg
                @From: vely@vely.dev
                @To: <<p-out email>>
                @Subject: verify your account
                @
                @Your verification code is: <<p-out verify>>
            end-write-string
            exec-program "/usr/sbin/sendmail" args "-i", "-t" input msg status define st
            if (st != 0) {
                @Could not send email to <<p-out email>>, code is <<p-out verify>>
                rollback-transaction @db_multitenant_SaaS
                exit-request
            }
            commit-transaction @db_multitenant_SaaS
            // Inform the user to go check email and enter verification code
            @Please check your email and enter verification code here:
            @<form action="<<p-path>>/login/actions/verify" method="POST">
            @<input name="email" type="hidden" value="<<p-out email>>">
            @<input name="code" type="text" value="" size="50" maxlength="50" required autofocus placeholder="Verification code">
            @<button type="submit">Verify</button>
            @</form>
        }

    // This runs when logged-in user logs out.    
    } else if (!strcmp (actions, "logout")) {
        // Update user table to wipe out session, meaning no such user is logged in
        if (rd->is_logged_in) {
            run-query @db_multitenant_SaaS = "update users set session='' where userId='%s'" : rd->sess_userId no-loop affected-rows define arows
            if (arows == 1) {
                rd->is_logged_in = false; // indicate user not logged in
                @You have been logged out.<hr/>
            }
        }
        _show_home();

    // Login: this runs when user enters user name and password
    } else if (!strcmp (actions, "login")) {
        input-param pwd
        input-param email
        // create one-way hash with the intention of comparing with user table - password is NEVER recorded
        hash-string pwd to define hashed_pwd
        // create random 30-long string for session ID
        random-string to rd->sess_id length 30
        // Check if user name and hashed password match
        run-query @db_multitenant_SaaS = "select userId from users where email='%s' and hashed_pwd='%s'" output sess_userId : email, hashed_pwd
            query-result sess_userId to rd->sess_userId
            // If match, update user table with session ID
            run-query @db_multitenant_SaaS no-loop = "update users set session='%s' where userId='%s'" : rd->sess_id, rd->sess_userId affected-rows define arows
            if (arows != 1) {
                @Could not create a session. Please try again. <<.login_or_signup();>> <hr/>
                exit-request
            }
            // Set user ID and session ID as cookies. User's browser will return those to us with every request
            set-cookie "sess_userId" = rd->sess_userId path "/"
            set-cookie "sess_id" = rd->sess_id path "/"
            // Display home, make sure session is correct first and set flags
            _check_session();
            _show_home();
            exit-request
        end-query
        @Email or password are not correct. <<.login_or_signup();>><hr/>

    // Login screen, asks user to enter user name and password    
    } else if (!strcmp (actions, "")) {
        login_or_signup();
        @Please Login:<hr/>
        @<form action="<<p-path>>/login/actions/login" method="POST">
        @<input name="email" type="text" value="" size="50" maxlength="50" required autofocus placeholder="Email">
        @<input name="pwd" type="password" value="" size="50" maxlength="50" required placeholder="Password">
        @<button type="submit">Go</button>
        @</form>
    }
}

// Display Login or Sign Up links
void login_or_signup() {
        @<a href="<<p-path>>/login">Login</a> &nbsp; &nbsp; <a href="<<p-path>>/login/actions/newuser">Sign Up</a><hr/>
}

General-purpose application (_show_home.vely)

With this tutorial you can create any multitenant SaaS application you want. The multitenant-processing module above (login.vely) calls _show_home() function, which can house any code of yours. In here, you'll have Notes application, but it can be anything. _show_home() simply calls any code you wish, and is a general-purpose multitenant application plug-in.
#include "vely.h"

void _show_home() {
    notes();
    exit-request
}

Notes application (notes.vely)

The application will be able to add notes, list them, and delete any given note. It operates under a request "notes", and the sub-requests it serves are:
while a few other subrequests are user-interface-only, such as:
#include "vely.h"
#include "login.h"

// Notes application in a multitenant Cloud 
void notes () {
    // get global request data
    reqdata *rd;
    get-req data to rd

    // If session invalid, display Login or Signup
    if (!rd->is_logged_in) {
        login_or_signup();
    }
    // Greet the user
    @<h1>Welcome to Notes!</h1><hr/>
    // If not logged in, exit - this ensures security verification of user's identity
    if (!rd->is_logged_in) {
        exit-request
    }
    // Get URL parameter that tells Notes what to do
    input-param subreqs
    // Display actions that Notes can do (add or list notes)
    @<a href="<<p-path>>/notes/subreqs/add">Add Note</a> <a href="<<p-path>>/notes/subreqs/list">List Notes</a><hr/>


    // List all notes for this user
    if (!strcmp (subreqs, "list")) {
        // select notes for this user ONLY
        run-query @db_multitenant_SaaS = "select dateOf, note, noteId from notes where userId='%s' order by dateOf desc" : rd->sess_userId output dateOf, note, noteId
            query-result dateOf to define dateOf
            query-result note to define note
            query-result noteId to define noteId
            (( define web_enc
            p-web note
            ))
            // change new lines to <br/> with fast cached Regex
            match-regex "\n" in web_enc replace-with "<br/>\n" result define with_breaks status define st cache
            if (st == 0) with_breaks = web_enc; // nothing was found/replaced, just use original
            // Display a note
            @Date: <<p-out dateOf>> (<a href="<<p-path>>/notes/subreqs/delete_note_ask?note_id=<<p-out noteId>>">delete note</a>)<br/>
            @Note: <<p-out with_breaks>><br/>
            @<hr/>
        end-query
    }

    // Ask to delete a note
    else if (!strcmp (subreqs, "delete_note_ask")) {
        input-param note_id
        @Are you sure you want to delete a note? Use Back button to go back, or <a href="<<p-path>>/notes/subreqs/delete_note?note_id=<<p-out note_id>>">delete note now</a>.
    }

    // Delete a note
    else if (!strcmp (subreqs, "delete_note")) {
        input-param note_id
        // Delete note
        run-query @db_multitenant_SaaS = "delete from notes where noteId='%s' and userId='%s'" : note_id, rd->sess_userId affected-rows define arows no-loop error define errnote
        // Inform user of status
        if (arows == 1) {
            @Note deleted
        } else {
            @Could not delete note (<<p-out errnote>>)
        }
    }

    // Add a note
    else if (!strcmp (subreqs, "add_note")) {
        // Get URL POST data from note form
        input-param note
        // Insert note under this user's ID
        run-query @db_multitenant_SaaS = "insert into notes (dateOf, userId, note) values (now(), '%s', '%s')" : rd->sess_userId, note affected-rows define arows no-loop error define errnote
        // Inform user of status
        if (arows == 1) {
            @Note added
        } else {
            @Could not add note (<<p-out errnote>>)
        }
    }

    // Display an HTML form to collect a note, and send it back here (with subreqs="add_note" URL param)
    else if (!strcmp (subreqs, "add")) {
        @Add New Note
        @<form action="<<p-path>>/notes/subreqs/add_note" method="POST">
        @<textarea name="note" rows="5" cols="50" required autofocus placeholder="Enter Note"></textarea>
        @<button type="submit">Create</button>
        @</form>
    }
}

See also
Examples ( example_cookies   example_create_table   example_docker   example_file_manager   example_form   example_hash   example_hello_world   example_json   example_multitenant_SaaS   examples   example_sendmail   example_shopping   example_stock   example_utility   example_write_report  )  SEE ALL (documentation)
Example: sendmail

Sending email through web interface demonstrates:

In a nutshell:  web browser; Apache; Unix sockets; 1 source files, 79 lines of code.
Screenshots of application
An HTML form will accept the necessary information to send an email. Your server must be enabled to send email via Internet; if not, you can send email to localhost (i.e. to users on your own computer) and for that you must have an MTA like sendmail installed:

Vely

Once email is sent, a confirmation is sent to the user.:

Vely

Setup prerequisites
Install Vely - you can use standard packaging tools such as apt, dnf, pacman or zypper.

Because it is used in this example, you will need to install Apache as a web server.

After installing Vely, turn on syntax highlighting in vim if you're using it:
vv -m

Get the source code
The source code is a part of Vely installation. It is a good idea to create a separate source code directory for each application (and you can name it whatever you like). In this case, unpacking the source code will do that for you:
tar xvf $(vv -o)/examples/sendmail.tar.gz
cd sendmail

Setup application
The very first step is to create an application. The application will be named "sendmail", but you can name it anything (if you do that, change it everywhere). It's simple to do with vf:
sudo vf -i -u $(whoami) sendmail

This will create a new application home (which is "/var/lib/vv/sendmail") and do the application setup for you. Mostly that means create various subdirectories in the home folder, and assign them privileges. In this case only current user (or the result of "whoami" Linux command) will own those directories with 0700 privileges; it means a secure setup.
Build application
Use vv utility to make the application:
vv -q

Start your application server
To start the application server for your web application use vf FastCGI process manager. The application server will use a Unix socket to communicate with the web server (i.e. a reverse-proxy):
vf -w 3 sendmail

This will start 3 daemon processes to serve the incoming requests. You can also start an adaptive server that will increase the number of processes to serve more requests, and gradually reduce the number of processes when they're not needed:
vf sendmail

See vf for more options to help you achieve best performance.

If you want to stop your application server:
vf -m quit sendmail

Setup web server
This shows how to connect your application listening on a Unix socket (started with vf) to Apache web server.

- Step 1:
To setup Apache as a reverse proxy and connect your application to it, you need to enable FastCGI proxy support, which generally means "proxy" and "proxy_fcgi" modules - this is done only once:
- Step 2:
Edit the Apache configuration file:
Add this to the end of file ("/sendmail" is the application path (see request_URL) and "sendmail" is your application name):
ProxyPass "/sendmail" unix:///var/lib/vv/sendmail/sock/sock|fcgi://localhost/sendmail

- Step 3:
Finally, restart Apache. On Debian systems (like Ubuntu) or OpenSUSE:
sudo systemctl restart apache2

On Fedora systems (like RedHat) and Arch Linux:
sudo systemctl restart httpd

Note: you must not have any other URL resource that starts with "/sendmail" (such as for example "/sendmail.html" or "/sendmail_something" etc.) as the web server will attempt to pass them as a reverse proxy request, and they will likely not work. If you need to, you can change the application path to be different from "/sendmail", see request_URL.

Setup local mail
This example uses email as a part of its function. If your server already has capability to send email, you can skip this.

Otherwise, you can use local mail, and that means email addresses such as "myuser@localhost". To do that, install postfix (or sendmail). On Debian systems (like Ubuntu):
sudo apt install postfix
sudo systemctl start postfix

and on Fedora systems (like RedHat):
sudo dnf install postfix
sudo systemctl start postfix

When the application sends an email to a local user, such as <OS user>@localhost, then you can see the email sent at:
sudo vi /var/mail/<OS user>

Access application server from the browser
Use the following URL(s) to access your application server from a client like browser (see request_URL). Use actual IP or web address instead of 127.0.0.1 if different.
#Display a send-mail form 
http://127.0.0.1/sendmail/mail?action=show_form

Note: if your server is on the Internet and it has a firewall, you may need to allow HTTP traffic - see ufw, firewall-cmd etc.
Files
You are now done with the example! What follows are the source files in this project so you can examine how it works:
Send email on the web (mail.vely)
Here, the user can enter information necessary to send an email, such as the recipient email, the subject and message etc. The form to do this is displayed with subrequest "show_form". When the user clicks Submit, the request is sent back with subrequest "submit_form" which uses input-param to collect all the user data, construct an email string and then execute "sendmail" program to send email.

Note that you must have an MTA (Mail Transfer Agent) installed (such as postfix or sendmail), and your computer must be authorized to send email on the internet. Otherwise you can test this by sending emails to your localhost.
// SPDX-License-Identifier: EPL-2.0
// Copyright 2018 DaSoftver LLC.

//
// Send email example
// 

#include "vely.h"

void mail()
{

    out-header default

    input-param action

    if (!strcmp (action, "show_form")) {

        // Display HTML form to input email details. Here we will set the 'action' parameter
        // to 'submit_form'. This way, when the user submits the form, such a request would
        // come back here and the code under else-if-string checking for 'submit_form' (below)
        // would execute

        @<h2>Enter email and click Send to send it</h2>
        @Note: 'From' field must be the email address from the domain of your server.<br/><br/>

        @<form action="<<p-path>>/mail" method="POST">
        @    <input type="hidden" name="action" value="submit_form">

        @    <label for="from_mail">From:</label><br>
        @    <input type="text" name="from_mail" value=""><br>

        @    <label for="to_mail">To:</label><br>
        @    <input type="text" name="to_mail" value=""><br><br>

        @    <label for="subject_mail">Subject:</label><br>
        @    <input type="text" name="subject_mail" value=""><br><br>

        @    <label for="message">Message:</label><br>
        @    <textarea name="message" rows="3" columns="50"></textarea>

        @    <br/><br/>

        @    <input type="submit" value="Send">
        @</form>

    } else if (!strcmp (action, "submit_form")) {

        // Send email using data from the form. This code is called from the form above.
        // Effectively, in this file we have the code to display the form and the code
        // to handle its submission (below).

        input-param from_mail
        input-param to_mail
        input-param message
        input-param subject_mail

        write-string define msg
        @From: <<p-out from_mail>>
        @To: <<p-out to_mail>>
        @Subject: <<p-out subject_mail>>
        @
        <<p-out message>>
        end-write-string
        num st;
        exec-program "/usr/sbin/sendmail" args "-i", "-t" input msg status st
        if (st!=0) {
            @Could not send email!
        } else {
            @Email sent!
        }
        @<hr/>

    } else {
        @Unrecognized action!<hr/>
    }

}

See also
Examples ( example_cookies   example_create_table   example_docker   example_file_manager   example_form   example_hash   example_hello_world   example_json   example_multitenant_SaaS   examples   example_sendmail   example_shopping   example_stock   example_utility   example_write_report  )  SEE ALL (documentation)
Example: shopping

This is a shopping web service REST API with basic functions:
The APIs return a valid JSON reply, even if it's just a single string (such as created ID for a customer, item or order). Listing an order returns a JSON document showing the order details.

The example demostrates usage of REST methods (POST, PUT, GET, DELETE) as well as construction and use of REST URLs, and the code that implements the API.

In a nutshell: PostgreSQL; web browser; Apache; REST API; Application path; Unix sockets; 11 source files, 212 lines of code.
Screenshots of application
Add a customer (named Diana Vega). "Customer ID" is the result JSON text (in this case "3"):

Vely

Add two items ("chocolate bar" and "swiss cheese"). "Item ID" is the result JSON text for each:

Vely

Add an order for customer created by specifying "Customer ID" (in this case it's "3"):

Vely

Add two items to order created by specifying "Order ID" (in this case "5"). The two items are the ones created with "Item ID"s of "3" and "4", and quantities of items ordered are "2" and "1" respectively:

Vely

Update an order by specifying "Order ID" (in this case "5"). The items is specified with "Item ID" (in this case "4") and the quantity is updated to "3":

Vely

Delete an order with "Order ID" (in this case "3"). The result is JSON text specifying number of orders deleted (in this case "1"), and the number of items in it (in this case "0"):

Vely

List a single order by using "Order ID" (in this case "5"). JSON response contains customer and items information:

Vely

List all orders. The JSON response contains customer and items information, and in this case there are two orders:

Vely

Deletes a line item (i.e. all items of the same kind) in an order. In this case "Item ID" specifies the item ("4") in an order given by "Order ID" ("5"). The result is the number of line items deleted:

Vely

Setup prerequisites
Install Vely - you can use standard packaging tools such as apt, dnf, pacman or zypper.

Because they are used in this example, you will need to install Apache as a web server and PostgreSQL as a database.

After installing Vely, turn on syntax highlighting in vim if you're using it:
vv -m

Get the source code
The source code is a part of Vely installation. It is a good idea to create a separate source code directory for each application (and you can name it whatever you like). In this case, unpacking the source code will do that for you:
tar xvf $(vv -o)/examples/shopping.tar.gz
cd shopping

Setup application
The very first step is to create an application. The application will be named "shopping", but you can name it anything (if you do that, change it everywhere). It's simple to do with vf:
sudo vf -i -u $(whoami) shopping

This will create a new application home (which is "/var/lib/vv/shopping") and do the application setup for you. Mostly that means create various subdirectories in the home folder, and assign them privileges. In this case only current user (or the result of "whoami" Linux command) will own those directories with 0700 privileges; it means a secure setup.
Setup the database
Before any coding, you need some place to store the information used by the application. First, you will create PostgreSQL database "db_shopping". You can change the database name, but remember to change it everywhere here. And then, you will create database objects in the database.

Note the example here uses peer-authentication, which is the default on all modern PostgreSQL installations - this means the database user name is the same as the Operating System user name.

Execute the following in PostgreSQL database as root (using psql utility):
echo "create user $(whoami);
create database db_shopping with owner=$(whoami);
grant all on database db_shopping to $(whoami);
\q
" | sudo -u postgres psql

Next, login to database db_shopping and create the database objects for the application:
psql -d db_shopping -f setup.sql

Connect Vely to a database
In order to let Vely know where your database is and how to log into it, you will create database_config_file named "db_shopping". This name doesn't have to be "db_shopping", rather it can be anything - this is the name used in actual database statements in source code (like run-query), so if you change it, make sure you change it everywhere. Create it:
echo "user=$(whoami) dbname=db_shopping"  > db_shopping

The above is a standard postgres connection string that describes the login to the database you created. Since Vely uses native PostgreSQL connectivity, you can specify any connection string that your database lets you.
Build application
Use vv utility to make the application:
vv -q --db=postgres:db_shopping --path="/api/v1/shopping"

Note usage of --db option to specify PostgreSQL database and the database configuration file name.

--path is used to specify the application path, see request_URL.
Start your application server
To start the application server for your web application use vf FastCGI process manager. The application server will use a Unix socket to communicate with the web server (i.e. a reverse-proxy):
vf -w 3 shopping

This will start 3 daemon processes to serve the incoming requests. You can also start an adaptive server that will increase the number of processes to serve more requests, and gradually reduce the number of processes when they're not needed:
vf shopping

See vf for more options to help you achieve best performance.

If you want to stop your application server:
vf -m quit shopping

Setup web server
This shows how to connect your application listening on a Unix socket (started with vf) to Apache web server.

- Step 1:
To setup Apache as a reverse proxy and connect your application to it, you need to enable FastCGI proxy support, which generally means "proxy" and "proxy_fcgi" modules - this is done only once:
- Step 2:
Edit the Apache configuration file:
Add this to the end of file ("/api/v1/shopping" is the application path (see request_URL) and "shopping" is your application name):
ProxyPass "/api/v1/shopping" unix:///var/lib/vv/shopping/sock/sock|fcgi://localhost/shopping

- Step 3:
Finally, restart Apache. On Debian systems (like Ubuntu) or OpenSUSE:
sudo systemctl restart apache2

On Fedora systems (like RedHat) and Arch Linux:
sudo systemctl restart httpd

Note: you must not have any other URL resource that starts with "/api/v1/shopping" (such as for example "/api/v1/shopping.html" or "/api/v1/shopping_something" etc.) as the web server will attempt to pass them as a reverse proxy request, and they will likely not work. If you need to, you can change the application path to be different from "/api/v1/shopping", see request_URL.

Access application server from the browser
Use the following URL(s) to access your application server from a client like browser (see request_URL). Use actual IP or web address instead of 127.0.0.1 if different.

Note: if your server is on the Internet and it has a firewall, you may need to allow HTTP traffic - see ufw, firewall-cmd etc.
Here is REST API for the application.

Substitute the loopback "127.0.0.1" with the IP or web address of your server.

To add a customer, use the POST URL to create new "customer" resource:
curl -X POST  "http://127.0.0.1/api/v1/shopping/customers/first-name/<first name>/last-name/<last name>"

for example:
curl -X POST  "http://127.0.0.1/api/v1/shopping/customers/first-name/Mia/last-name/Beltran"

The return value is a JSON document containing a single value, and that is <customer ID> of a newly created customer.

To add an item to the list of inventory items, use the POST URL to create new "item" resource:
curl -X POST  "http://127.0.0.1/api/v1/shopping/items/name/<item name>?description=<item description>"

for example:
curl -X POST  "http://127.0.0.1/api/v1/shopping/items/name/rice-pudding?description=Delicious"

The return value is a JSON document containing a single value, and that is <item ID> of a newly created item.

To create an order, use the following POST URL to create a new "order" resource, and use <customer ID> previously obtained:
curl -X POST "http://127.0.0.1/api/v1/shopping/orders/customer-id/<customer ID>"

for example:
curl -X POST "http://127.0.0.1/api/v1/shopping/orders/customer-id/1"

The return value is a JSON document containing a single value, and that is <order ID> of a newly created order.

To add an item to an order, use the following POST URL to create a new "order/item" resource, and use <order ID> and <item ID> previously obtained:
curl -X POST "http://127.0.0.1/api/v1/shopping/orders/order-id/<order ID>/item-id/<item ID>?quantity=<item quantity>"

for example:
curl -X POST "http://127.0.0.1/api/v1/shopping/orders/order-id/1/item-id/1?quantity=2"

The return value is a JSON document containing a single value, and that is the number of line items created ("1" or "0").

To update an order (meaning change the number of items), use the following PUT URL, and use <order ID> and <item ID> previously obtained:
curl -X PUT "http://127.0.0.1/api/v1/shopping/orders/order-id/<order ID>/item-id/<item ID>?quantity=<item quantity>"

for example:
curl -X PUT "http://127.0.0.1/api/v1/shopping/orders/order-id/1/item-id/1?quantity=3"

The return value is a JSON document containing a single value, and that is the number of line items updated ("1" or "0"). Per REST methodology, this is an idemopotent operation, i.e. it can be repeated any number of times with the same result.

To delete an item from order, use the following DELETE URL, and use <order ID> and <item ID> previously obtained:
curl -X DELETE "http://127.0.0.1/api/v1/shopping/orders/order-id/<order ID>/item-id/<item ID>"

for example:
curl -X DELETE "http://127.0.0.1/api/v1/shopping/orders/order-id/1/item-id/2"

The return value is a JSON document containing a single value, and that is the number of line items delete ("1" or "0").

To list all orders, use the following GET URL:
curl -X GET "http://127.0.0.1/api/v1/shopping/orders"

The return value is a JSON document with all orders, including customer information, as well as items, their descriptions and quantities.

To list a specific order, use the following GET URL:
curl -X GET "http://127.0.0.1/api/v1/shopping/orders/order-id/<order ID>"

for example:
curl -X GET "http://127.0.0.1/api/v1/shopping/orders/order-id/1"

The return value is a JSON document with a specific order, including customer information, as well as items, their descriptions and quantities.

To delete a specific order, use the following DELETE URL:
curl -X DELETE "http://127.0.0.1/api/v1/shopping/orders/order-id/<order id>"

for example:
curl -X DELETE "http://127.0.0.1/api/v1/shopping/orders/order-id/1"

The return value is a JSON document with a number of orders and number of items deleted.
Files
You are now done with the example! What follows are the source files in this project so you can examine how it works:
Customers resource (customers.vely)
This implements REST API for a customer.
#include "vely.h"

void customers()
{
    get-req method to define req_method

    if (req_method == VV_POST) add_customer ();
}

Orders resource (orders.vely)
This implements REST API for an order.
#include "vely.h"

void orders()
{
    out-header use content-type "application/json"

    get-req method to define req_method

    if (req_method == VV_POST) {
        input-param item_id
        if (item_id[0] == 0) create_order (); else add_to_order();
    }
    else if (req_method == VV_PUT) update_order ();
    else if (req_method == VV_GET) list_orders ();
    else if (req_method == VV_DELETE) {
        input-param item_id
        if (item_id[0] == 0) delete_order (); else {
            set-input "quantity" = "0"
            update_order ();
        }
    }
}

Items resource (items.vely)
This implements REST API for an item.
#include "vely.h"

void items()
{
    out-header use content-type "application/json"

    get-req method to define req_method

    if (req_method == VV_POST) add_item ();
}

Add a customer (add_customer.vely)
This will add a new customer. Removing a customer is not included, and it should remove all its orders.
#include "vely.h"

void add_customer()
{
    out-header use content-type "application/json"

    input-param first_name
    input-param last_name
    // Add a customer SQL
    run-query @db_shopping ="insert into customers (firstName, lastName) \
            values ('%s', '%s') returning customerID" output define customerID : \
            first_name, last_name
        @"<<p-out customerID>>"
    end-query
}

Add an item to inventory (add_item.vely)
Here an item is added to invetory available for sale.
#include "vely.h"

void add_item()
{
    out-header use content-type "application/json"

    input-param name
    input-param description
    // Add an item to inventory SQL
    run-query @db_shopping ="insert into items (name, description) \
        values ('%s', '%s') returning itemID" output itemID : name, description
        query-result itemID to define item_id
        @"<<p-out item_id>>"
    end-query
}

Create an order (create_order.vely)
This creates a new order, ready to have items added to it.
#include "vely.h"

void create_order()
{
    out-header use content-type "application/json"

    input-param customer_id
    // SQL to create an order
    run-query @db_shopping ="insert into orders (customerId) \
        values ('%s') returning orderID" output orderID : customer_id
        query-result orderID to define order_id
        @"<<p-out order_id>>"
    end-query
}

Add item to order (add_to_order.vely)
Add an item to existing order.
#include "vely.h"

void add_to_order()
{
    out-header use content-type "application/json"

    input-param order_id
    input-param item_id
    input-param quantity
    // SQL to add an item to an order
    run-query @db_shopping ="insert into orderItems (orderId, itemID, quantity) values  ('%s', '%s', '%s')" \
        : order_id, item_id, quantity no-loop affected-rows define arows
    @"<<p-num arows>>"
}

Update item quantity in an order (update_order.vely)
If the quantity update is 0, the item is deleted from order, otherwise the quantity is updated.
#include "vely.h"

void update_order()
{
    out-header use content-type "application/json"

    input-param order_id
    input-param item_id
    input-param quantity
    num arows;
    // If quantity update is 0, issue SQL to delete an item from order, otherwise update 
    if (!strcmp (quantity, "0")) {
        run-query @db_shopping ="delete from orderItems where orderID='%s' and itemID='%s'" \
            : order_id, item_id no-loop affected-rows arows
    } else {
        run-query @db_shopping ="update orderItems set quantity='%s' where orderID='%s' and itemID='%s'" \
            : quantity, order_id, item_id no-loop affected-rows arows
    }
    @"<<p-num arows>>"
}

List orders as JSON (list_orders.vely)
All orders are listed along with customer ID and name, and under each order are the items with their names, descriptions and quantity of items ordered.
#include "vely.h"
#include "shopping.h"

void list_orders()
{
    out-header use content-type "application/json"

    input-param order_id

    num curr_order = 0;

    // Start JSON output
    @{ "orders": [

    if (order_id[0] != 0) {
        // Query just a specific order
        run-query @db_shopping = "select o.orderID, c.customerID, c.firstName, c.lastName \
                from orders o, customers c \
                where o.customerID=c.customerID and o.orderId='%s'" \
                output define customer_id, first_name, last_name \
                row-count define order_count : order_id
            _json_from_order (order_id, curr_order, order_count, customer_id,
                first_name, last_name);
        end-query
    } else {
        // Query to get all orders
        run-query @db_shopping ="select o.orderID, c.customerID, c.firstName, c.lastName \
                from orders o, customers c \
                where o.customerID=c.customerID order by o.orderId" \
                output define order_id, customer_id, first_name, last_name \
                row-count define order_count
            _json_from_order (order_id, curr_order, order_count, customer_id,
                first_name, last_name);
        end-query
    }

    // Finish JSON output
    @   ]
    @}
}

Represent order as JSON (_json_from_query.vely)
This will take order information from the list of orders (as directed by GET REST API for either all orders or a specific one), and find all items within an order. The JSON text is output.
#include "vely.h"
#include "shopping.h"

void _json_from_order (char *order_id, num curr_order, num order_count,
    char *customer_id, char *first_name, char *last_name)
{
    @   {
    @       "orderID": "<<p-out order_id>>",
    @       "customer":
    @       {
    @           "customerID": "<<p-out customer_id>>",
    @           "firstName": "<<p-out first_name>>",
    @           "lastName": "<<p-out last_name>>"
    @       },
    @       "items": [
    num curr_item = 0;
    // Query to get all items in an order
    run-query @db_shopping ="select i.itemID, t.name, t.description, i.quantity \
            from orderItems i, items t where i.orderID='%s' \
                and t.itemID=i.itemID" \
            output itemID, itemName, itemDescription, itemQuantity : order_id \
            row-count define item_count
        @       {
        @           "itemID": "<<query-result itemID>>",
        @           "itemName": "<<query-result itemName>>",
        @           "itemDescription": "<<query-result itemDescription>>",
        @           "itemQuantity": "<<query-result itemQuantity>>"
        // add a comma if there are more items after this
        @       }<<p-out ++curr_item < item_count ? ",":"">>
    end-query
    @   ]
    // add a comma if there are more orders after this
    @   }<<p-out ++curr_order < order_count ? ",":"">>
}

Include file (shopping.h)
This file has a function declaration for _json_from_query() so it can be used in multiple .vely files.
#include "vely.h"

void _json_from_order (char *order_id, num curr_order, num order_count,
    char *customer_id, char *first_name, char *last_name);

See also
Examples ( example_cookies   example_create_table   example_docker   example_file_manager   example_form   example_hash   example_hello_world   example_json   example_multitenant_SaaS   examples   example_sendmail   example_shopping   example_stock   example_utility   example_write_report  )  SEE ALL (documentation)
Examples

Click on each example for instructions. For a condensed Hello World that you can run from command line in just minutes, try 123_hello_world.
See also
Examples ( example_cookies   example_create_table   example_docker   example_file_manager   example_form   example_hash   example_hello_world   example_json   example_multitenant_SaaS   examples   example_sendmail   example_shopping   example_stock   example_utility   example_write_report  )  SEE ALL (documentation)
Example: stock

Stock ticker example:

In a nutshell: MariaDB; command line; web browser; Nginx; Unix sockets; 3 source files, 55 lines of code.
Screenshots of application
Stock value is updated:

Vely

Showing the stock:

Vely

Setup prerequisites
Install Vely - you can use standard packaging tools such as apt, dnf, pacman or zypper.

Because they are used in this example, you will need to install Nginx as a web server and MariaDB as a database.

After installing Vely, turn on syntax highlighting in vim if you're using it:
vv -m

Get the source code
The source code is a part of Vely installation. It is a good idea to create a separate source code directory for each application (and you can name it whatever you like). In this case, unpacking the source code will do that for you:
tar xvf $(vv -o)/examples/stock.tar.gz
cd stock

Setup application
The very first step is to create an application. The application will be named "stock", but you can name it anything (if you do that, change it everywhere). It's simple to do with vf:
sudo vf -i -u $(whoami) stock

This will create a new application home (which is "/var/lib/vv/stock") and do the application setup for you. Mostly that means create various subdirectories in the home folder, and assign them privileges. In this case only current user (or the result of "whoami" Linux command) will own those directories with 0700 privileges; it means a secure setup.
Setup the database
Before any coding, you need some place to store the information used by the application. First, you will create MariaDB database "db_stock" owned by user "vely" with password "your_password". You can change any of these names, but remember to change them everywhere here. And then, you will create database objects in the database.

Execute the following logged in as root in mysql utility:
--Create velydb database hosting application data (if it doesn't exist):
create database if not exists db_stock;
create user if not exists vely identified by 'your_password';
grant create,alter,drop,select,insert,delete,update on db_stock.* to vely;
-- Create database objects needed for the application (eg. tables, indexes):
use db_stock;
source setup.sql;
exit

Connect Vely to a database
In order to let Vely know where your database is and how to log into it, you will create database_config_file named "db_stock". This name doesn't have to be "db_stock", rather it can be anything - this is the name used in actual database statements in source code (like run-query), so if you change it, make sure you change it everywhere. Create it:
echo '[client]
user=vely
password=your_password
database=db_stock
protocol=TCP
host=127.0.0.1
port=3306' > db_stock

The above is a standard mariadb client options file. Vely uses native MariaDB database connectivity, so you can specify any options that a given database lets you.
Build application
Use vv utility to make the application:
vv -q --db=mariadb:db_stock

Note usage of --db option to specify MariaDB database and the database configuration file name.
Start your application server
To start the application server for your web application use vf FastCGI process manager. The application server will use a Unix socket to communicate with the web server (i.e. a reverse-proxy):
vf -w 3 stock

This will start 3 daemon processes to serve the incoming requests. You can also start an adaptive server that will increase the number of processes to serve more requests, and gradually reduce the number of processes when they're not needed:
vf stock

See vf for more options to help you achieve best performance.

If you want to stop your application server:
vf -m quit stock

Setup web server
This shows how to connect your application listening on a Unix socket (started with vf) to Nginx web server.

- Step 1:
You will need to edit the Nginx configuration file. For Ubuntu and similar:
sudo vi /etc/nginx/sites-enabled/default

while on Fedora and other systems it might be at:
sudo vi /etc/nginx/nginx.conf


Add the following in the "server {}" section ("/stock" is the application path (see request_URL) and "stock" is your application name):
location /stock { include /etc/nginx/fastcgi_params; fastcgi_pass  unix:///var/lib/vv/stock/sock/sock; }

- Step 2:
Finally, restart Nginx:
sudo systemctl restart nginx

Note: you must not have any other URL resource that starts with "/stock" (such as for example "/stock.html" or "/stock_something" etc.) as the web server will attempt to pass them as a reverse proxy request, and they will likely not work. If you need to, you can change the application path to be different from "/stock", see request_URL.
Access application server from the browser
Use the following URL(s) to access your application server from a client like browser (see request_URL). Use actual IP or web address instead of 127.0.0.1 if different.
#Add stock ticker 'XYZ' with stock price 450 
http://127.0.0.1/stock/add-stock?name=XYZ&price=450

#Display list of stock tickers 
http://127.0.0.1/stock/show-stock

Note: if your server is on the Internet and it has a firewall, you may need to allow HTTP traffic - see ufw, firewall-cmd etc.
Access application server from command line
To access your application server from command line (instead through web browser/web server), use "cgi-fcgi" to see the application response:
#Add stock ticker 'XYZ' with stock price 450 
export REQUEST_METHOD=GET
export SCRIPT_NAME='/stock'
export PATH_INFO='/add-stock'
export QUERY_STRING='name=XYZ&price=450'
cgi-fcgi -connect /var/lib/vv/stock/sock/sock /

#Display list of stock tickers 
export REQUEST_METHOD=GET
export SCRIPT_NAME='/stock'
export PATH_INFO='/show-stock'
export QUERY_STRING=''
cgi-fcgi -connect /var/lib/vv/stock/sock/sock /

Note: to suppress output of HTTP headers, add this before running "cgi-fcgi":
export VV_SILENT_HEADER=yes

If you need to, you can also run your application as a CGI program.
Run program from command line
Execute the following to run your application from command line (as a command-line utility):
#Add stock ticker 'XYZ' with stock price 450 
export REQUEST_METHOD=GET
export SCRIPT_NAME='/stock'
export PATH_INFO='/add-stock'
export QUERY_STRING='name=XYZ&price=450'
/var/lib/vv/bld/stock/stock

#Display list of stock tickers 
export REQUEST_METHOD=GET
export SCRIPT_NAME='/stock'
export PATH_INFO='/show-stock'
export QUERY_STRING=''
/var/lib/vv/bld/stock/stock

Note: to suppress output of HTTP headers, add this before running /var/lib/vv/bld/stock/stock program:
export VV_SILENT_HEADER=yes

Note: if running your program as a command-line utility is all you want, you don't need to run an application server.
Files
You are now done with the example! What follows are the source files in this project so you can examine how it works:
Database objects (setup.sql)
Table "stock" with stock name and price is created for this example:
create table if not exists stock (stock_name varchar(100) primary key, stock_price bigint);

Add stock ticker (add_stock.vely)
This requests (/add-stock) saves the names of stock tickers and their prices. It will obtain the stock name and price from the web client via input-param statement, and then use INSERT SQL to store this data in the database.
// SPDX-License-Identifier: EPL-2.0
// Copyright 2018 DaSoftver LLC.

#include "vely.h"

void add_stock()
{
    out-header default
    @<html>
        @<body>
        input-param name
        input-param price
        // Add data to stock table, update if the stock exists
        run-query @db_stock = "insert into stock (stock_name, stock_price) values ('%s', '%s') on duplicate key update stock_price='%s'" : name, price, price error define err no-loop
        if (strcmp (err, "0")) {
            report-error "Cannot update stock price, error [%s]", err
        }
        @<div>
            @Stock price updated!
        @</div>
        @</body>
    @</html>
}

Show stock tickers (show_stock.vely)
You can view stock tickers in a list. SELECT SQL is used to get all the stocks saved so far, and display them in a table using run-query and then query-result to get the query results.
// SPDX-License-Identifier: EPL-2.0
// Copyright 2018 DaSoftver LLC.

#include "vely.h"

void show_stock()
{
    out-header default
    @<html>
        @<body>
        // Show stock names and values
            @<table>
                @<tr>
                    @<td>Stock name</td>
                    @<td>Stock price</td>
                @</tr>
                run-query @db_stock = "select stock_name, stock_price from stock" output stock_name, stock_price
                    @<tr>
                        @<td>
                        query-result  stock_name
                        @</td>
                        @<td>
                        query-result  stock_price
                        @</td>
                    @</tr>
                end-query
            @</table>
        @</body>
    @</html>
}

See also
Examples ( example_cookies   example_create_table   example_docker   example_file_manager   example_form   example_hash   example_hello_world   example_json   example_multitenant_SaaS   examples   example_sendmail   example_shopping   example_stock   example_utility   example_write_report  )  SEE ALL (documentation)
Example: utility

In this example, a temperature measuring device will periodically insert temperatures into a database table, along with a timestamp. The purpose is to read this history and output the result from time to time, which can then be piped or sent elsewhere.

In a nutshell: SQLite; command line; Unix sockets; 2 source files, 26 lines of code.
Screenshots of application
Adding a record to the database, for example to record a temperature reading of 91F:

Vely

To view the current database in chronological order:

Vely

Setup prerequisites
Install Vely - you can use standard packaging tools such as apt, dnf, pacman or zypper.

Because they are used in this example, you will need to install Nginx as a web server and SQLite as a database.

After installing Vely, turn on syntax highlighting in vim if you're using it:
vv -m

Get the source code
The source code is a part of Vely installation. It is a good idea to create a separate source code directory for each application (and you can name it whatever you like). In this case, unpacking the source code will do that for you:
tar xvf $(vv -o)/examples/utility.tar.gz
cd utility

Setup application
The very first step is to create an application. The application will be named "utility", but you can name it anything (if you do that, change it everywhere). It's simple to do with vf:
sudo vf -i -u $(whoami) utility

This will create a new application home (which is "/var/lib/vv/utility") and do the application setup for you. Mostly that means create various subdirectories in the home folder, and assign them privileges. In this case only current user (or the result of "whoami" Linux command) will own those directories with 0700 privileges; it means a secure setup.
Setup the database
Before any coding, you need some place to store the information used by the application. First, create SQLite database "db_utility". You can change the database name, but remember to change it everywhere here. And then, create database objects in the database.

Execute the following to create database "utility.db" and also setup the database objects needed for the example:
sqlite3 /var/lib/vv/utility/app/utility.db < setup.sql

The SQLite database will be in the application home directory (see how_vely_works):
/var/lib/vv/utility/app

Connect Vely to a database
In order to let Vely know where your database is and how to log into it, you will create database_config_file named "db_utility". This name doesn't have to be "db_utility", rather it can be anything - this is the name used in actual database statements in source code (like run-query), so if you change it, make sure you change it everywhere. Create it:
echo '/var/lib/vv/utility/app/utility.db' > db_utility

The above in general is a location of SQLite database and is all that's needed to connect to it.
Build application
Use vv utility to make the application:
vv -q --db=sqlite:db_utility

Note usage of --db option to specify SQLite database and the database configuration file name.
Run program from command line
Execute the following to run your application from command line (as a command-line utility):
#Record a temperature to 91F
export REQUEST_METHOD=GET
export SCRIPT_NAME='/utility'
export PATH_INFO='/temphist'
export QUERY_STRING='action=record&temp=91'
/var/lib/vv/bld/utility/utility

#List recorded temperatures
export REQUEST_METHOD=GET
export SCRIPT_NAME='/utility'
export PATH_INFO='/temphist'
export QUERY_STRING='action=list'
/var/lib/vv/bld/utility/utility

Note: to suppress output of HTTP headers, add this before running /var/lib/vv/bld/utility/utility program:
export VV_SILENT_HEADER=yes

Files
You are now done with the example! What follows are the source files in this project so you can examine how it works:
Recording temperature readings in a table (setup.sql)
There is a "temp" column (temperature reading) and "timest" (time stamp of the reading). Table name is "temps" (temperatures).
create table temps (temp integer, timest text primary key);

Utility for recording temperatures (temphist.vely)
This application will record temperatures, as well as list them. The source code in "temphist.vely" file is fairly self-explanatory. If input parameter "action" is "record", store the temperature into table "temps" and check for any errors. If it is "list", just display all the temperature records in a historical order.
// SPDX-License-Identifier: EPL-2.0
// Copyright 2018 DaSoftver LLC.
#include "vely.h"

void temphist() {
    out-header default

    input-param action

    if (!strcmp (action, "record")) {
        input-param temp
        run-query @db_utility = "insert into temps (temp, timest) values ('%s', current_timestamp)" : temp affected-rows define rc error-text define er no-loop
        if (rc != 1) {
            @Could not insert temperature reading, error <<p-out er>>.
        } else {
            @Temperature reading stored.
        }
    }

    else if (!strcmp (action, "list")) {
        run-query @db_utility = "select temp, timest from temps order by timest" output temp, timest
            @Date: <<query-result  timest>>
            @Temperature: <<query-result  temp>>
        end-query
    }
}

See also
Examples ( example_cookies   example_create_table   example_docker   example_file_manager   example_form   example_hash   example_hello_world   example_json   example_multitenant_SaaS   examples   example_sendmail   example_shopping   example_stock   example_utility   example_write_report  )  SEE ALL (documentation)
Example: write report

Builds a report of employees from a database:

In a nutshell: MariaDB; command line; web browser; Nginx; Unix sockets; 2 source files, 69 lines of code.
Screenshots of application
Database report is saved into a file (the path to which is displayed) and the report itself is shown in the web page:

Vely

Setup prerequisites
Install Vely - you can use standard packaging tools such as apt, dnf, pacman or zypper.

Because they are used in this example, you will need to install Nginx as a web server and MariaDB as a database.

After installing Vely, turn on syntax highlighting in vim if you're using it:
vv -m

Get the source code
The source code is a part of Vely installation. It is a good idea to create a separate source code directory for each application (and you can name it whatever you like). In this case, unpacking the source code will do that for you:
tar xvf $(vv -o)/examples/write_report.tar.gz
cd write_report

Setup application
The very first step is to create an application. The application will be named "write_report", but you can name it anything (if you do that, change it everywhere). It's simple to do with vf:
sudo vf -i -u $(whoami) write_report

This will create a new application home (which is "/var/lib/vv/write_report") and do the application setup for you. Mostly that means create various subdirectories in the home folder, and assign them privileges. In this case only current user (or the result of "whoami" Linux command) will own those directories with 0700 privileges; it means a secure setup.
Setup the database
Before any coding, you need some place to store the information used by the application. First, you will create MariaDB database "db_write_report" owned by user "vely" with password "your_password". You can change any of these names, but remember to change them everywhere here. And then, you will create database objects in the database.

Execute the following logged in as root in mysql utility:
--Create velydb database hosting application data (if it doesn't exist):
create database if not exists db_write_report;
create user if not exists vely identified by 'your_password';
grant create,alter,drop,select,insert,delete,update on db_write_report.* to vely;
-- Create database objects needed for the application (eg. tables, indexes):
use db_write_report;
source setup.sql;
exit

Connect Vely to a database
In order to let Vely know where your database is and how to log into it, you will create database_config_file named "db_write_report". This name doesn't have to be "db_write_report", rather it can be anything - this is the name used in actual database statements in source code (like run-query), so if you change it, make sure you change it everywhere. Create it:
echo '[client]
user=vely
password=your_password
database=db_write_report
protocol=TCP
host=127.0.0.1
port=3306' > db_write_report

The above is a standard mariadb client options file. Vely uses native MariaDB database connectivity, so you can specify any options that a given database lets you.
Build application
Use vv utility to make the application:
vv -q --db=mariadb:db_write_report

Note usage of --db option to specify MariaDB database and the database configuration file name.
Start your application server
To start the application server for your web application use vf FastCGI process manager. The application server will use a Unix socket to communicate with the web server (i.e. a reverse-proxy):
vf -w 3 write_report

This will start 3 daemon processes to serve the incoming requests. You can also start an adaptive server that will increase the number of processes to serve more requests, and gradually reduce the number of processes when they're not needed:
vf write_report

See vf for more options to help you achieve best performance.

If you want to stop your application server:
vf -m quit write_report

Setup web server
This shows how to connect your application listening on a Unix socket (started with vf) to Nginx web server.

- Step 1:
You will need to edit the Nginx configuration file. For Ubuntu and similar:
sudo vi /etc/nginx/sites-enabled/default

while on Fedora and other systems it might be at:
sudo vi /etc/nginx/nginx.conf


Add the following in the "server {}" section ("/write_report" is the application path (see request_URL) and "write_report" is your application name):
location /write_report { include /etc/nginx/fastcgi_params; fastcgi_pass  unix:///var/lib/vv/write_report/sock/sock; }

- Step 2:
Finally, restart Nginx:
sudo systemctl restart nginx

Note: you must not have any other URL resource that starts with "/write_report" (such as for example "/write_report.html" or "/write_report_something" etc.) as the web server will attempt to pass them as a reverse proxy request, and they will likely not work. If you need to, you can change the application path to be different from "/write_report", see request_URL.
Access application server from the browser
Use the following URL(s) to access your application server from a client like browser (see request_URL). Use actual IP or web address instead of 127.0.0.1 if different.
#Run report 
http://127.0.0.1/write_report/write_report

Note: if your server is on the Internet and it has a firewall, you may need to allow HTTP traffic - see ufw, firewall-cmd etc.
Access application server from command line
To access your application server from command line (instead through web browser/web server), use "cgi-fcgi" to see the application response:
#Run report 
export REQUEST_METHOD=GET
export SCRIPT_NAME='/write_report'
export PATH_INFO='/write_report'
export QUERY_STRING=''
cgi-fcgi -connect /var/lib/vv/write_report/sock/sock /

Note: to suppress output of HTTP headers, add this before running "cgi-fcgi":
export VV_SILENT_HEADER=yes

If you need to, you can also run your application as a CGI program.
Run program from command line
Execute the following to run your application from command line (as a command-line utility):
#Run report 
export REQUEST_METHOD=GET
export SCRIPT_NAME='/write_report'
export PATH_INFO='/write_report'
export QUERY_STRING=''
/var/lib/vv/bld/write_report/write_report

Note: to suppress output of HTTP headers, add this before running /var/lib/vv/bld/write_report/write_report program:
export VV_SILENT_HEADER=yes

Note: if running your program as a command-line utility is all you want, you don't need to run an application server.
Files
You are now done with the example! What follows are the source files in this project so you can examine how it works:
Database setup (setup.sql)
For this example, table "employees" is created if it doesn't exist (all its data deleted if it does), and it is populated with some data:
create table if not exists employees (name varchar(50), salary int);
delete from employees;
insert into employees (name, salary) values ('Mike', 50000), ('Joan', 60000), ('Kelly', 70000);

Database report (write_report.vely)
A result set of a query is stored in a string using write-string with run-query within in. write-string simply redirects any output (that would normally go to the client) into a string. This output is then written to a file using write-file and also sent back to the client.

A few other things are demonstrated here, such as get-req to obtain the database vendor (i.e. which database is used, as any number of databases can be, such as MariaDB/MySQL, PostgreSQL, SQLite). Also getting current time (get-time) is shown to produce a timestamp for the file name, and match-regex to replace spaces with underscores.
// SPDX-License-Identifier: EPL-2.0
// Copyright 2018 DaSoftver LLC.

#include "vely.h"

void write_report()
{

    out-header default

    get-app db-vendor db_write_report to define dbv
    @Database vendor used is <<p-out dbv>><br/>

    // Build report for a query and store it into variable outmsg

    write-string define outmsg
    @Employee report<hr/>

    // Get data from the database
    run-query @db_write_report="select name, salary from employees order by name" output name, salary
       @  -----<br/>
       @  Name: <<query-result name>>
       @  Salary: <<query-result salary>><br/>
    end-query
    @-----<br/>

    @End report.

    end-write-string


    // build time stamp for report file
    get-time to define curr_time format "%A %B %d %Y %l %M %p %Z"
    get-req process-id to define pid
    match-regex " " in curr_time replace-with "_" result define tstamp

    // get application home directory and build the path for reports directory
    get-app directory to define home_dir
    write-string define report_dir
    @<<p-out home_dir>>/reports
    end-write-string

    // make directory, ignore error if it exists
    exec-program "mkdir" args "-p", report_dir status define st
    if (st != VV_OKAY  && st != VV_ERR_EXIST) {
        @Error in creating reports directory
        exit-request
    }

    // build the name of report file
    write-string define report_file
    @<<p-out home_dir>>/reports/report_<<pf-out "%lld", pid>><<p-out tstamp>>
    end-write-string

    // write report file

    write-file report_file from outmsg status st
    if (st<0) {
        @Error in writing report file <<p-out report_file>> (<<pf-out "%lld", st>>)
        exit-request
    } else {
        // if okay, display report file to the web
        @Report file [<<p-out report_file>>] written.<br/>
        @Report file content:<br/>
        p-out outmsg
    }

}

See also
Examples ( example_cookies   example_create_table   example_docker   example_file_manager   example_form   example_hash   example_hello_world   example_json   example_multitenant_SaaS   examples   example_sendmail   example_shopping   example_stock   example_utility   example_write_report  )  SEE ALL (documentation)
exec-program

Purpose: Execute a program.

exec-program <program path> \
    [ args <program arg> [ , ... ] ] \
    [ status [ define ] <exit status> ] \
    [ ( input <input string> [ input-length <string length> ] ) \
        | ( input-file <input file> ) ] \
    [ ( output [ define ] <output string> [ output-length [ define ] <output length> ] ) \
        | ( output-file <output file> ) ] \
    [ ( error [ define ] <error string> ) | ( error-file <error file> ) ]

exec-program executes a program specified in <program path>, which can be a program name without path that exists in the path specified by the PATH environment variable; or an absolute path; or a path relative to the application home directory (see how_vely_works).

A program can have input arguments (specified with "args" clause), and if there are more than one, they must be separated by a comma. There can be a maximum of 32 input arguments. Each argument <program arg> may contain a comma if it is a string (i.e. double-quoted) or it is an expression within parenthesis.

You can specify a status variable <exit status>, which either must exist, or if it doesn't, use "define" to create it - this variable will have program's exit status. Note that if the program was terminated by a signal, <exit status> will have a value of 128+signal_number, so for example if the program was terminated with signal 9 (i.e. KILL signal), <exit status> will be 137 (i.e. 128+9). Any other kind of abnormal program termination (i.e. program termination that did not end in setting the exit code with exit() or similar) will return 126 as <exit code>.

Specifying program input and output is optional. If program has output and you are not capturing it in any way, the output is redirected to a temporary file that is deleted after exec-program completes.

You can specify an <input string> to be passed to program's standard input (stdin) via "input" clause. If "input-length" is not used, the length of this input is the string length of <input string>, otherwise <string length> bytes is passed to the program, allowing binary input. Alternatively, you can specify a file <input file> (via "input-file" clause) to be opened and directed into program's standard input (stdin).

You can redirect the program's output (which is "stdout") to a file <output file> using "output-file" clause. Alternatively, program's output can be captured in <output string> (via "output" clause) with its length in <output length> (via "output-length" clause), both of which can be created with optional "define". <output string> is allocated memory.

To get the program's error output (which is "stderr") to a file <error file> using "error-file" clause. Alternatively, program's error output can be captured in <error string> (via "error" clause) which can be created with "define" if not existing. <error string> is allocated memory.

If <input file> cannot be opened, VV_ERR_READ is reported in <exit status>, and if either <output file> or <error file> cannot be opened, the status is VV_ERR_WRITE.

Do not use system() function as it may be unsafe. Use exec-program instead.
Examples
To simply execute a program that is in the path, without any arguments, input or output:
exec-program "myprogram"

Run "grep" program using a string as its standard input in order to remove a line that contains "bad line" in it, and outputting the result into "ovar" variable:
exec-program "grep" args "-v", "bad line" "config" input "line 1\nline 2\nbad line\nline 3" output ovar
p-out ovar

Get the list of files in the application home directory into buffer "ovar" (and its length into variable "olen") and then display it:
exec-program "ls" output define ovar output-length define olen

Similar to the above example of listing files, but output results to a file (which is then read and the result displayed), and provide options "-a -l -s" to "ls" program:
exec-program "ls" args "-a", "-l", "-s" output-file "lsout"
read-file "lsout" to define final_res
p-out final_res

Count the lines of file "config" (which is redirected to the standard output of "wc" program) and store the result in variable "ovar" (by means of redirecting the output of "wc" program to it), and then display:
exec-program "wc" args "-l" input-file "config" output define ovar
p-out ovar

See also
Program execution ( exec-program   exit-code  )  SEE ALL (documentation)
exit-code

Purpose: Return exit code when running from the command_line.

exit-code <exit code>

exit-code specifies <exit code> (which must be an integer expression) when the program runs from the command_line.

exit-code can be specified anywhere in the code, and does not mean exiting the program. To exit the program, either use exit-request or simply allow request function to reach its end.

When exit-code is not used, the default exit code is 0.
Examples
When the program exits, its exit code will be 12:
exit-code 12
...
exit-request

See also
Program execution ( exec-program   exit-code  )  SEE ALL (documentation)
exit-request

Purpose: Exit current request processing.

exit-request

Exits current request by transferring control directly after the top-level request dispatcher. If there is an after_request_handler, it will still execute, unless exit-request is called from before_request_handler. exit-request will have no effect in startup_handler because that handler runs before any requests.

exit-request is useful when your request handler has called other functions (i.e. those implemented in non_request source files), which may have called others (etc.), and there is no need to return in the reverse order, nor to pass any further data back to them; in which case returning one step at a time may be cumbersome and error prone.

In other words, exit-request jumps to the top-level request dispatcher, and the stack of functions called to get to exit-request will be bypassed, thus those functions will not get control back; and they will not perform any additional work, rather simply the next request will be processed immediately.

Never user C's exit() function, as it will terminate the server process and prevent exit-code from functioning properly.
Examples
#include "vely.h"

void req_handler()
{
    ...
    exit-request
    ...
}

See also
Program flow ( exit-request  )  SEE ALL (documentation)
FastCGI client

You can use cgi-fcgi command-line utility to communicate with your FastCGI application without using a web server. Use it to get responses from the command line. This is useful for shell scripting, debugging, testing etc.

If you have application <app name> running, for example 3 concurrent processes:
vf -w 3 <app name>

then you can send requests to it and receive replies:
export REQUEST_METHOD=GET
export SCRIPT_NAME="/<app name>"
export PATH_NAME="/<request name>"
export QUERY_STRING="par1=val1&par2=val2.."
cgi-fcgi -connect /var/lib/vv/<app name>/sock/sock  /<app name>

If you're running your application on TCP/IP instead of Unix socket (as above), for instance on TCP port 3000:
vf -w 3 -p 3000 <app name>

then you can send requests to it and receive replies:
cgi-fcgi -connect 127.0.0.1:3000  /

Examples
The following example will create an application with 3 Vely request files, and test them as FastCGI application (both using Unix sockets and TCP) and as a command_line program. You can execute below bash commands one by one, or save them in a single file and run:
#
#Create hello_1.vely source file
#
echo '#include "vely.h"

void hello_1()
{
out-header default
@Hello World #1
}' > hello_1.vely

#
#Create hello_2.vely source file
#
echo '#include "vely.h"

void hello_2()
{
out-header default
@Hello World #2
}' > hello_2.vely

#
#Create Vely application named "hithere"
#
sudo vf -i -u $(whoami) hithere

#
#Make the application
#
vv -q

#
#Stop (any) running server processes
#Start 2 workers for your application running as FastCGI server via Unix sockets
#
vf -m quit hithere
vf -w 2 hithere

#
#Send request to execute hello_1() function.
#
export REQUEST_METHOD=GET
export SCRIPT_NAME="/hithere"
export PATH_INFO="/hello_1"
export VV_SILENT_HEADER=no
cgi-fcgi -connect /var/lib/vv/hithere/sock/sock  /hithere

#
#Restart your application as FastCGI server running on TCP port 3000
#Then send request to execute hello_2() function.
#
vf -m quit hithere
vf -p 3000 -w 2 hithere
export REQUEST_METHOD=GET
export SCRIPT_NAME="/hithere"
export PATH_INFO="/hello_2"
export VV_SILENT_HEADER=no
cgi-fcgi -connect 127.0.0.1:3000 /

#
#Run your application as command line program. Supress HTTP headers.
#
export REQUEST_METHOD=GET
export SCRIPT_NAME="/hithere"
export PATH_INFO="/hello_2"
export VV_SILENT_HEADER=no
/var/lib/vv/bld/hithere/hithere

See also
Running application ( application_setup   CGI   Client_API   command_line   containerize_application   FastCGI   FastCGI_client   plain_C_FCGI  )  SEE ALL (documentation)
FastCGI

To run Vely web application with FastCGI protocol, you need to setup a reverse proxy, i.e. a web server that will forward request and send replies back to clients. Vely application runs as a number of (zero or more) background processes in parallel, processing requests.
Setting up reverse proxy (web server)
To access your application via a reverse proxy (i.e. web server), generally you need to add a proxy directive and restart the web server.

If you use Apache, you need to connect it to your application, see connect_apache_tcp_socket (for using TCP sockets) and connect_apache_unix_socket (for using Unix sockets).

If you use Nginx, you need to connect it to your application, see connect_nginx_tcp_socket (for using TCP sockets) and connect_nginx_unix_socket (for using Unix sockets).
Starting FastCGI server processes
Use vf, for example:
vf <app name>

which in general will start zero or more background resident process(es) (daemons) that process requests in parallel.
Connection timeout
In a heavy-load environment, a client's connection may be rejected by the server. This may happen if the client runs very slowly due to swapping perhaps. Once a client establishes a connection, it has up to 5 seconds by default to send data; if it doesn't, the server will close the connection. Typically, FastCGI clients send data right away, but due to a heavy load, this time may be longer. To set the connection timeout in milliseconds, set the following variable before starting the application server, for instance:
export "LIBFCGI_IS_AF_UNIX_KEEPER_POLL_TIMEOUT"="8000"
vf -w 1 <app name>

In this case, the timeout is set to 8 seconds.
See also
Running application ( application_setup   CGI   Client_API   command_line   containerize_application   FastCGI   FastCGI_client   plain_C_FCGI  )  SEE ALL (documentation)
file-position

Purpose: Set a position or get a current position for an open file.

file-position file-id <file id> \
    ( set <position> ) | ( get [ define ] <position> ) \
    [ status [ define ] <status> ]

file-position will set or get position for a file opened with open-file, where <file id> is an open file identifier.

If "set" clause is used, file position is set to <position>.

If "get" clause is used, file position is obtained in <position>, which can be created with optional "define".

The optional <status> in "status" clause will be VV_OKAY if set/get succeeded, or VV_ERR_POSITION if not. <status> can be created with optional "define".

Note that setting position past the last byte of file is okay for writing - in this case the bytes between the end of file and the <position> are filled with null-bytes when the write operation occurs.
Examples
Open file "testwrite" and set file byte position to 100, then obtain it later:
open-file "testwrite" file-id define nf
file-position file-id nf set 100
...
file-position file-id nf get define pos

See also open-file.
See also
Files ( close-file   copy-file   delete-file   file-position   file_storage   file_uploading   lock-file   open-file   read-file   read-line   rename-file   stat-file   temporary_file   uniq-file   unlock-file   write-file  )  SEE ALL (documentation)
File storage

Vely provides a file directory that you can use for any general purpose, including for storing temporary_files. This directory is also used for automatic upload of files from clients. It provides a two-tier directory system, with sub-directories automatically created to spread the files for faster access.

Files in Vely file directory are located in the sub-directories of:
/var/lib/vv/<app_name>/app/file

If you wish to place this directory in another physical location, see vv.

You can create files here by means of uniq-file, where a new unique file is created - with the file name made available to you. This directory is also used for uploading of files from clients (such as web browsers or mobile device cameras) - the uploading is handled automatically by Vely (see input-param).

In general, a number of sub-directories is created within the file directory that contain files, with (currently) the maximum of 40,000 directories with unlimited files per each directory (so if you had 50,000 files per each directory, then you could store a total of about 2 billion files). This scheme also allows for faster access to file nodes due to relatively low number of sub-directories and files randomly spread per each such sub-directory. The random spreading of files across subdirectories is done automatically.

Do not rename file names or sub-directory names stored under file directory.
See also
Files ( close-file   copy-file   delete-file   file-position   file_storage   file_uploading   lock-file   open-file   read-file   read-line   rename-file   stat-file   temporary_file   uniq-file   unlock-file   write-file  )  SEE ALL (documentation)
File uploading

Purpose: Upload a file to server.

input-param <name>

Files uploaded via client (such as a browser, curl etc.) are input-parameters.

Vely uploads files for you automatically, meaning you do not have to write any code for that specific purpose. An uploaded file will be stored in file_storage, with path and name of such a file generated by Vely to be unique. For example, a file uploaded might be named "/home/user/file/d0/f31881". When file is uploaded, the following input parameters can be obtained, assuming "name" attribute of "input" element is "myfile":

For example, for an HTML form which is uploading a file named "myfile", such as
<input type='file' name='myfile'>

your code that handles this might be:
input-param myfile_filename
input-param myfile_location
input-param myfile_ext
input-param myfile_size

You have uploaded file <<p-web myfile_filename>> to a server file at <<p-web myfile_location>>

See also
Files ( close-file   copy-file   delete-file   file-position   file_storage   file_uploading   lock-file   open-file   read-file   read-line   rename-file   stat-file   temporary_file   uniq-file   unlock-file   write-file  )  SEE ALL (documentation)
finish-output

Purpose: Finishes the request output.

finish-output

finish-output will flush out and conclude all of the request's output (see output_statement). Any such output afterwards will silently fail to do so. As far as the client is concerned (or the user running a command_line program), all the output from the request is complete.

This statement is useful when you need to continue work after the request's output is complete. For example, if the request is a long-running one, you can inform the client that the job has started, and then take any amount of time to actually complete the job, without worrying about client timeouts. The client can inquire about the job status via a different request, or be informed via email etc.
Examples
finish-output

See also
Output ( finish-output   flush-output   output_statement   p-dbl   pf-out   pf-url   pf-web   p-num   p-out   p-path   p-url   p-web  )  SEE ALL (documentation)
flush-output

Purpose: Flush output.

flush-output

Use flush-output statement to flush any pending output to the client (such as FastCGI_client) or from the command_line application.

This can be useful if the complete output would take longer to produce and intermittent partial output would be needed.

Note that whether the client will actually receive the output in this fashion depends on several factors, including the client itself and any intermediaries such as proxy servers.
Examples
In this case the complete output may take at least 20 seconds. With flush-output, the message "This is partial output" will be flushed out immediately.
@This is partial output
flush-output
sleep(20);
@This is final output

See also
Output ( finish-output   flush-output   output_statement   p-dbl   pf-out   pf-url   pf-web   p-num   p-out   p-path   p-url   p-web  )  SEE ALL (documentation)
get-app

Purpose: Obtain data that describes the application.

get-app \
    name | directory | trace-directory | file-directory \
        | db-vendor <database configuration> | upload-size \
        | path | process-data \
    to [ define ] <variable>

Application-related variables can be obtained with get-app statement. The following application variables can be obtained (they are all strings except upload-size):
Note that all the string values above (except "process-data") should be treated as constants - do not change them as that may cause program to malfunction. If you want to alter any of these values, make a copy first (see copy-string).
Examples
Get the name of Vely application:
get-app name to define appname

Get the vendor of database db:
get-app db-vendor db to define dbv
if (!strcmp (dbv, VV_POSTGRES)) {
    // do something Postgres specific
}

See also
Application information ( get-app   set-app  )  SEE ALL (documentation)
get-cookie

Purpose: Get cookie value.

get-cookie [ define ] <cookie value> = <cookie name>

get-cookie obtains the value of a cookie with the name given by <cookie name>. A cookie would be obtained from an incoming request from the client (such as web browser) or it would be set using set-cookie.

The value of cookie is stored in <cookie value>. If it does not exist, you can create it within the statement by using "define" clause. <cookie value> is allocated memory.

Cookies are often used to persist user data on the client, such as for maintaining session security or for convenience of identifying the user or its actions.
Examples
Get value of cookie named "my_cookie_name" - variable my_cookie_value will hold its value:
get-cookie define my_cookie_value="my_cookie_name"

See also
Cookies ( delete-cookie   get-cookie   set-cookie  )  SEE ALL (documentation)
get-hash

Purpose: Get usage specifics for a hash table.

get-hash  <hash > \
    ( length [ define ] <length> ) \
    | ( size [ define ] <size> ) \
    | ( average-reads [ define ] <reads> )

get-hash provides usage specifics of a hash table <hash> (created by new-hash).

Use "length" clause to obtain its <length> (i.e. the number of elements stored in it), "size" clause to obtain its <size> (i.e. the number of "buckets", or possible hash codes) and "average-reads" clause to obtains the average number of <reads> (i.e. how many string comparisons are needed on average to find a key).

Each of these number variables can be created with "define".

This information may be useful in determining the performance of a hash, and whether resize-hash is indicated.
Examples
get-hash h length define l size define s average-reads define r

See also
Hash table ( get-hash   new-hash   purge-hash   read-hash   resize-hash   write-hash  )  SEE ALL (documentation)
get-req

Purpose: Obtain data that describes the input request.

get-req \
    data | errno | error | cookie-count | cookie <cookie index> \
        | arg-count | arg-value <arg index> | input-count | \
        | input-value <input index> | input-name <input index> \
        | header <header> | referring-url | method \
        | content-type | trace-file | process-id | name \
    to [ define ] <variable>

Variables related to an input request can be obtained with get-req statement and the result stored into <variable>. The following request variables can be obtained (they are strings except where stated otherwise):
Note that all the string values above (except "data") should be treated as constants - do not change them as that may cause program to malfunction. If you want to alter any of these values, make a copy first (see copy-string).
Examples
Get the name of current trace file:
get-req trace-file to define trace_file

See also
Request information ( get-req   input-param   request-body   set-input   set-req  )  SEE ALL (documentation)
get-sys

Purpose: Obtain data that describes the system.

get-sys \
    environment <var name> | web-environment <var name> \
        | os-name | os-version \
    to [ define ] <variable>

System-describing variables can be obtained with get-sys statement and the result stored into <variable>. The following system variables can be obtained:
Note that all the string values above should be treated as constants - do not change them as that may cause program to malfunction. If you want to alter any of these values, make a copy first (see copy-string).
Examples
Get the name of the Operating System
get-sys os-name to define os_name

See also
System information ( get-sys  )  SEE ALL (documentation)
get-time

Purpose: Get time.

get-time to [ define ] <time var> \
    [ timezone <tz> ] \
    [ year <year> ] \
    [ month <month> ] \
    [ day <day> ] \
    [ hour <hour> ] \
    [ minute <minute> ] \
    [ second <second> ] \
    [ format <format> ]

get-time produces <time var> variable that contains string with time. <time var> is allocated memory.

If none of "year", "month", "day", "hour", "minute" or "second" clauses are used, then current time is produced. If variable <time var> does not exist, you can use define clause to create it within the statement.

Use timezone to specify that time produced will be in timezone <tz>. For example if <tz> is "EST", that means Eastern Standard Time, while "MST" means Mountain Standard Time. The exact way to get a full list of timezones recognized on your system may vary, but on many systems you can use:
timedatectl list-timezones

So for example to get the time in Phoenix, Arizona you could use "America/Phoenix" for <tz>. If timezone clause is omitted, then time is produced in "GMT" timezone by default. DST (Daylight Savings Time) is automatically adjusted.

Each variable specified with "year", "month", "day", "hour", "minute" or "second" is a time to be added or subtracted. For example "year 2" means add 2 years to the current data, and "year -4" means subtract 4 years, whereas "hour -4" means  subtract 4 hours, etc. So for example, a moment in time that is 2 years into the future minus 5 days minus 1 hour is:
get-time to time_var year 2 day -5 hour -1

<format> allows you to get the time in any string format you like, using the specifiers available in C "strftime". For example, if <format> is "%A, %B %d %Y, %l:%M %p %Z", it will produce something like "Sunday, November 28 2021, 9:07 PM MST". The default format is "UTC/GMT" format, which for instance, is suitable for use with cookie timestamps, and looks something like "Mon, 16 Jul 2012 00:03:01 GMT".
Examples
To get current time in "GMT" timezone, in a format that is suitable for use with set-cookie (for example to set expiration date):
get-time to define mytime

To get the time in the same format, only 1 year and 2 months in the future:
get-time to define mytime year 1 month 2

An example of a future date (1 year, 3 months, 4 days, 7 hours, 15 minutes and 22 seconds into the future), in a specific format (see "strftime"):
get-time to time_var timezone "MST" year 1 month 3 day 4 hour 7 minute 15 second 22 format "%A, %B %d %Y, %l:%M %p %Z"

See also
Time ( get-time  )  SEE ALL (documentation)
Getting URL

To get the URL used to make a request, use get-sys statement with "web-environment" clause.

For a query string, get the "QUERY_STRING" environment variable. For a full URL path, combine "SCRIPT_NAME" and "PATH_INFO" variables.

To get a request body, use request-body statement.
Examples
To get the full URL path in variable "fpath", concatenate "SCRIPT_NAME" and "PATH_INFO":
get-sys web-environment "SCRIPT_NAME" to define sname
get-sys web-environment "PATH_INFO" to define pinfo
(( define fpath
    @<<p-out sname>><<p-out pinfo>>
))

See also
Requests ( after_request_handler   before_request_handler   building_URL   getting_URL   global_request_data   non_request   normalized_URL   request   request_URL   startup_handler   vely_dispatch_request  )  SEE ALL (documentation)
Global process data

The purpose of global-process data is to help with use of global cross-request variable(s) anywhere in your code. The difference from global_request_data is in the scope: global request data is valid only for a single request, while global-process data is valid for all requests processed in sequence by a single process (see how_vely_works).

While you can create global variables in C (with C's "extern" to make them globally available), Vely's global-process data is an easier and more structured way to share data globally across requests. This way, you do not have to define and maintain global variables. It also makes it more maintainable as such variables are well-encapsulated and easy to track (for instance in code reviews).
What is global-process data
Global-process data is a generic pointer (void*) that points to any memory you wish to be shared among all your code across all the requests that any given process serves. This data cannot be shared between different processes. It is usually a structure containing information globally pertinent to your application that is shared between separate requests. The pointer's scope is a lifetime of the process and thus spans all the requests it serves. The global-process data pointer is initialized to NULL before startup_handler executes. It is stored in Vely's internal process context structure so it can be used anywhere.
Setting
set-app process-data <data>

where <data> is a pointer to any memory you wish to use anywhere in your code across all process' requests. This memory must be unmanaged; see memory_handling and the example below.
Getting
Global-process data can be obtained anywhere in your code with:
get-app process-data to <data>

where <data> is a pointer to any type.
Usage
Typically you would define a structure or a typedef that encapsulates any data that needs to be shared throughout your code in all requests in any given process. Different processes have separate process data that cannot be shared between them. For this reason, global-process data is not the same as global application data; rather it is global within any given process and not beyond that process.

In startup_handler, you would create a variable (or an array) of this type by using unmanaged new-mem that producess cross-request memory - this is done by using "off" clause in manage-memory. Use new-mem for any members that need allocating. Initialize anything that needs it. Finally, switch back to managed memory by using "on" clause in manage-memory statement.

The reason for using unmanaged memory is because otherwise the data would be automatically released at the end of the following request and would not stay allocated for the life of the process.

Next, save the pointer to the variable (or an array) you created by using set-app.

Finally, anywhere you need to set or get any data, use get-app with "process-data" clause to get the pointer and manipulate or read your global-process data. Don't forget that any manipulation must be in unmanaged mode as well.
Examples
Suppose your application has an include file my.h in which you define type "procdata":
#ifndef _MY
#define _MY

typedef struct s_procdata {
    bool some_flag;
    bool another_flag;
    char *ptr;
} procdata;

#endif

Your startup_handler (i.e. file _startup.vely) might look like this - note that my.h is included, providing your type definition "procdata":
#include "vely.h"
#include "my.h"

void _startup () {
    procdata *rd; // A pointer to global request data

    // Use unmanaged memory for process-wide data
    manage-memory off
    // Allocate global request data
    new-mem rd size sizeof(procdata)
    // Turn back on managed memory
    manage-memory on

    // Initialize values in it
    rd->some_flag = false;
    rd->another_flag = false;

    // Save the pointer so you can use it anywhere with get-req
    set-app process-data rd
}

In the above code, a new pointer "rd" of type "procdata" is created with new-mem. Data initialization takes place - anything that needs initialization should be initialized. Finally, pointer "rd" is saved to process' internal structure with set-app, so it can be used in any request for the life of the process.

In your code, wherever it's needed, you can obtain this data into a local pointer of the same type "procdata" (in this case pointer name is "mydata"). You can do that with get-app and then examine or set any global-process variable you wish:
#include "vely.h"
#include "my.h"

void mycode () {

    ...
    procdata *mydata; // declare local pointer 

    // get the actual value of a pointer, so now it points to global request data
    get-app process-data to mydata

    // do whatever you want with the data: examine, set etc.
    if (mydata->another_flag) {
        mydata->some_flag = true;
        // If you free, allocate or reallocate global-process data, use unmanaged memory
        manage-memory off
        resize-mem my_data->ptr size 1024
        manage-memory on
    }
}

See also
Process ( global_process_data  )  SEE ALL (documentation)
Global request data

The purpose of global-request data is to facilitate using global variable(s) within a single request anywhere in your code. This is different from global_process_data which is used across many requests.

You can of course create global variables in C (and use C's "extern" to make them available anywhere). Vely's global-request data is an easier and more structured way to share data globally within a single request, without having to actually define and maintain global variables. It also makes it more maintainable because the usage of shared global information is well-encapsulated and easy to track (for instance in code reviews).
What is global-request data
Global-request data is a generic pointer (void*) that points to any memory you wish to be shared among all your code within a single request. This memory is usually a structure containing information globally pertinent to your application. The pointer's scope is a single request, and it is initialized to NULL before each request. It is stored in Vely's internal request structure so it can be used anywhere.
Setting
set-req data <data>

where <data> is a pointer to any memory you wish to use anywhere in your code.
Getting
Global-request data can be obtained anywhere in your code with:
get-req data to <data>

where <data> is a pointer to any type.
Usage
Typically you would define a structure or a typedef that encapsulates any data that needs to be shared throughout your code during a single request processing.

Then in before_request_handler, you would create the variable (or an array) of this type by using new-mem - this way the memory is automatically released at the end of the request. Use new-mem for any members that need allocating, thus eliminating the possibility of memory leaks. Initialize anything that needs it.

Next, save the pointer to the variable (or an array) you created by using set-req.

Finally, anywhere you need to set or get any data, use get-req to get the pointer and manipulate or read your global-request data.
Examples
Suppose your application has an include file my.h in which you define type "reqdata":
#ifndef _MY
#define _MY

typedef struct s_reqdata {
    bool some_flag;
    bool another_flag;
    char *ptr;
} reqdata;

#endif

Your before_request_handler might look like this - note that my.h is included, providing your type definition "reqdata":
#include "vely.h"
#include "my.h"

void _before () {
    reqdata *rd; // A pointer to global-request data

    // Allocate global-request data
    new-mem rd size sizeof(reqdata)

    // Initialize values in it
    rd->some_flag = false;
    rd->another_flag = false;

    // Save the pointer so you can use it anywhere with get-req
    set-req data rd
}

In the above code, a new pointer "rd" to type "reqdata" is created with new-mem. Data initialization takes place - anything that needs initialization should be initialized. Finally, pointer "rd" is saved to request's internal structure with set-req, so it can be used anywhere during request processing.

In your code, wherever it's needed, you can obtain this data into a local pointer of the same type "reqdata" (in this case pointer name is "mydata"). You can do that with get-req and then examine or set any global variable you wish:
#include "vely.h"
#include "my.h"

void mycode () {

    ...
    reqdata *mydata; // declare local pointer 

    // get the actual value of a pointer, so now it points to global-request data
    get-req data to mydata

    // do whatever you want with the data: examine, set etc.
    if (mydata->another_flag) {
        mydata->some_flag = true;
        my_data->ptr = "some data";
    }
}

See also
Requests ( after_request_handler   before_request_handler   building_URL   getting_URL   global_request_data   non_request   normalized_URL   request   request_URL   startup_handler   vely_dispatch_request  )  SEE ALL (documentation)
hash-string

Purpose: Hash a string.

hash-string <string> to [ define ] <result> \
    [ binary [ <binary> ] [ output-length [ define ] <output length> ] \
    [ digest <digest algorithm> ]

hash-string produces by default a SHA256 hash of <string> (if "digest" clause is not used), and stores the result into <result> which can be created with optional "define". <result> is allocated memory. You can use a different <digest algorithm> in "digest" clause (for example "SHA3-256"). To see a list of available digests:
#get digests
openssl list -digest-algorithms

If "binary" clause is used without optional boolean expression <binary>, or if <binary> evaluates to true, then the <result> is a binary string that may contain null-characters. With the default SHA256, it is 32 bytes in length, while for instance with SHA3-384 it is 48 bytes in length, etc.

Without "binary" clause, or if <binary> evaluates to false, the <result> is null-terminated and each binary byte is converted to two hexadecimal characters ("0"-"9" and "a"-"f"), hence <result> is twice as long as with "binary" clause.

The actual length of <result> (regardless of whether "binary" clause is used or not) can be obtained in the optional "output-length" clause in <output length>, which can be created with optional "define".
Examples
String "hash" will have a hashed value of the given string, an example of which might look like "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855":
hash-string "hello world" to define hash

Using a different digest:
hash-string "hello world" to define hash digest "sha3-384"

Producing a binary value instead of a null-terminated hexadecimal string:
hash-string "hello world" to define hash digest "sha3-384" binary output-length define outlen

See also
Encryption ( decrypt-data   derive-key   encrypt-data   hash-string   random-crypto   random-string  )  SEE ALL (documentation)
How vely works

Creating an application
A Vely application <app name> is created by an application owner, i.e. an Operating System user who will own its processes and data, by using vf, such as:
sudo vf -i -u $(whoami) <app name>

This will create directory structure described here. Vely application can be used as a FastCGI server, a command_line program or a CGI program. Note that vf is the only Vely utility requiring sudo privileges, and only for application creation.

If you will build an application from source code, the above vf command must run in the directory that contains the application source code. Each application must be in its own separate directory which contains all of its source code.
How Vely works: code, requests and processes
In a single step, Vely code (.vely files) is precompiled entirely into C, linked, and ready to run as a command-line program or as an application server, or both. Error reporting is based on Vely source code (.vely files), though you can specify generated code lines to be used.

vv will preprocess .vely source files and generate C code for supported statement_APIs (database, file, strings etc.). Then, vv builds both a command-line program and a FastCGI application server executable for application <app name> (the name <app name> is specified when running vf with "-i" flag to create the application). Executables created are located in directory:
/var/lib/vv/bld/<app name>

A FastCGI application runs as a daemon, i.e. it runs in the background and does not exit, serving incoming requests. Typically, a number of FastCGI processes are started to serve incoming processes in parallel; each process serves a single request at a time and thus can serve great many requests during its lifetime; in adaptive mode, the number of processes running may vary according to the load - including zero running processes for near-zero memory usage.

A command-line program works exactly the same way as a FastCGI application; it takes the same input and produces the same output. A command-line program runs as a single process and processes a single request.
.vely files and requests
Files with the .vely extension are Vely source files. You must have at least one Vely source file (.vely file). If a Vely source file starts with an underscore, it is a non_request file, which can implement anything; otherwise it is a request file. A request file implements a request handler, which handles a request_URL sent by the client (such as reverse proxies like Apache or Nginx) or provided to command_line program via environment variables.

Requests and request handlers
To specify the request handler, specify its name in the URL after the application path (see request_URL). A request handler must implement a function named after the base name of .vely file. For example, in a URL such as:
/stocks/add-stock?ticker=ABC&price=130

the application path is "/stocks" (by default it's the application name) and the request name is "add_stock" (hyphens are converted to underscore by Vely). The request name handler must be implemented as function
void add_stock() {...}

in file add_stock.vely.

This correlation between requests and file names makes it easy to find the code you want and to maintain it. Vely will generate the code that will automatically route input requests to the appropriate code based on the request name, so in the above example Vely will automatically invoke function add_stock() implemented in file "add_stock.vely" with input parameters "ticker" and "price" (and their values) available via input-param statement:
void add_stock() {
    input-param ticker
    input-param price
    ...
}

Non-request source code
Not every source code file was meant to handle a request. You might have a number of non_request files that implement other code, such as commonly used functions. Such files are non-request source code and their names must start with underscore ("_").

For example, file "_display_table.vely" might implement a common HTML table displaying code, which is used in various request handlers.

Some non-request source files have predefined meaning: "_startup.vely" (startup_handler), "_before.vely" (before_request_handler) and "_after.vely" (after_request_handler).
Building an application, Makefile
Vely builds an application by looking at .vely and .h files present in the current directory. Therefore you don't have to write a Makefile.

You don't have to specify what source code your application uses - all .vely files will be automatically picked up and compiled, and request handlers (.vely files that don't start with an underscore) are used to generate vely_dispatch_request.vely file (a request-dispatcher). Thus, it is easy to view at a glance what kind of requests an application serves and what is its general purpose. Files with .h extension are standard C include files and are automatically included.

You can see the default implementations for all auto-generated files in directory
/var/lib/vv/bld/<app name>

An example of building a Vely application is:
vv -q --db=mariadb:notes

"-q" options is the most important one - it specifies that you intend to build. In this instance, MariaDB database (named "notes") is used.

Vely builds an application using standard Linux "make" mechanism. Note that when you change the command line options (see vv) in a build statement like above, it has the effect of "make clean", recompiling source files.
Directories and files
While you can keep and compile Vely source files in any directory, the directories used by Vely are always under /var/lib/vv directory. A Vely application is always owned by a single Operating System user, while different applications can be owned by different users. This is the directory structure:
While Vely directories are fixed, you can effectively change their location by creating a soft link. This way, your directories and files can be elsewhere, even on a different disk. For example, to house your files on a different disk:
ln -s /home/disk0/file /var/lib/vv/<app name>/app/file

Memory allocation
Vely uses its own memory allocator based on standard Linux malloc family of functions. It is used internally and for allocating the results of any statements (each statement's documentation specifies which results, if any, are such). The memory will be automatically freed upon the completion of the request - you can also explicitly free them if you need to. This approach avoids costly errors, crashes and memory leaks. See memory_handling.
Data types
Vely statement_APIs use mostly strings (char* type only, i.e. no "const char*") and numbers ("num" type, which is "long long" for maximum range of uses, "num32" for a 32-bit integer, "dbl" for a double precision number), "bool" (with true and false value as provided by stdbool.h) and other data types as specified. When a "number" is specified anywhere without an additional qualification, it is assumed to be of "num" type. If a type is not specified, it is either a string or a number, depending on the context in which it is used - in those cases the type is not specified for simplicity.
Names of objects
Do not use object names (such as variables, functions, types) that start with "_vv", "_vely", "vv" or "vely" as those are reserved by Vely.
See also
General ( deploying_application   how_vely_works   quality_control   rename_files   SELinux   vely   vely_architecture   vely_removal   vf   vv   why_C_and_Vely  )  SEE ALL (documentation)
Inline code

Purpose: Inline Vely code in string statements.

<<vely code>>

You can write Vely code within other Vely statements that build strings, whether for output (output_statement) or to build other strings (write-string), by using << and >> delimiters. There is no need for white space between the delimiters and Vely code, i.e. you could either write
<<p-web my_string>>

or
<<  p-web my_string  >>

to the same effect.
Examples
query-result statement displays the result of a query, and in the following code it's used to display results on the same line as other output (i.e. as inline):
run-query
   @<tr>
   @    <td>
   @        First name is << query-result  firstName >>
   @    </td>
   @    <td>
   @        Last name is << query-result# employees, lastName >>
   @    </td>
   @</tr>
end-query

In the code below, "some_function()" is a C function that uses Vely code to output some text, and it's used inline to output "Hello world":
@Hello <<.some_function();>>

(note the usage of dot statement to use any C expression, and finishing with semi-colon as a C statement). Function "some_function()" would simply output "world":
void some_function()
{
    @world
}

A write-string can be built with other Vely statements inlined, in this case we print the value of another string, resulting in "There is 42 minutes left!":
char * mins="42";
(( define my_string
There is <<p-out mins>> minutes left!
))

See also
Language ( dot   inline_code   statement_APIs   syntax_highlighting   unused-var  )  SEE ALL (documentation)
input-param

Purpose: Get input parameter.

input-param <name>

To obtain input parameters from an incoming request, use input-param. Whether it's a web or command-line application, input parameters are specified as name/value pairs (see request_URL). The value of input parameter <name> will be in the string variable with the same name. Input parameter values are trimmed for whitespace (both on left and right).

Input parameter name can be made up of alphanumeric characters, hyphen or underscore only and cannot start with a digit. Note that a hyphen is automatically converted to underscore, so an input parameter "some-parameter" in the HTTP request will be known in your code as "some_parameter". For example in URL:
http://<your web server>/<app name>/some-request?my-par1=val1&my-par2=val2

the request name will be "some_request" (handled by some_request.vely source code), and input parameters will be "my_par1" and "my_par2":
input-param my_par1
input-param my_par2

File uploads
File uploads are handled as input parameters as well, see file_uploading.
Using input parameters
As an example, for HTML form input parameters named "param1" with value "value1" and "param2" with value "value2":
<input type='hidden' name='param1' value='value1'>
<input type='hidden' name='param2' value='value2'>

you can get these parameters and print out their values by using:
input-param param1
input-param param2
p-web param1
p-web param2

the output of which is:
value1
value2

A request may be in the form of a web link URL, and getting the parameter values is the same:
http://<your web server>/<app name>/<request name>&param1=value1&param2=value2

Duplicate parameter names
If there are multiple input parameters with the same name, such as
http://<web address>/<app name>/<request name>?par=val1&par=val2

the value of input parameter "par" is undefined. Do not specify multiple input parameters with the same name.
See also
Request information ( get-req   input-param   request-body   set-input   set-req  )  SEE ALL (documentation)
json-utf8

Purpose: Convert JSON text to UTF8 string.

json-utf8 <text> \
    [ status [ define ] <status> ] \
    [ error-text [ define ] <error text> ]

json-utf8 will convert JSON string value <text> to UTF8. <text> itself will hold the resulting UTF8 string. If you don't wish <text> to be modified, make a copy of it first (see copy-string). See utf8-json for the reverse conversion and data standards information.

You can obtain the optional <status> in "status" clause, which can be created with optional "define". <status> is VV_OKAY if successful, or VV_ERR_UTF8 if there was an error, in which case optional <error text> in "error-text" clause (which can be created with optional "define") will contain the error message.
Examples
// JSON Unicode value to encode to UTF8
char txt[] = "\u0459\\\"Doc\\\"\\n\\t\\b\\f\\r\\t\\u21d7\\u21d8\\t\\u25b7\\u25ee\\uD834\\uDD1E\\u13eb\\u2ca0\\u0448\\n\\/\\\"()\\t";

// Convert to UTF8 
json-utf8 txt status define txt_status error-text define txt_error

// Expected UTF8 result
char utf8[] = \"Doc\"\n\t\b\f\r\t⇗⇘\t▷◮𝄞ᏫⲠш\n/\"()\t";

// Make sure conversion was successful
if (strcmp (utf8, txt) || txt_status != VV_OKAY || txt_error[0] != 0) {
    @Error in converting JSON string to UTF8
}

See also
UTF8 ( json-utf8   utf8-json  )  SEE ALL (documentation)
License

Licensing and copyright
Vely is Free Open Source software licensed under Eclipse Public License 2.

Vely is copyright (c) 2017 Dasoftver LLC.
Discussion
The following discussion is not legal advice.

Vely makes use of the following dynamically-linked libraries (with copyright by their respective owners), and only if your application actually uses them:
These libraries are installed as Vely package dependencies by the Operating System packager (such as apt, dnf, pacman or zypper), but are not distributed with Vely. Vely does not link to any outside static libraries.

Vely uses FNV-1a hash function, which is released in the public domain (see wikipedia page) and is not patented (see Landon Noll's web page).

Vely source code uses SPDX, an open ISO standard for communicating sofware bill of material information (such as licenses), to improve on open source licensing compliance for companies and communities.

With regards to any application written using Vely, the prevailing (the most restrictive, i.e. final) license, from the Open Source License Compliance perspective, is the EPL-2 license.

If you change Vely code itself (i.e. create a derivative work) and distribute it, then the result of such changes should be made open source under the EPL-2 license. Note that code generated by Vely is not considered derivative work.

EPL-2 is know as "business friendly" license that makes it easy and straightforward to use Vely in commercial applications. If you are a software developer using Vely to write any applications, commercial or otherwise, you do not have to release your source code.

For a complete list of requirements, please refer to the EPL-2 license.
See also
License ( license  )  SEE ALL (documentation)
lock-file

Purpose: Locks file exclusively.

lock-file <file path> id [ define ] <lock id> status [ define ] <status>

lock-file attempts to create a file (deleting all contents of the previous file, if existed), and exclusively lock it. If successful, no other process can do the same unless current process ends or calls unlock-file. This statement is non-blocking, thus you can check in a sleepy loop for success.

<file path> should be either an existing or non-existing file with a valid file path.

<lock id> (in "id" clause) is a file descriptor associated with locked file.

<status> (in "status" clause) represents the status of file locking: VV_OKAY if successfully locked, VV_ERR_FAILED if cannot lock (likely because another process holds a lock), VV_ERR_INVALID if the path of the file is invalid (i.e. if it is empty), VV_ERR_CREATE if lock file cannot be created.

Generally, this statement is used for process control. A process would use lock-file to start working on a job that only one process can do at a time; once done, by using unlock-file statement, another process will be able to issue a successful lock-file call. Typically, lock-file is issued in a sleepy loop, waiting for a resource to be released.

Note that file lock is not carried across children processes (i.e. if your process creates children, such as with exec-program, then such children must obtain their own lock). In addition, if a process serving a request terminates before the request could issue unlock-file, the lock will be automatically released.

You can use any file name (likely choose a name that signifies the purpose of a lock, as long as you have permissions to create it), and create any number of them. This way you can create as many "binary semaphore"-like objects as you like.
Examples
// Get application home directory
get-app directory to define dir

// Create lock file name
write-string define fname
@<<p-out dir>>/.lock
end-write-string

// Enter loop in which something is done, OR, program waits for others to complete their work before doing its own
num lockid;
while (1) {

    // Attempt to lock file
    lock-file fname id lockid status define lockst

    // Check the status of locking
    if (lockst == VV_OKAY) {

        // File successfully locked, simulate working for 20 seconds
        @WORKING
        sleep (20);
        @DONE
        // Exit while loop
        break;

    } else if (lockst == VV_ERR_FAILED) {

        // Another process holds the lock, wait, try again
        sleep(1);
        @WAITING
        continue;

    } else if (lockst == VV_ERR_OPEN || lockst == VV_ERR_INVALID) {

        // Errors
        @BAD LOCK
        return;
    }
}

// Once done, unlock file
unlock-file id lockid
return;

See also
Files ( close-file   copy-file   delete-file   file-position   file_storage   file_uploading   lock-file   open-file   read-file   read-line   rename-file   stat-file   temporary_file   uniq-file   unlock-file   write-file  )  SEE ALL (documentation)
lower-string

Purpose: Lower-case a string.

lower-string <string>

lower-string converts all <string>'s characters to lower case.
Examples
The resulting "str" is "good":
char str[]="GOOD";
lower-string str

See also
Strings ( copy-string   count-substring   lower-string   split-string   trim-string   upper-string   write-string  )  SEE ALL (documentation)
manage-memory

Purpose: Turn on/off managed memory.

manage-memory [ on | off | <expression> ]

By default, Vely memory is managed - this means any outstanding memory used by your request will be freed when the request ends (see memory_handling). You can change this behavior for a time by using manage-memory.

If "on" clause is used, any memory allocated afterwards by any Vely statement is freed automatically (the default behavior). This is managed memory mode.

If "off" clause is used, any memory allocated afterwards by any Vely statement is not freed automatically; this behavior will be in effect until the end of the request or until manage-memory with "on" clause is called. This is unmanaged memory mode.

If boolean <expression> is given, and if it evaluates to true then it's the same as "on" clause; if it evaluates to false then it's the same as "off" clause.

- Freeing, reallocating, using with other C API
If memory is unmanaged, you can use other memory handling statements on such memory (such as delete-mem or resize-mem) in unmanaged memory mode only - do not directly use C's free(), realloc() and similar. However, unmanaged memory can be used with any other C API (i.e. libraries); this is useful if you receive already allocated memory from such libraries or need to pass malloc-allocated memory to them.

- Scope
manage-memory has effect in the current request only. Regardless of whether managed or unmanaged memory was used last before the request ended, both the after_request_handler and the following request will run with the default behavior (i.e. managed). This is true even when your request exits with exit-request or report-error.

If you have a startup_handler, you can use unmanaged memory; once the execution returns from the startup handler, all memory requests will be managed again.

If you have a before_request_handler or after_request_handler, you can use unmanaged memory; once the execution returns from either handler, it will be managed again.

- Request data
Note that request-specific variables provided by Vely are always managed internal memory. As such, if you want to use them as unmanaged memory, you must make a copy first (for instance with copy-string). This includes input-param, request-body and get-req. See below an example to save a request's input parameter as unmanaged memory available to all future requests.

- Using
You may want to store managed memory pointers in global_process_data in order to be useful across many requests - this is one purpose of unmanaged memory. Another is to use with external C APIs.

Important: using unmanaged memory carries additional responsibilities for the developer, because any such allocated memory must be explicitly deleted when not needed, or there will be a memory leak. Use it only in special circumstances; its use is generally discouraged otherwise. Such circumstances typically involve data that should survive automatic memory deallocation at the end of the request or with external C API.
Examples
In this example, the input parameter to a very first request issued in a process is copied to variable "var", the value of which then persists for all following requests:
input-param ipar
static char *var = NULL;
if (var == NULL) {
    manage-memory off
    copy-string ipar to var
    manage-memory on
}
// Print out input parameter from the very first request
p-out var

Create memory and pass it to another API:
manage-memory off
new-mem define var size 1024
call_some_API(&var);
...
delete-mem var
manage-memory on

See also
Memory ( delete-mem   manage-memory   memory_handling   new-mem   resize-mem  )  SEE ALL (documentation)
match-regex

Purpose: Find, or find and replace patterns in strings using regex (regular expressions).

match-regex <pattern> in <target> \
    [ \
        ( replace-with <replace pattern> \
            result [ define ] <result> \
            [ status [ define ] <status> ] )  \
    | \
        ( status [ define ] <status> \
            [ case-insensitive [ <case-insensitive> ] ] [ single-match [ <single-match> ] ] ) \
    ] \
    [ cache ] \
    [ clear-cache <clear cache> )

match-regex searches <target> string for regex <pattern>. If "replace-with" is specified, then instance(s) of <pattern> in <target> are replaced with <replace pattern> string, and the result is stored in <result> string, which must be different from <target>. <result> is allocated memory.

Both the search and replacement use extended regex syntax (ERE) from the  built-in Linux regex library. The number of found or found/replaced patterns can be obtained in number <status> variable.

If "replace-with" is not specified, then the number of matched <pattern>s within <target> is given in <status> number variable, which in this case must be specified.

If "case-insensitive" is used without optional boolean expression <case-insensitive>, or if <case-insensitive> evaluates to true, then searching for pattern is case insensitive. By default, it is case sensitive.

If "single-match" is specified without optional boolean expression <single-match>, or if <single-match> evaluates to true, then only the very first occurrence of <pattern> in <target> is processed. Otherwise, all occurrences are processed.

<result> and <status> variables can be created within the statement with "define" clause.

If the pattern is bad (i.e. <pattern> is not a correct regular expression), Vely will error out with a message.
Caching and performance
If "cache" clause is used, then regex compilation of <pattern> will be done only once and saved for future use. There is a significant performance benefit when match-regex executes repeatedly with "cache" (such as in case of web applications or in any kind of loop). If <pattern> changes and you need to recompile it once in a while, use "clear-cache" clause. <clear cache> is a "bool" variable; the regex cache is cleared if it is true, and stays if it is false. For example:
bool cl_c;
if (q == 0) cl_c = true; else cl_c = false;
match-regex ps in look_in replace-with "Yes it is \\1!" result res cache clear-cache cl_c

In this case, when "q" is 0, cache will be cleared, and the pattern in variable "ps" will be recompiled. In all other cases, the last computed regex stays the same.

While every pattern is different, when using cache even a relatively small one was shown in tests to speed up the match-regex by about 500%, or 5x faster. Use cache whenever possible as it brings parsing performance close to its theoretical limits.
Subexpressions and back-referencing
Subexpressions are referenced via compatible ERE extension, which is a backslash followed by a number. Because C treats backslash followed by a number as an octal number, you must use double backslash (\\). For example:
match-regex "(good).*(day)" \
    in "Hello, good day!" \
    replace-with "\\2 \\1" \
    result res

will produce string "Hello, day good!" as a result in "res" variable. Each subexpression is within () parenthesis, so for instance "day" in the above pattern is the 2nd subexpression, and is back-referenced as \\2 in replacement.

There can be a maximum of 23 subexpressions.

Note that backreferences to non-existing subexpressions are ignored - for example \\4 when there are only 3 subexpressions. Vely is "smart" about using two digits and differentiating between \\1 and \\10 for instance - it takes into account the actual number of subexpressions and their validity, and selects a proper subexpression even when two digits are used in a backreference.
Examples
Use match-regex statement to find out if a string matches a pattern, for example:
match-regex "SOME (.*) IS (.*)" in "WOW SOME THING IS GOOD HERE" status define st

In this case, the first parameter ("SOME (.*) IS (.*)") is a pattern and you're matching it with string ("WOW SOME THING IS GOOD HERE"). Since there is a match, status variable (defined on the fly as integer) "st" will be "1" (meaning one match was found) - in general it will contain the number of patterns matched.

Search for patterns and replace them by using replace-with clause, for example:
match-regex "SOME (.*) IS ([^ ]+)" in "WOW SOME THING IS GOOD HERE FOR SURE" replace-with "THINGS ARE \\2 YES!" result define res status define st

In this case, the result from replacement will be in a new string variable "res" specified with the result clause, and it will be
WOW THINGS ARE GOOD YES! HERE FOR SURE

The above demonstrates a typical use of subexpressions in regex (meaning "()" statements) and their referencing with "\\1", "\\2" etc. in the order in which they appear. Consult regex documentation for more information.  Status variable specified with status clause ("st" in this example) will contain the number of patterns matched and replaced. As is the case with all Vely statements, you could use "define" clause to create a result/output variable within the statement, or omit it if it's created elsewhere.

Matching is by default case sensitive. Use "case-insensitive" clause to change it to case insensitive, for instance:
match-regex "SOME (.*) IS (.*)" in "WOW some THING IS GOOD HERE" status define st case-insensitive

In the above case, the pattern would not be found without "case-insensitive" clause because "SOME" and "some" would not match. This clause works the same in matching-only as well as replacing strings.

If you want to match only the first occurrence of a pattern, use "single-match" option:
match-regex "SOME ([^ ]+) IS ([^ ]+)" in "WOW SOME THING IS GOOD HERE AND SOME STUFF IS GOOD TOO" status define st single-match

In this case there would be two matches by default ("SOME THING IS GOOD" and "SOME STUFF IS GOOD") but only the first would be found. This clause works the same for replacing as well - only the first occurrence would be replaced.
See also
Regex ( match-regex  )  SEE ALL (documentation)
Memory handling

Managed and unmanaged
Memory allocated by Vely statements (and internally by Vely) can be managed or unmanaged. By default it's managed and it might be the only kind you use.  Managed memory is tracked and always fully freed at the end of a request, even if you don't do it. In fact that is preferrable in most cases (more on this below). This is the default behavior. In this case, your program is in "managed memory mode".

Unmanaged memory is rarely used; more on it further down.
Allocated memory
Some Vely statements allocate memory for their results. Each statement's documentation specifies the clause(s) that allocate memory. Those that have not been so specified do not allocate memory. If memory cannot be allocated, your request will error out (see error_handling); this does not affect other requests.
Managed memory
All such memory is automatically freed at the end of each request, so in general you may not need to free memory at all. If needed, you can free memory with delete-mem or reallocate with resize-mem, or use statements that explicitly do that (such as delete-query for instance). However, unless your program uses lots of memory for longer, it may make sense to not do that and let Vely release it at the end of the request. The reasons for this are:
In the end, it is up to you whether to release memory explicitly, let Vely do it, or a bit of both.

Managed memory is freed even if it is no longer accessible to the program, thus preventing memory leaks; this is important for stability of long-running processes.
Internal process memory
Memory needed for cross-request purposes, such as prepared database statements or process configuration data, is kept for the life of the process.
Memory reuse
Any of the Vely statement_APIs that return allocated memory will always create it new. In other words, the previously allocated memory will not be reused by default.

This is so that common and hard-to-track bugs relating to memory reuse, proper sizing and re-allocation can be avoided, such as passing non-Vely allocated pointers, bad pointers or memory of insufficient allocation. In addition, this approach may increase performance because allocating memory is generally faster than reallocating; given requests are short and frequent by nature, it means increased productivity and performance.

Either way, you can always explicitly reuse memory during request processing by freeing previously allocated memory of any statement that does so, see delete-mem or any other specialized statement (such as delete-query or delete-json for example).
Open file descriptors
Any files opened by open-file statement are automatically closed by Vely at the end of the request. This enhances stability of long-running server processes because Linux system by default offers only about 1000 open files to a process. A bug can quickly exhaust this pool and cause a malfunction or a crash - Vely prevents this by closing any such open files when the request ends. Note that this is true whether your program is currently in managed or unmanaged memory mode.
Unmanaged memory
Unmanaged memory is not tracked and you have to manually free it. It uses the same allocation as standard C's malloc() memory. Memory allocated is unmanaged when "off" clause is used in manage-memory - this is "unmanaged memory mode". Unmanaged memory is used only in special circumstances and in general should not be used without a good reason. See global_process_data.

Memory allocated in managed mode can only be freed or reallocated while in managed mode. And conversely, memory allocated in unmanaged mode can only be freed or reallocated while in unmanaged mode. Attempting otherwise will cause your program to error out.

By default, memory usage is managed. Use manage-memory with "off" clause to enter unmanaged mode, and "on" clause to revert to managed mode. Here is an example of unmanaged memory mode:
// Enter unmanaged mode
manage-memory off
new-mem var size 1024
// Exit managed mode
manage-memory on

If you are using unmanaged memory, then you must explicitly manage it by using delete-mem or resize-mem, or use statements that explicitly do that (such as delete-query for instance) - these operations must be performed in unmanaged memory mode as well.
See also
Memory ( delete-mem   manage-memory   memory_handling   new-mem   resize-mem  )  SEE ALL (documentation)
new-fifo

Purpose: Create FIFO list.

new-fifo [ define ] <list>

new-fifo initializes new FIFO <list> (First In First Out). <list> is a pointer to type "vely_fifo", and can be created with optional "define".

FIFO <list> contains data stored on a first-in, first out basis. It is useful for storing temporary information in memory which can be quickly retrieved. Note that a list is accessible to the current process only.

Generally information is stored in a FIFO list, and retrieved (possibly many times) in the same order in which it was stored.

The internal positions for write-fifo and read-fifo actions are separate so you can keep adding data to the list, and obtain it in any number of retrieval passes by using rewind-fifo.
Allocated internals
<list> is allocated memory along with additional internal memory, which can be released if purge-fifo is used with <list> from a previously executed new-fifo.
Examples
new-fifo define nf

See also
FIFO ( new-fifo   purge-fifo   read-fifo   rewind-fifo   write-fifo  )  SEE ALL (documentation)
new-hash

Purpose: Create hash table.

new-hash [ define ] <hash> size <size>

new-hash initializes hash table named <hash>, which is a pointer to type "vely_hash" and can be created with optional "define".  <size> is the number of "buckets" in the hash table. All elements with the same hash code are stored in a linked list within the same bucket. Greater table size usually means less elements per bucket and better performance. However, memory usage grows with a bigger hash table, so its size should be balanced based on the program needs.

Vely uses high-performing FNV1_a hash algorithm. Each element in a bucket list is lightweight, containing pointers to a key, value and next element in the linked list.

Note that a hash table is accessible to the current process only. <size> must be at least 10; if less, it will be set to 10.
Allocated internals
<hash> is allocated memory along with additional internal memory, which can be released if purge-hash is used with <hash> from a previously executed new-hash.
Examples
Create a new hash with 500 buckets:
new-hash define h size 500

See read-hash for more examples.
See also
Hash table ( get-hash   new-hash   purge-hash   read-hash   resize-hash   write-hash  )  SEE ALL (documentation)
new-json

Purpose: Parse JSON text.

new-json [ define ] <json> from <text> \
    [ length <length> ] \
    [ status [ define ] <status> ] \
    [ error-text [ define ] <error text> ] \
    [ error-position [ define ] <error position> ] \
    [ max-hash-size <max-hash-size> ] \
    [ noencode ]

new-json will parse <text> into <json> variable (a pointer to type "vely_json") which can be created with optional "define".

The length of <text> may be specified with "length" clause in <length> variable, or if not, it will be calculated as the string length of <text>.

The "status" clause specifies the return <status>, which is VV_OKAY if successful or VV_ERR_JSON if there is an error. The number variable <error position> in "error-position" clause is the byte position in <text> where error was found, in which case <error text> in "error-text" clause is the error message. Both can be created with optional "define".

See read-json on obtaining values from JSON document.
No copying
String <text> is modified during parsing for performance reasons. It is parceled out and <json> contains pointers to it that hold the actual primitive values (string, numbers, boolean, null). This way no data is ever copied for faster parsing. If you don't wish <text> to be modified, make a copy of it before parsing it (see copy-string). In many cases though, this is not necessary, allowing for better performance.
Hash table
Hash table is used to provide fast access to any value in JSON text, with the number of lookups being close to 1, which means near-direct memory access. By default, the maximum size of a hash table is limited to 10000. If your JSON document has considerably more values than that, use "max-hash-size" clause to specify the maximum hash table size. Hash table mechanism is the same as used internaly in new-hash.
Encoding
"noencode" clause will not encode strings, i.e. convert from JSON Unicode strings to UTF8 (see read-json), nor will it perform any validity checks on strings. This may be useful as a performance boost, however it is not recommended in general.
Limitations
The maximum depth of nested structures in JSON document (i.e. objects and arrays) is 32, and the maximum length of normalized leaf node name is 1024 (see read-json for more on normalized names). There is no limit on document size.

Duplicate normalized names are valid, however, only one of them is reported, and the very last value encountered is actually stored (the others from duplicate names are discarded).
Creating JSON
To create a JSON document, you can use write-string to programmatically construct one - use utf8-json to create JSON-compatible Unicode string values.
Allocated internals
<json> is allocated memory along with additional internal memory, which can be released if delete-json is used with <json> from a previously executed new-json.
Examples
Parse text "json_text1" and store the result in "json_var" variable, get status, error text and the location of error:
...
new-json json_var from json_text1 status define st error-text errt error-position errp
read-json json_var key "glossary"."title" value d

See also
JSON ( delete-json   new-json   read-json  )  SEE ALL (documentation)
new-mem

Purpose: Allocate memory.

new-mem [ define ] <memory> size <size> \
    [ block-size <block size> ] \
    [ init ]

new-mem allocates <memory> of <size> blocks, each block of <block size> bytes as specified in "block-size" clause, thus allocating <size>*<block size> bytes aligned suitably for any pointer type. By default, block size is 1, which means in that case <size> is the number of bytes. <memory> is allocated memory.

When "init" is used, the memory is zero-initialized. If you need the memory initialized, always use "init" as it is generally faster than creating memory and then initializing it (for instance with "memset()").

If "define" is used, variable <memory> is created if it does not exist. The pointer returned is always "void*" and can be used for any purpose; always cast it to your desired type.

If an existing pointer is used (i.e. without "define"), then such pointer can be of any type.
Examples
Allocate memory of 300 bytes, producing a "void *". Then the data is copied into it - note the casting to "char*";
new-mem define mystr size 300
strcpy ((char*)mystr, "Some string");

Initialize memory of 1000 bytes (filled with all zeroes):
new-mem define mymem size 1000 init

Allocate an array of 1000 integers:
int *mymem;
new-mem mymem size 1000 block-size sizeof(int)

Allocate an array of 1000 integers initialized to 0 and then a single element of the array is assigned a value:
int *mymem;
new-mem mymem size 1000 block-size sizeof(int) init
mymem[50] = 23;

The memory can be created as "void *" and then assigned to any type:
new-mem define mymem size 1000 block-size sizeof(int) init
int *newmem = (int*)mymem;

See also
Memory ( delete-mem   manage-memory   memory_handling   new-mem   resize-mem  )  SEE ALL (documentation)
Non request

Non-request source files implement common code used in request-handling .vely files. Their names always start with an underscore ('_').

So for instance, file '_display.vely' would never handle any requests directly - such file may be (for example) implementing common code that displays various HTML statements.

A non-request source file can implement any number of functions which can be named in any fashion. However, Vely will declare a prototype for a function with the same name, saving you to the effort to do that, should you decide to implement it. For example, if you have function "void _display_table()" in file "_display.table.vely":
#include "vely.h"

void _display_table() {
    ...
}

then the prototype for function "void _display_table()" will be automatically generated and you do not have to do it manually. You would implement any other functions related to it in the same source file.

When Vely code is compiled, both request and non-request source code is automatically picked up - all files with .vely extensions are used.
Examples
In the following example, a list on customer names is shown (note functions header() and footer()), and the code is implemented in request source file show.vely:
#include "vely.h"

void show() {
    run-query @db="select name from cust" output name
        header();
        query-result  name
        footer();
    end-query
}

Functions header() and footer() are implemented in non-request source file _display.vely:
#include "vely.h"

void header() {
    @Name:<br/>
}

void footer() {
    @<hr/>
}

Both files (show.vely and _display.vely) will be automatically picked up, compiled and linked.
See also
Requests ( after_request_handler   before_request_handler   building_URL   getting_URL   global_request_data   non_request   normalized_URL   request   request_URL   startup_handler   vely_dispatch_request  )  SEE ALL (documentation)
Normalized URL

A request_URL can be written entirely as a query string. This is normalized URL. In this case, request name is omitted as a path segment and is specified as a "req" input parameter along with all other input parameters. Query string follows the application path.

For instance a URL like:
https://some.web.site/shopping/buy-item?name=ABC&price=600

can be written as:
https://some.web.site/shopping?req=buy_item&name=ABC&price=600

A request name "buy_item" (hyphens get converted to underscores) is now the value of parameter "req", which can be specified anywhere in a query string, i.e. it does not have to be the first parameter.

Note that parameter name "req" has a special meaning only in normalized URLs. You can use it just like any other input parameter otherwise.
See also
Requests ( after_request_handler   before_request_handler   building_URL   getting_URL   global_request_data   non_request   normalized_URL   request   request_URL   startup_handler   vely_dispatch_request  )  SEE ALL (documentation)
on-error

Purpose: Either exit request or continue processing when there is an error in executing a query.

on-error ( exit | continue ) [ @<database> ]

When a database statement (like run-query) fails, either exit request processing if "exit" is used, or continue if "continue" is used. "Exiting" is equivalent to calling report-error with the message containing details about the error. "Continuing" means that your program will continue but you should examine error code (see "error" clause in run-query) and handle any issues.

The default action is "exit". You can switch back and forth between "exit" and "continue". Typically, "exit" is preferable because errors in database SQL code generally mean application issues, i.e. bugs that need fixing, however "continue" may be used when application wants to attempt to recover from errors or perform other actions.

Note that you can override the effect of on-error for a specific query by using "on-error-continue" and "on-error-exit" clauses in run-query.
Database
Optional <database> is specified in "@" clause and is the name of the database_config_file.
Examples
The following will not exit when errors happen but rather continue execution (and you must check every error henceforth):
on-error continue @mydb

See also
Database ( begin-transaction   commit-transaction   current-row   database_config_file   database_queries   delete-query   on-error   prepared_statements   query-result   rollback-transaction   run-query  )  Error handling ( error_code   error_handling   on-error   report-error  )  SEE ALL (documentation)
open-file

Purpose: Open file for reading and writing.

open-file <file> file-id [ define ] <file id> \
    [ new-truncate ] \
    [ status [ define ] <status> ]

Opens <file> for reading and writing and creates an open file variable identified by <file id>, which can be created with optional "define". If you supply your own <file id>, it should be of type "vely_file*".

<file> can be a full path name, or a path relative to the application home directory (see how_vely_works).

You can obtain the status of file opening via optional <status> (in "status" clause), which can be created with optional "define". The <status> is VV_OKAY if file is opened, or VV_ERR_OPEN if could not open file.

If "new-truncate" clause is used, a new file is created if it doesn't exist, or it is truncated if it does.
Examples
// Create new file, or truncate an old file if it exists
open-file "testwrite" file-id define nf new-truncate

// Write 25000 rows
num i;
for (i = 1; i <= 25000; i++) {
    (( define line
    some text in line <<p-out i>>
    )) bytes-written define line_len notrim
    write-file file-id nf from line length line_len
}

// Rewind back to the beginning
file-position set 0 file-id nf

// Read all 25000 rows back, and print them out
for (i = 1; i <= 25000; i++) {
    read-file file-id nf  to define one_item
    p-out one_item
}

// Close the file
close-file file-id nf

See also
Files ( close-file   copy-file   delete-file   file-position   file_storage   file_uploading   lock-file   open-file   read-file   read-line   rename-file   stat-file   temporary_file   uniq-file   unlock-file   write-file  )  SEE ALL (documentation)
out-header

Purpose: Output HTTP header.

out-header default

out-header use \
    [ content-type <content type> ] \
    [ download [ <download> ] ] \
    [ etag [ <etag> ] ] \
    [ file-name <file name> ] \
    [ ( cache-control <cache control> ) | no-cache ] \
    [ status-id <status id> ] \
    [ status-text <status text> ] \
    [ custom <header name>=<header value> [ , ... ] ]

A web page must have an HTTP header output before any other response. Note that while cookies may be set after out-header, they still must be set before outputting the data. If your application uses out-header multiple times, all but the very first one are ignored.

If out-header is not called prior to the actual output, either the header or the output itself (or both) may not be output, and you might get an error. Be sure to always first use out-header prior to any output for a request.

Use out-header for dynamically generated web pages. If you wish to output a file (such as an image or a PDF document), do not use this statement; rather use send-file instead.

The HTTP header is sent back to a client who initiated a request. You can specify any custom headers with "use" clause.
Default header
If "default" clause is in place, a default header is constructed, which uses a status of 200/OK and content type of
text/html;charset=utf-8

and cache control of
Cache-Control:max-age=0, no-cache; Pragma: no-cache

and also sends any cookies produced by set-cookie and delete-cookie. The default header is typical for dynamically generated web pages, and most of the time you would use the default header.
Headers
The following are subclauses that allow setting any custom header:
You can use silent-header before output-header in order to suppress its output.
Examples
To output the default HTTP header for a dynamically generated page (a typical usage):
out-header default

To set a custom header for a web page that changes cache control and adds two new headers:
out-header use content-type "text/html" cache-control "max-age:3600" custom "some_HTTP_option"="value_for_some_HTTP_option", "some_HTTP_option_1"="value_for_some_HTTP_option_1"

See also
Web ( call-web   out-header   send-file   silent-header  )  SEE ALL (documentation)
Output statement

Purpose: Output text.

@<text>

!<verbatim text>

Outputting free form text from Vely code is done by starting the line with "@" or "!". The text is output unencoded to the client.  

With "@" statement, any inline_code executes and any output from those statements is output.

With "!" statement, all text is output verbatim, and any inline code is not executed. This is useful when the text printed out should not be checked for any inline_code (such as << ... >>).

All trailing whitespaces are trimmed from each line. If you need to write trailing whitespaces, with "@" statement you can use p-out as inline_code. Maximum line length is 8KB - this is the source code line length, the actual run-time output length is unlimited.

Note that all characters are output as they are written, including the escape character (\). If you wish to output characters requiring an escape character, such as new line and tab (as is done in C by using \n, \t etc.), use p-out as inline_code.
Examples
Outputting "Hello there" from Vely code:
@Hello there

You can use other Vely statements inlined and mixed with the text you are outputting:
char *weatherType="sunny";
@Today's weather is <<p-out weatherType>>

which would output
Today's weather is sunny

With "!" statement, the text is also output, and this example produces the same "Hello there" output as "@":
!Hello there

In contrast to "@" statement, "!" statement outputs all texts verbatim  and does not execute any inline code:
char *weatherType="sunny";
!Today's weather is <<p-out weatherType>>

which would output
Today's weather is <<p-out weatherType>>

See also
Output ( finish-output   flush-output   output_statement   p-dbl   pf-out   pf-url   pf-web   p-num   p-out   p-path   p-url   p-web  )  SEE ALL (documentation)
p-dbl

Purpose: Outputs a floating point number.

p-dbl <double number>

p-dbl outputs a floating point number expression given by <double number>. The output is sent to a client that made the request. If this is within write-string, then <double number> is output into the buffer that builds a new string.

Note that number must be of "dbl" type (which is "double" C type).
Examples
To output a number to a client:
dbl x = 100.51;
p-dbl x

See also
Output ( finish-output   flush-output   output_statement   p-dbl   pf-out   pf-url   pf-web   p-num   p-out   p-path   p-url   p-web  )  SEE ALL (documentation)
pf-out

Purpose: Outputs a formatted string without encoding.

pf-out [ bytes-written [ define ] <bytes> ] [ to-error ] <format> <expression> [ , <expression> ]...

pf-out formats a string according to the <format> string and a list of variable-type <expression>s (in the same way C's "printf()" does) and then outputs the result without any encoding (meaning a string is output exactly as it is, and the client may interpret such text in any way it sees fit). The result is sent to a client that made the request. If another string is built by using write-string, then the result is output into the buffer that builds a new string.

<format> string must be present. If you'd like to know the number of bytes written out, use the optional "bytes-written" clause prior to <format>, in which case <bytes> holds the number of bytes written. If this variable is not defined, use "define" clause to create it within the statement. There must be at least one <expression> (it means if you want to print out a simple string literal you still have to use "%s" as format).

If the optional "to-error" clause is used, the output is sent to "stderr", or standard output stream.
Examples
To output data (the string output is "the number is 20" and the number of bytes written is in variable "bwritten"):
pf-out bytes-written define bwritten "%s is %d", "the number", 20

The following is writing to client, outputting text for which the browser will interpret tags "<br/>" and "<hr/>" as a line break and a horizontal line and display them as such:
pf-out "<hr/> %s <br/>", "This is a non-encoded output"

Create a query text string by means of write-string statement:
void get_table_data (const char *table_name, num id_num)
{

   //
   // Construct the run-time text of dynamic SQL
   //
   write-string define qry_txt
   @select * from <<pf-out "%s where id="%lld", table_name, id_num>>
   end-write-string
 
}

See also
Output ( finish-output   flush-output   output_statement   p-dbl   pf-out   pf-url   pf-web   p-num   p-out   p-path   p-url   p-web  )  SEE ALL (documentation)
pf-url

Purpose: Outputs a URL-encoded formatted string.

pf-url [ bytes-written [ define ] <bytes> ] [ to-error ] <format> <expression> [ , <expression> ]...

pf-url is the same as pf-out, except that the output is URL-encoded. This means such output is suited for use in URL parameters.
Examples
Create a URL based on arbitrary strings used as URL parameters - for instance space would be encoded as "%20" in the final output:
@<a href='<<p-path>>/update?val=<<pf-url "Purchased %s for %lld dollars", piece_desc, price>>'>Log transaction</a>

See pf-out for more examples.
See also
Output ( finish-output   flush-output   output_statement   p-dbl   pf-out   pf-url   pf-web   p-num   p-out   p-path   p-url   p-web  )  SEE ALL (documentation)
pf-web

Purpose: Outputs a web-encoded formatted string.

pf-web [ bytes-written [ define ] <bytes> ] [ to-error ] <format> <expression> [ , <expression> ]...

pf-web is the same as pf-out, except that the output is web-encoded (or HTML-encoded). This means such output is suited for use in web pages - the text will be displayed verbatim without HTML-markup being interpreted.
Examples
Display text containing HTML tags without them being rendered in the browser:
pf-web "We use %s markup", "<hr/>"

See pf-out for more examples.
See also
Output ( finish-output   flush-output   output_statement   p-dbl   pf-out   pf-url   pf-web   p-num   p-out   p-path   p-url   p-web  )  SEE ALL (documentation)
Plain C FCGI

Purpose: Building FastCGI programs in C.

See below

Plain C programs (linked with fcgi, the FastCGI library) can run as server applications with vf FastCGI program manager, which is used to start, stop and manage such applications.

Your program should include "fcgi_stdio.h" include file, and handle incoming requests in a loop by accepting them via FCGI_Accept() function, which is provided by fcgi library. Also, your program must handle SIGTERM signal properly and terminate when signalled without disruption to request processing.

An example of a simple program that fulfills these requirements is shown here.

Flags "busy" and "end_program" are used to handle a termination signal. "busy" is set to 1 while in the processing request loop; if termination signal happens then, "end_program" will be set to 1 so the program can exit once the request is processed. If termination signal happens during any other time (most likely while waiting for request in FCGI_Accept()), the program will exit right away.
Examples
The code below is a "Hello World" example that runs as a server - save it to file plain_fcgi.c:
#include "fcgi_stdio.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>

static int busy = 0; // busy processing request?
static int end_program = 0; // flag to terminate process

// Handle termination signal by setting flag
void signal_handler(int sig) {
    if (sig == SIGTERM) {
        // if not busy, exit now. If busy, exit when the main loop ends
        if (busy == 0) exit(0);
        end_program = 1;
        return;
    }
}

// Main FCGI process
void main(void) {
    // Set termination signal handler
    struct sigaction psa;
    memset (&psa, 0, sizeof (psa));
    psa.sa_handler = signal_handler;
    sigaction(SIGTERM, &psa, NULL);


    // main FCGI request-processing loop, simply display Hello world page each time
    while (FCGI_Accept() >= 0)   {
        busy = 1; // program busy now

        printf("Content-type: text/html\r\n"
           "\r\n"
           "<title>Hello World!</title>"
           "<h1>Hello there!</h1>");

        busy = 0; // program no longer busy
        // exit graceful at the end of request processing loop
        if (end_program) exit(0);
    }
 }

Initialize application
Before vf can run your program as a server, your application must be initialized - this is done only once:
sudo vf -i -u $USER plain_fcgi

Compile application
To compile and link this C program on any distro other than OpenSUSE:
gcc -o /var/lib/vv/bld/plain_fcgi/plain_fcgi plain_fcgi.c -lfcgi

For OpenSUSE, use the following:
gcc -o /var/lib/vv/bld/plain_fcgi/plain_fcgi plain_fcgi.c -lfcgi -I /usr/include/fastcgi

Start application
To start your server application with 2 parallel workers:
vf -w 2 plain_fcgi

Or if you are using TCP to connect to your application, in this case TCP port 2300:
vf -w 2 -p 2300 plain_fcgi

Configure web server (reverse proxy)
To setup a web server to use your application, your must set it up - see application_setup. Make sure to replace <app name> with the actual application name, in this case "plain_fcgi".
Run application
To see your application running, enter this in your browser, assuming your web server is accessible as localhost, i.e. 127.0.0.1 (otherwise replace 127.0.0.1 with your server hostname):
http://127.0.0.1/plain_fcgi

If you wish to stop the FastCGI server and your application:
vf -m quit plain_fcgi

Whenever you recompile your C program, vf will automatically reload it. For more information on all the options available, see vf.
See also
Running application ( application_setup   CGI   Client_API   command_line   containerize_application   FastCGI   FastCGI_client   plain_C_FCGI  )  SEE ALL (documentation)
p-num

Purpose: Outputs a number.

p-num <number>

p-num outputs a number expression given by <number>. The output is sent to a client that made the request. If this is within write-string, then <number> is output into the buffer that builds a new string.

Note that number must be of "num" type (which is "long long" C type).
Examples
To output a number to a client:
num x = 100;
p-num x

See also
Output ( finish-output   flush-output   output_statement   p-dbl   pf-out   pf-url   pf-web   p-num   p-out   p-path   p-url   p-web  )  SEE ALL (documentation)
p-out

Purpose: Outputs a string without encoding.

p-out <string>

p-out outputs a string expression given by <string>, without any encoding (meaning a string is output exactly as it appears). The string is sent to a client that made the request. If this is within write-string, then <string> is output into the buffer that builds a new string.
Examples
To output data verbatim to a client:
char *mydata="Hello world";
p-out mydata

Writing to client, outputting text followed by a horizontal rule - the text is output to a client (such as browser) as it is, and the browser will interpret tags "<br/>" and "<hr/>" as a line break and a horizonal line and display them as such:
p-out "This is a non-encoded output<br/>"
p-out "<hr/>"

Create a query text string by means of write-string statement:
void get_table_data (const char *table_name)
{
   //
   // Construct the run-time text of dynamic SQL
   //
   write-string define qry_txt
   @select * from <<p-out table_name>>
   end-write-string
}

See also
Output ( finish-output   flush-output   output_statement   p-dbl   pf-out   pf-url   pf-web   p-num   p-out   p-path   p-url   p-web  )  SEE ALL (documentation)
p-path

Purpose: Outputs URL application path.

p-path

p-path outputs a URL application path (see request_URL), i.e. the leading path segment(s) prior to request name.

If no "--path" option in vv is used to specify URL application path, then it is the same as application name prepended with a forward slash:
/<app name>

p-path provides the leading part of a URL path after which request name and its parameters can be specified. It is used in HTML forms and URLs (either for HTML or API) to refer back to the same application.
Examples
Create URL in web link:
@<a href="<<p-path>>/add-note">Add Note</a>

In HTML forms:
...
<form action="<<p-path>>/add-note" method="POST" enctype="multipart/form-data">
...

See also
Output ( finish-output   flush-output   output_statement   p-dbl   pf-out   pf-url   pf-web   p-num   p-out   p-path   p-url   p-web  )  SEE ALL (documentation)
Prepared statements

Prepared queries
A prepared query differs only in that it is natively prepared for the database you are using. It is pre-compiled and (among other things) its execution plan is created once, instead of each time a query executes. The statement is cached going forward for the life of the process (with the rare exception of re-establishing a lost database connection). It means effectively an unlimited number of requests will be reusing the query statement.

In practicality it means the execution of a query may be faster due to being prepared only once and executed many times.

Note that databases do not allow prepared queries for DDL (Data Definition Language), as there is not much benefit in general, hence only DML queries (such as INSERT, DELETE etc.) and SELECT can be prepared.
Caching of queries
In order to cache a query statement, Vely will save query text that actually executes the very first time it runs. Then, regardless of what query text you supply in the following executions, it will not mutate anymore. It means from that moment onward, the query will always execute that very same query text, just with different input parameters.

In general, majority of queries are static and not dynamic, so this works just fine. When you use prepared statements with dynamic queries though, you should be aware that query text is immutable. For instance, code in a request that is called many times may look like:
char *qry;
static char is_query_built = 0;

if (!is_query_built) {
    qry = create_query();
    is_query_built = 1;
}

run-prepared-query @mydb = qry output col1, col2, col3 : inp1, inp2
end-query

In the above example, query text "qry" is built only once (by using static boolean "is_query_built"), because the actual query text is needed only for the very first execution of query "myquery", even when connection is lost and re-established. This improves performance beyond what prepared statement may already do. You can, of course, build it every time (i.e. not use the boolean or a similar mechanism), but the query text that actually executes will not change after the very first run. The input parameter variables (such as "inp1" and "inp2" in this example) may change, of course.

In a majority of code, a query may be static as in:
run-prepared-query @mydb = "select col1 from test where someID='%s'" output col1 : id_value
end-query

then clearly the query text is a string literal and will never change. All that changes is the input parameters (in this case "id_value"), and of course the output (in this case "col1"). Most queries are like this.
SQL injections
Note that regardless of whether you use prepared statements or not, the execution of your queries is guarded against SQL injections:
Performance
In most cases, prepared statement will exhibit better performance, and this is particularly true in Vely, where Vely FCGI processes (see vely_architecture and how_vely_works) keep a single database connection (and thus a single session) open for the life of the process (re-creating it only when the connection is lost). Because of this, a prepared statement is done so once; and then reused and re-run many times over afterwards.

In some cases, you might not want to use prepared statements. Some reasons may be:
PostgreSQL
You may get an error like:
could not determine data type of parameter $N

when preparing statements for PostgreSQL. This is an issue with Postgres server, and has nothing to do with Vely, for example in statement:
select col1 from test where someId>='%s' and col1 like concat( '%s' ,'%')

you might get an error "could not determine data type of parameter $2". An issue like this may be that Postgres cannot determine the type, or it may be a bug in Postgres; regardless, this is not a Vely issue. In this case $2 is the second '%s' input parameters and you should specify the type manually for Postgres, generally in form of
...  '%s'::<type> ...

In this case, the type in question is "text", so your statement would be:
select col1 from test where someId>='%s' and col1 like concat( '%s'::text ,'%')

This solution generally works for any Postgres client, not just Vely, regardless of how is the positional input parameter specified.
See also
Database ( begin-transaction   commit-transaction   current-row   database_config_file   database_queries   delete-query   on-error   prepared_statements   query-result   rollback-transaction   run-query  )  SEE ALL (documentation)
purge-fifo

Purpose: Delete FIFO list data.

purge-fifo <list> [ delete ]

purge-fifo will delete all elements from the FIFO <list>, created by new-fifo. The list is then empty and you can still put data into it, and get data from it afterwards, without having to call new-fifo again.

If you use "delete" clause, then all the internal memory of a <list> is freed, and you must call new-fifo in order to use it again.

Note that in any case, purge-fifo will not free any possibly allocated memory for keys or values stored in its elements (see write-fifo).

See memory_handling for more on when (not) to delete memory explicitly like this; the same rules apply as for delete-mem.
Examples
See read-fifo.
See also
FIFO ( new-fifo   purge-fifo   read-fifo   rewind-fifo   write-fifo  )  SEE ALL (documentation)
purge-hash

Purpose: Purge hash table.

purge-hash <hash> [ delete ]

purge-hash deletes all elements from <hash> table that was created with new-hash. Note that it does not free any possibly allocated memory for keys or values (see write-hash).

After purge-hash, the hash is empty and you can use it without calling new-hash again. Note however, that "average-reads" statistics (see get-hash) is not reset - it keeps being computed and remains for the life of the hash.

If you use "delete" clause, then all the internal memory of a <hash> is freed, and you must call new-hash in order to use it again; in this case all statistics are reset.

See memory_handling for more on when (not) to delete memory explicitly like this; the same rules apply as for delete-mem.
Examples
Create hash, put some data in it and then delete the data:
new-hash h size 100
write-hash h key "mykey" value "myvalue"
purge-hash h

See read-hash for more examples.
See also
Hash table ( get-hash   new-hash   purge-hash   read-hash   resize-hash   write-hash  )  SEE ALL (documentation)
p-url

Purpose: Outputs a URL-encoded string.

p-url <string>

p-url is the same as p-out, except that the output is URL-encoded. This means such output is suited for use in URL parameters.
Examples
Create a URL based on arbitrary strings used as URL parameters - for instance a space would be encoded as "%20" in the final output:
@<a href='<<p-path>>/update?item=<<p-url item_name>>'>Update</a>

See p-out for more examples.
See also
Output ( finish-output   flush-output   output_statement   p-dbl   pf-out   pf-url   pf-web   p-num   p-out   p-path   p-url   p-web  )  SEE ALL (documentation)
p-web

Purpose: Outputs a web-encoded string.

p-web <string>

p-web is the same as p-out, except that the output is web-encoded (or HTML-encoded). This means such output is suited for use in web pages - the text will be displayed verbatim without HTML-markup being interpreted.
Examples
Display "We use <hr/> markup" text, without "<hr/>" being displayed as a horizontal line:
p-web "We use <hr/> markup"

See p-out for more examples.
See also
Output ( finish-output   flush-output   output_statement   p-dbl   pf-out   pf-url   pf-web   p-num   p-out   p-path   p-url   p-web  )  SEE ALL (documentation)
Quality control

Quality control
Each candidate for a Vely release must build successfully for a given Linux distribution and on its native architecture, without simulators or cross-compilers. A suite of functional tests (including bug fixes to prevent regressions) must run without errors; currently, there are 1543 such tests.

Vely is tested on following distros: archlinux_rolling, debian_10, debian_11, fedora_37, fedora_38, mageia_8, opensuse_tumbleweed, redhat_8, redhat_9, ubuntu_18, ubuntu_20, ubuntu_22, windows_11 however it may work on other distros derived from this list (see installation page). Both builds and tests are automated to prevent gaps in coverage.

Valgrind and Google Address Sanitizer (ASAN) are used to help eliminate memory violations and leaks.

Vely documentation is automated to a good degree, with internal/external link checking, HTML/troff make and reference/index building.
See also
General ( deploying_application   how_vely_works   quality_control   rename_files   SELinux   vely   vely_architecture   vely_removal   vf   vv   why_C_and_Vely  )  SEE ALL (documentation)
query-result

Purpose: Get query results.

query-result <column name> \
    [ ( to [ define ] <result> ) \
      | ( urlencode | noencode | webencode ) ] \
    [ length [ define ] <length> ]

Use query-result to obtain query results. An instance of query-result obtains a string value of a single query column named <column name>. Note that <column name> can be a computed column, and is not necessarily the same as a table column.

If query-result is used without "to" clause, then the result is printed out. If "to" clause is used, then the result is not printed out, rather it is stored into a string variable <result>, which can be created with optional "define" clause. <result> is allocated memory.

Without "to" clause, the result can be either not encoded (if "noencode" clause is used), URL encoded (if "urlencode" is used) or web (HTML) encoded (if "webencode" is used) - by default, webencode is used. With "to" clause (i.e. if the result is stored in a variable <result>), then no encoding takes place (you can then use encode-url or encode-web to convert it as desired).

query-result is often used as inline_code, especially when output.

A NULL value is always represented as an empty string ("").

Since queries can be nested, any result must always be used directly under the inner-most run-query, to which it refers.

Optional "length" clause places the binary length of a result into number variable <length>, with optional "define". Note the length is the same regardless of encoding and always represents the number of bytes used for data, not counting any trailing null byte for strings. In case of binary data, <length> is the actual number of bytes used by such data, regardless of if (and where and how) is such binary data represented in string form.
Examples
Display table columns in an HTML table:
@<table>
run-query @mydb="select firstName, lastName from employee" output firstName, lastName
@   <tr>
@       <td>
           query-result firstName
@       </td>
@       <td>
           query-result lastName length define lastName_len
@          (length of last name is <<p-num lastName_len>>)
@       </td>
@   </tr>
end-query
@</table>

Using inline_code query-result:
run-query @mydb="select fileName, description, fileSize  from files where fileID='%s'" output fileName, description, fileSize : file_id
    // ask user if sure to delete
    @Are you sure you want to delete file <<query-result fileName>> (<<query-result description>>) of size <<query-result fileSize>>? Click <a href="<<p-path>>/delete_file?action=delete&amp;file_id=<<p-url file_id>>">Delete</a> or click the browser's Back button to go back.<br/>
end-query


Nested queries and usage of define clause:
run-query @mydb="select id from user" output id
   query-result id to define id
   run-query @mydb="select timeout from settings where id='%s'" output timeout : id
       query-result timeout
   end-query
end-query

Obtain query results first in variables and then use them to build HTML output:
run-query @mydb="select firstName, lastName from employee" output firstName, lastName
   query-result firstName to define first_name
   query-result lastName to define last_name
   @Employee (
   p-web first_name
   @,
   p-web last_name
   @) found<br/>
end-query

See also
Database ( begin-transaction   commit-transaction   current-row   database_config_file   database_queries   delete-query   on-error   prepared_statements   query-result   rollback-transaction   run-query  )  SEE ALL (documentation)
random-crypto

Purpose: Obtain a random string for cryptographic use.

random-crypto to [ define ] <random string> \
    [ length <string length> ]

random-crypto obtains a random string of length <string length>. This statement uses a cryptographically secure pseudo random generator (CSPRNG) from OpenSSL library.  If "length" clause is omitted, the length is 20 by default. You can create <random string> with "define" clause. <random string> is allocated memory.

The value generated is always binary and may contain null-characters and is null-terminated.

Use this statement only when needed for specific cryptographic uses. In all other cases, use random-string which is 2-3 times faster.
Examples
Get a 20-digit long random binary value:
random-crypto to define str length 20

See also
Encryption ( decrypt-data   derive-key   encrypt-data   hash-string   random-crypto   random-string  )  SEE ALL (documentation)
random-string

Purpose: Obtain a random string.

random-string to [ define ] <random string> \
    [ length <string length> ] \
    [ number | binary ]

random-string obtains a random string of length <string length>. If "length" clause is omitted, the length is 20 by default. You can create <random string> with "define" clause. <random string> is allocated memory.

If "number" clause is used, then the resulting string is composed of digits only ("0" through "9").

If "binary" clause is used, then the resulting string is binary, i.e. each byte can have an unsigned value of 0-255.

By default, if neither "number" or "binary" is used, the resulting string is alphanumeric, i.e. digits ("0" through "9") and alphabet letters ("a"-"z" and "A"-"Z") are used only.

Random generator is based on the Linux random() generator seeded by local process properties such as its PID and time. A single process is seeded once, and thus any number of requests served by the same process will use a subset of the process' random sequence. Due to joint entropy, each result given to any request is random, not just within a single request, but among any number of different requests.

Note that in any case, the random string is null-terminated.
Examples
Get a 100-digit long random value (as an alphanumeric string):
random-string to define str length 100
pf-out "%s\n", str

Get a random number of length 10 in string representation:
random-string to define str length 10 number
pf-out "%s\n", str

Get a random binary value that is 8 bytes in length - this value may contain null bytes (i.e. it will contain bytes with values ranging from 0 to 255) and will also be null-terminated regardless:
random-string to define str length 8 binary

See also
Encryption ( decrypt-data   derive-key   encrypt-data   hash-string   random-crypto   random-string  )  SEE ALL (documentation)
read-fifo

Purpose: Reads key/value pair from a FIFO list.

read-fifo <list> \
    key [ define ] <key> \
    value [ define ] <value>

read-fifo retrieves an element from the FIFO <list>, storing it into <key> pointer (in "key" clause) and <value> pointer (in "value" clause), both of which can be created as strings with optional "define".

Once an element has been retrieved, the next use of read-fifo will obtain the following one, in the same order they were put in. If the obtained <key> is NULL, the end of the list was reached. read-fifo starts with the first element put in, and moves forward from there, unless rewind-fifo is called, which positions back to the first one.
Examples
In this example, a FIFO list is created, and two key/value pairs added. They are then retrieved in a loop and printed out (twice with rewind), and then the list is purged:
// Create a list
new-fifo mylist

// Add data to the list
write-fifo mylist key "key1" value "value1"
write-fifo mylist key "some2" value "other2"

while (1) {
    // Get data from the list
    read-fifo mylist key define k value define v

    // Check if no more data
    if (k == NULL) {
        break;
    }

    @Obtained key <<p-out k>> with value <<p-out v>>
}

// Go through the list again, use rewind-fifo for that
rewind-fifo mylist

while (1) {
    read-fifo mylist key define k value define v
    if (k == NULL) {
        break;
    }
    @Again obtained key <<p-out k>> with value <<p-out v>>
}

purge-fifo mylist

See also
FIFO ( new-fifo   purge-fifo   read-fifo   rewind-fifo   write-fifo  )  SEE ALL (documentation)
read-file

Purpose: Read file into a string variable.

read-file <file> | ( file-id <file id> ) \
    to [ define ] <content> \
    [ position <position> ] \
    [ length <length> ] \
    [ status [ define ] <status> ]

Without file-id
This is a simple method of reading a file. File named <file> is opened, data read, and file is closed.

<file> can be a full path name, or a path relative to the application home directory (see how_vely_works).

Data read is stored into <content>, which can be created with optional "define". <content> is allocated memory. Note that file can be binary or text and <content> may have null-bytes. Regardless, a null-byte is always placed after the data read.

If "position" and "length" clauses are not specified, read-file reads the entire <file> into <content>, which can be created with optional "define".

If "position" clause is used, then reading starts at byte <position>, otherwise it starts at the beginning of the file. Position of zero (0) represents the beginning of the file.

If "length" clause is used, then <length> number of bytes is read, otherwise the rest of the file is read. If <length>  is 0, <content> is empty string and <status> is 0.

If "status" clause is used, then the number of bytes read is stored to <status>, unless error occurred, in which case <status> has the error code. The error code can be VV_ERR_POSITION (if <position> is negative, outside the file, or file does not support it), VV_ERR_READ (if <length> is negative or there is an error reading file) or VV_ERR_OPEN if file is not open.
With file-id
This method uses a <file id> that was created with open-file. You can then read (and write) file using this <file id> and the file stays open until close-file is called, or the request ends (i.e. Vely will automatically close any such open files).

Data read is stored into <content>, which can be created with optional "define". <content> is allocated memory. Note that file can be binary or text and <content> may have null-bytes. Regardless, a null-byte is always placed after the data read.

If "position" clause is used, then data is read starting from byte <position>, otherwise reading starts from the current file position determined by the previous reads/writes or as set by using "set" clause in file-position. Note that after each read or write, the file position is advanced by the number of bytes read or written.

If "length" clause is used, then <length> number of bytes is read, otherwise the rest of the file is read. If <length>  is 0, <content> is empty string and <status> is 0.

Note that when you reach the end of file and no more bytes can be read, <status> is 0.

If "status" clause is used, then the number of bytes read is stored to <status>, unless error occurred, in which case <status> has the error code. The error code can be VV_ERR_POSITION (if <position> is negative, outside the file, or file does not support it), VV_ERR_READ (if <length> is negative or there is an error reading file) or VV_ERR_OPEN if file is not open.
Examples
To read the entire file and create both the variable that holds its content and the status variable:
read-file "/home/user/some_file" to define file_content status define st
if (st>=0) {
    @Read:
    @<hr/>
    p-web file_content
    @<hr/>
} else {
    @Could not read (<<pf-out "%lld" st>>)
}

"define" in both the content variable and the status are optional, so it could be:
num st;
char *file_content;

read-file "/home/user/some_file" to file_content status st

To read 10 bytes starting at position 20:
read-file "/home/user/some_file" to file_content position 20 length 10

See open-file for an example with "file-id" clause.
See also
Files ( close-file   copy-file   delete-file   file-position   file_storage   file_uploading   lock-file   open-file   read-file   read-line   rename-file   stat-file   temporary_file   uniq-file   unlock-file   write-file  )  SEE ALL (documentation)
read-hash

Purpose: Get data from hash table.

// Random access to hash data:

read-hash <hash> \
    key <key> \
    value [ define ] <value> \
    [ delete [ <delete> ] ] \
    [ status [ define ] <status> ] \
    [ old-key [ define ] <old key> ]

// Sequential access to hash data:

read-hash <hash> traverse begin

read-hash <hash> traverse \
    key [ define ] <key> \
    value [ define ] <value>

Without "traverse" clause
read-hash will obtain an element from <hash>, which is a <value> pointer (in "value" clause) based on a <key> (in "key" clause). <hash> was created by new-hash. <value> can be created as a string with optional "define", but in general can be a pointer of any type. An optional "old-key" clause will obtain the pointer to a key used in a previous write-hash statement; it can be created with an optional "define".

You can also delete an element from the hash by using optional "delete" clause - the <value> is still obtained though it is no longer in the hash table. The hash element is deleted if "delete" clause is used without optional boolean expression <delete>, or if <delete> evaluates to true. Note that "delete" clause will not free any possibly allocated memory for either key or value stored in the element (see write-hash).

If no <key> was found in the hash table, optional <status> (in "status" clause) is VV_ERR_EXIST and <value> is NULL, otherwise it is VV_OKAY. A number <status> can be created with optional "define".

Note that while <key> and <old key> will contain matching strings, the <old key> will contain a pointer to a key used in a prior write-string statement, which may be different than <key>.
With "traverse" clause
read-hash with "traverse" clause obtains <key> and <value> of the current element, and then positions to the next one. Use "begin" clause to position at the very first element. This is useful if you wish to get all the key/value pairs from a hash table - note they are not extracted in any particular order. When there are no more elements, <key> is NULL.

You may search, add or delete elements while traversing a hash table, and this will be reflected in all elements not yet traversed.
Examples
In this example, new hash is created, a key/value pair is written to it, and then the value is obtained and the element deleted; return status is checked:
// Create new hash
new-hash h size 300

// Write to hash
write-hash h key "X0029" value "some data"

// Read from hash
read-hash h key "X0029" value define res status define f delete
if (f == VV_ERR_EXIST) {
    @No data in hash!
} else {
    @Deleted value is <<p-out res>>
}

The following will traverse the entire hash and display all the data:
// Position at the beginning of hash table
read-hash h traverse begin
while (1) {
    // Get elements, one by one, until NULL returned as a key
    read-hash h traverse key define k value define r
    if (k == NULL) break;
    pf-out "Key [%s] data [%s]\n", k, r
}

See also
Hash table ( get-hash   new-hash   purge-hash   read-hash   resize-hash   write-hash  )  SEE ALL (documentation)
read-json

Purpose: Parse JSON text and get values.

// Random access to JSON data:

read-json <json> key <key> \
    value [ define ] <value> \
    [ status [ define ] <status> ]
    [ type [ define ] <type> ]

// Sequential access to JSON data:

read-json <json> traverse begin

read-json <json> traverse \
    key [ define ] <key> \
    value [ define ] <value> \
    [ status [ define ] <status> ] \
    [ type [ define ] <type> ]

read-json will obtain the values from <json> variable that was produced by parsing JSON text with new-json.
Without "traverse" clause
key/value pairs are accessed with "key" clause specifying a normalized key name, which is the value's name preceded with the names of all objects and array members leading up to it, separated by a comma, with each name quoted. The actual <value> is obtained with "value" clause, and the <type> of value can be obtained with optional "type" clause.

The <status> variable in "status" clause is either VV_OKAY on success (meaning value is obtained) or VV_ERR_EXIST if not found. The value is stored in string variable <value> specified in "value" clause.  

<value>, <type> and <status> can be created with optional "define".

Optional number <type> in "type" clause will contain the type of data, which can be VV_JSON_TYPE_STRING, VV_JSON_TYPE_NUMBER, VV_JSON_TYPE_REAL, VV_JSON_TYPE_BOOL or VV_JSON_TYPE_NULL  for a string, integer, decimal, boolean and null value respectively.
With "traverse" clause
This allows sequential access to JSON values. No key is specified, rather key/value pairs are obtained in sequence from beginning to the end. <key> can be created with optional "define". The other clauses used are the same as without "traverse".
Random access via built-in hash
You can access randomly any number, string, boolean or null value within a JSON document without having to create a matching structure in your program. Internally, Vely creates a hash table for fast and direct access to any key/value pair (see new-json).
Keys (normalized names)
<key> in "key" clause is a normalized name of any given leaf node in JSON text. This means every non-leaf node is included (such as arrays and objects), separated by a dot ("."), and arrays are indexed with "[]". An example would be:
"menu"."popup"."menuitem"[1]."onclick"

For instance, the above is a normalized name in this JSON text with the value of "OpenDoc()":
{"menu": {
  "id": "file",
  "value": "File",
  "popup": {
    "menuitem": [
      {"value": "New", "onclick": "CreateNewDoc()"},
      {"value": "Open", "onclick": "OpenDoc()"},
      {"value": "Close", "onclick": "CloseDoc()"}
    ]
  }
}

the normalized names for all leaf nodes are:
"menu"."id"
"menu"."value"
"menu"."popup"."menuitem"[0]."value"
"menu"."popup"."menuitem"[0]."onclick"
"menu"."popup"."menuitem"[1]."value"
"menu"."popup"."menuitem"[1]."onclick"
"menu"."popup"."menuitem"[2]."value"
"menu"."popup"."menuitem"[2]."onclick"

The code to parse this JSON text might then look like:
// Json text
char json_text[] =\
"{\"menu\":\
{\"id\": \"file\",\
\"value\": \"File\",\
\"popup\":\
    {\"menuitem\":\
        [{\"value\": \"New\", \"onclick\": \"CreateNewDoc()\"},\
        {\"value\": \"Open\", \"onclick\": \"OpenDoc()\"},\
        {\"value\": \"Close\", \"onclick\": \"CloseDoc()\"}\
        ]\
    }\
}\
}\n";

// Parse json text
new-json define json_var from json_text status define st error-text define errt error-position define errp
if (st != VV_OKAY) {
    @Could not parse json, error <<p-out errt>> at position <<p-num errp>>
} else {
    @Json parsed okay.
}

// Get value based on key
read-json json_var key "menu"."popup"."menuitem"[1]."onclick" value define val status st
if (st != VV_OKAY) {
    @Could not find json key
} else {
    @Key value is <<p-out val>>
}

The result is:
Json parsed okay.
Key value is OpenDoc()

Values
All values are always returned as strings. This is because in many cases that's how they are used in an application and converting them to other types (like numbers) and back again would affect performance.

Vely checks type validity - for example an invalid integer or decimal number will produce an error. If you need to convert a value to a number you can use C's library functions like atoll() or atof().

String values are returned encoded as UTF8, and any escaped characters (like \n or \t) are converted to their binary equivalent (use "noencode" in new-json to skip this). Such encoded strings can be output anywhere, from a terminal to a client (like browser).
Walk through JSON document
Use "traverse" clause to access JSON nodes sequentially, one by one, from the beginning to the end. Use "traverse begin" to rewind to the beginning, and then read data using "traverse" with "key" clause.

The following code loops through all the leaf nodes of a JSON document - you can also use it to examine a JSON document with unknown structure:
// Parse JSON
new-json define jv from json_text

// Position at the beginning of the JSON document
read-json jv traverse begin

// Loop through all JSON leaf nodes, obtain normalized name, type and value
while (1)
{
    // Get nodes' key, value and type. Check status for end of loop detection.
    read-json jv traverse key define k value define v type define t status define s

    if (s != VV_OKAY) break; // end of loop

    // Output name, value and type
    pf-out "Name [%s], value [%s], type [%lld]\n", k, v, t
}

Examples
read-json jv key "glossary"."title" value d

See also
JSON ( delete-json   new-json   read-json  )  SEE ALL (documentation)
read-line

Purpose: Read text file line by line in a loop.

read-line <file> to [ define ] <line content> [ status [ define ] <length/status> ] [ delimiter <delimiter> ]

<any code>

end-read-line

read-line starts the loop in which a text <file> is read line by line into <line content> (which can be created with "define" if it doesn't exist), with end-read-line ending this loop. Once the end of file has been reached, or an error occurs, the loop exits.

The length of a text line can be obtained with optional <length/status>, which will be VV_ERR_READ if there is an error in reading file, or VV_ERR_OPEN if file cannot be opened, or VV_OKAY if end-of-file has been reached.

Check <length/status> variable within the loop to obtain the length of the currently read-in line, or after the loop for successful completion or an error condition.

Buffer allocated for each line read cannot be used in other iterations (i.e. when other lines are read) or outside the code between read-line and end-read-line. For that reason, if you want to save a read-in line for use elsewhere, save a copy of it using copy-string.

<delimiter> separates the lines in the file, and is by default new line, however it can be any character (note that it is not a string, but a single character).

Each line is null-terminated and new line (or a <delimiter>) remains if it was present (last line may not have it). There is no limit on line length, and Vely will automatically adjust the size of <line content> to accommodate the line length.

<file> can be a full path name, or a path relative to the application home directory (see vv).

Use standard C break and continue statements to exit and continue the loop.
Examples
To read a text file line by line, and display as a web page with line breaks:
read-line "/home/user/dir/file" to define one_line status define len
    @Line length is <<p-num len>>, line is <<p-web one_line>><br/>
end-read-line

To read a text file delimited by "|" character:
read-line "/home/user/dir/file" to define one_line status define len delimiter '|'
...

See also
Files ( close-file   copy-file   delete-file   file-position   file_storage   file_uploading   lock-file   open-file   read-file   read-line   rename-file   stat-file   temporary_file   uniq-file   unlock-file   write-file  )  SEE ALL (documentation)
rename-file

Purpose: Renames a file.

rename-file <from file> to <to file> [ status [ define ] <status> ]

rename-file will rename <from file> to <to file>. Optional <status> variable is VV_OKAY on success and VV_ERR_RENAME on failure, and can be created with optional "define".

<from file> and <to file> must be specified with full paths unless they are in the current working directory (see vv), in which case a name alone will suffice, and can be in different directories.
Examples
Rename files:
rename-file "/home/u1/d1/f1" to "/home/u1/d2/f2" status define st
if (st == VV_OKAY)  {
    @Rename successful. <br/>
}

Rename files in the current working directory:
rename-file "f1" to "f2" status define st
if (st == VV_OKAY)  {
    @Rename successful. <br/>
}

See also
Files ( close-file   copy-file   delete-file   file-position   file_storage   file_uploading   lock-file   open-file   read-file   read-line   rename-file   stat-file   temporary_file   uniq-file   unlock-file   write-file  )  SEE ALL (documentation)
Rename files

From time to time you may need to rename files within your project.

Vely source code files can be request handlers (with file names that do not start with an underscore) and non_request files (with names that start with an underscore).

A Vely non_request file can be renamed freely. Whatever it's name is, it will be picked up automatically and compiled as a part of the project - so you don't have to worry about file names.

A Vely request file can be renamed as well, and whatever it's name is, it will also be picked up and compiled as a part of the project. You must however, change the name of the handler function within such file to match the name of the file. For example a file "process_request.vely" must have a handler function implemented in it with the signature of "void process_request()". If you rename this file to "my_request.vely" then you must change the handler function to have a signature of "void my_request()".
See also
General ( deploying_application   how_vely_works   quality_control   rename_files   SELinux   vely   vely_architecture   vely_removal   vf   vv   why_C_and_Vely  )  SEE ALL (documentation)
report-error

Purpose: Reports a fatal error.

report-error <format>,  <expression> [ , ... ]

To report a fatal error, and write the relevant description of it in the trace file (see how_vely_works) regardless of whether tracing is enabled or not, use report-error. The error message is output in the same fashion as in pf-out, where <format> (of string type) and <expression>s (of any type) are used as in C's "printf()" function.

Note that you must always have at least one <expression>, even when the entire output is just a string constant, so for example you would write:
report-error "%s", "Bad value for number of processes"

The reason for this is to avoid formatting errors, and to use formatting in a consistent fashion.

See error_handling when report-error is called.
Examples
report-error "Too many input parameters, encountered total of [%lld]", num_count

See also
Error handling ( error_code   error_handling   on-error   report-error  )  SEE ALL (documentation)
request-body

Purpose: Get the body of an HTTP request.

request-body <request body> [ length [ define ] <body length> ]

request-body stores the request body of an HTTP request into string <request body> which can hold text or binary data and which is created for that purpose. In either case, optional "length" clause provides the length in bytes of the body in <body length> variable, which can be created with optional "define".

If the content type of the request is "multipart/form-data", the request body is empty because all the data (including any attached files) can be obtained by using input-param (see file_uploading for files). In all other cases, request body is available.

Typical use of request-body is when some text or binary information is attached to the request, such as JSON for example, though it can be anything else, for example an image, some text, or a PDF document. Usually request body is present for POST, PUT or PATCH requests, but you can also obtain it for GET or DELETE requests, if supplied (for instance identifying a resource may require more information than can fit in a query string), or for any custom request method.
Examples
String variable "reqb" will hold request body of a request and "reqb_length" will be its length in bytes:
request-body reqb length define reqb_length

See also
Request information ( get-req   input-param   request-body   set-input   set-req  )  SEE ALL (documentation)
Request

Vely application runs by processing requests. A request always takes form of an HTTP request, meaning a URL and an optional HTTP request body. This is regardless of whether it's a web or command line application.

A request name is always specified in the URL path. By default a request URL path is comprised of application name and request name, so it may look like:
http://<your website>/<app name>/<request name>?some_param=some_value...

See request_URL for more details on the structure of a URL, and building_URL on using URL in your application.

vely_dispatch_request (a main Vely request dispatcher) uses request name to route the request to the appropriate function that handles it.

This handling is based on the names of .vely files, i.e. source code files that make up your project. The request name always matches the file name that handles it.

So for example, file "get_stock.vely" handles request "get_stock" by implementing a function "void get_stock()" in it. A request that is meant to call this function would have "/get_stock" in its URL path right after the application path (see request_URL). The routing of this request to the namesake function will be done automatically by Vely. For instance, if application name is "stocks" and request name "get_stock", such request would be called by URL like:
http://<your website>/stocks/get_stock?some_param=some_value...

Thus file "get_stock.vely" must implement a function "void get_stock()", and in the code below, it simply outputs text "Hello from get_stock: some_value":
void get_stock()
{
    input-param some_param
    @Hello from get_stock: <<p-out some_param>>
}

If the name of .vely file starts with an underscore ("_"), then it is a non_request file and it will not handle a request. See how_vely_works.

You can use hyphens in URLs and they are converted to underscore by Vely. So for example the above URL could be written with "get-stock" instead of "get_stock":
http://<your website>/stocks/get-stock?some_param=some_value...

A request can have any number of input parameters, and based on them, perform any number of actions. A "request" generally refers to a combination of input parameters. Those requests that share the same request name but have different set of parameter names are its subrequests.
See also
Requests ( after_request_handler   before_request_handler   building_URL   getting_URL   global_request_data   non_request   normalized_URL   request   request_URL   startup_handler   vely_dispatch_request  )  SEE ALL (documentation)
Request URL

Application path
A request URL is a URL that calls your Vely code. The leading part of its path is called "application path" and is determined by the caller; it is usually specified in a web proxy configuration or in an environment for command-line programs.

By default, application path is assumed to be the application name (see how_vely_works), and would be:
/<app name>

So if your application name is "shopping", then the default application path is:
/shopping

You would then specify this application path in the client, for instance:
Request name
Request name is always the first path segment after the application path, for instance:
https://some.web.site/shopping/buy-item

In this case the request name is "buy_item". Note that hyphen ("-") in the URL name is always converted to underscore ("_") for a Vely request name. This is true for all input parameters, see input-param.

A request name can be can be made up of alphanumeric characters, hyphen or underscore only and cannot start with a digit. Often it is in the same form as the names of Vely statement_APIs, which is that of "verb"-"object", for example "add-note", "show-orders" etc. This form clearly communicates the functional purpose of the request in the URL itself, as well as the names of source code files that handle them, which in this case would be "add_note.vely", "show_orders.vely" etc. Note however you can name a request any way you like.
URL payload
The actual URL payload, i.e. the input parameters, follow after the request name. For example, the full URL may be:
https://some.web.site/shopping/buy-item?name=ABC&price=600

Here, the required request name is "buy_item" and there are two other parameters ("name" and "price") with values of "ABC" and "600" respectively (see request).

The input parameters can also be specified as path segments in the form of "/name/value". This is often done when writing a REST API. So for example, the above URL can be:
https://some.web.site/shopping/buy-item/name/ABC?price=600

You can specify some or all parameters as path segments, so for example to specify all of them, the above URL can be written as:
https://some.web.site/api/v2/shopping/buy-item/name/ABC/price/600

The very last path segment can miss a value, and by default it would be "true", so if you add a parameter "edit":
https://some.web.site/api/v2/shopping/buy/name/ABC/price/600/edit

then this would be the same URL as before with the additional parameter "edit" of value "true".

If you specify input parameters as path segments, keep in mind that the path is meant to represent a logical hierarchy, where each path segment precedes in meaning the previous. In other words, input parameters representing a broader logical notion come first in the path, and those with a narrower one come afterwards. This order may represent the resource hierarchy (as it is often done with REST), but it does not have to.
Customizing application path
The application path can be customized to be any path you wish, but it must have the application name as the last segment:
/some/path/<app name>

This helps identify application at a glance. For example, if your application name is "shopping", the application path may be
/api/v2/shopping

and would be specified like this:
For web requests, the URL sent by the caller might look something like this:
https://some.web.site/api/v2/shopping/buy-item?name=ABC&price=600

To tell Vely what the custom application path is, specify it in the "--path" parameter in vv when making your application, in this case:
vv -q --path="/api/v2/shopping"

Parameters
It is up to you how you structure your parameters, i.e. the order in a query path or path segments, and which ones (if any) are in a query string. Regardless of your choices, the code handling a request is the same. In the example used here, you can obtain the parameters in request handler source file "buy_item.vely":
#include "vely.h"

void buy_item() {
    out-header default

    input-param name
    input-param price

    run-query @mydb = "insert into orders (name, price) values ('%s', '%s')" : name, price no-loop
    @OKAY
}

Normalized URL
You can also write URLs where the entire request, including the request name, is in the query string. See normalized_URL.
See also
Requests ( after_request_handler   before_request_handler   building_URL   getting_URL   global_request_data   non_request   normalized_URL   request   request_URL   startup_handler   vely_dispatch_request  )  SEE ALL (documentation)
resize-hash

Purpose: Resize hash table.

resize-hash <hash> size <new size>

resize-hash will resize hash table <hash> (created by new-hash) to size <newsize>, which refers to the number of "buckets", or possible hash codes derived from keys stored.

When a number of elements stored grows, the search performance may decline if hash table size remains the same. Consequently, if the number of elements shrinks, the memory allocated by the hash table may be wasted. Use get-hash to obtain its current size, its length (the number of elements currently stored in it) and the statistics (such as average reads) to determine if you need to resize it.

Resizing is generally expensive, so it should not be done too often. The goal is to amortize this expense through future gain of lookup performance. For that reason it may be better to resize proportionally (i.e. by a percentage), unless you have a specific application reason to do otherwise, or to avoid exponential growth.
Examples
resize-hash h size 100000

See also
Hash table ( get-hash   new-hash   purge-hash   read-hash   resize-hash   write-hash  )  SEE ALL (documentation)
resize-mem

Purpose: Resize memory.

resize-mem <memory> size <size>

resize-mem resize memory <memory> that must have been previously allocated by Vely. Do not use it on memory allocated by any C-library functions (such as malloc(), calloc() or realloc()). If memory cannot be allocated, or if the pointer is not a valid one, the program will error out.

The pointer returned is void* and can be used for any purpose.
Examples
Allocate and resize memory:
new-mem define mystr size 300
resize-mem mystr size 1000

See also
Memory ( delete-mem   manage-memory   memory_handling   new-mem   resize-mem  )  SEE ALL (documentation)
rewind-fifo

Purpose: Rewind FIFO list to the beginning.

rewind-fifo <list>

rewind-fifo will position at the very first data element put into <list> which was created with new-fifo. Each time read-fifo is used, the internal position moves to the next element in the order they were put in. rewind-fifo rewinds back to the very first one.
Examples
See read-fifo.
See also
FIFO ( new-fifo   purge-fifo   read-fifo   rewind-fifo   write-fifo  )  SEE ALL (documentation)
rollback-transaction

Purpose: Rollbacks a SQL transaction.

rollback-transaction [ @<database> ] [ on-error-continue | on-error-exit ] [ error [ define ] <error> ] [ error-text [ define ] <error text> ]

rollback-transaction will roll back a transaction started with begin-transaction.
Database
Optional <database> is specified in "@" clause and is the name of the database_config_file.
Error handling
The error code is available in  <error> variable in optional "error" clause - this code is always "0" if successful. The <error> code may or may not be a number but is always returned as a string value. <error> is allocated memory. In case of error, error text is available in optional "error-text" clause in <error text>, which is allocated memory.

"on-error-continue" clause specifies that request processing will continue in case of error, whereas "on-error-exit" clause specifies that it will exit. This setting overrides database-level on-error for this specific statement only. If you use "on-error-continue", be sure to check the error code.

<error> and <error text> can be created with optional "define".

Note that if database connection was lost, and could not be reestablished, the request will error out (see error_handling).
Examples
begin-transaction @mydb
run-query @mydb="insert into employee (name, dateOfHire) values ('Terry', now())"
run-query @mydb="insert into payroll (name, salary) values ('Terry', 100000)"
rollback-transaction @mydb

See also
Database ( begin-transaction   commit-transaction   current-row   database_config_file   database_queries   delete-query   on-error   prepared_statements   query-result   rollback-transaction   run-query  )  SEE ALL (documentation)
run-query

Purpose: Execute a query and loop through result set.

run-query \
    [ @<database> ] \
    = <query text> \
    [ ( output [ define ] <column name> [ , ... ] ) | unknown-output ] \
    [ no-loop ] \
    [ error [ define ] <error> ] \
    [ error-text [ define ] <error text> ] \
    [ affected-rows [ define ] <affected rows> ] \
    [ row-count [ define ] <row count> ] \
    [ on-error-continue | on-error-exit ] \
    [ name <query name> ] \
    [ column-count [ define ] <column count> ] \
    [ column-names [ define ] <column names> ] \
    [ column-data [ define ] <column data> ] \
    [ : <input parameter> [ , ... ] ]

    <any code>

[ end-query ]

run-prepared-query \
    ... ( the same as run-query ) ...

run-query executes a query specified with <query text>, which is a dynamic query text (i.e. computed at run-time), or it is a constant string value.
Database
Optional <database> is specified in "@" clause and is the name of the database_config_file. It is optional if there is only one database used (see vv), in which case it is the default.
Output
- output clause
The optional "output" clause is a comma-delimited list of the query's output columns. These column names are used in query-result to get the columns values. The column names do not need to match the actual query column names, rather you can name them anyway you want, as long as they positionally correspond.

If optional "define" is used, then string variables with the same name are created for each column and query's output assigned to them, in which case each name must be a valid C identifier name. For example:
run-query @db = "select firstName, lastName from employees" output define first_name, last_name
    ...
end-loop

is the same as:
run-query @db = "select firstName, lastName from employees" output first_name, last_name
    query-result first_name to define first_name
    query-result last_name to define last_name
    ...
end-loop

"define" is useful in "output" clause to quickly and efficiently create query's output variables in very little code.

Note that the result obtained via "define" is always unencoded. If you need different encoding or other details about the result, use query-result.

- unknown-output clause
If for some reason you don't know the number of output columns of the query (for instance in "SELECT * from ..." kind of query), use "unknown-output" clause, in which case you can use "column-data", "column-names" and "column-count" to get the query metadata in order to obtain the results. If you use neither "output" nor "unknown-output" clause, then your query has no output columns, for example it might be an "INSERT" or "DELETE" statement, or a DDL statement like "CREATE TABLE".
Input
The query's input parameters (if any) are specified with '%s' in the <query text> (note that single quotes must be included). The actual input variables are provided after optional semicolon (":"), in a list separated by a comma. Each input variable is a string regardless of the actual column type, as the database engine will interpret the data according to its usage. Each input variable is trimmed (left and right) before used in a query. Each <input parameter> may contain a comma if it is a string (i.e. quoted) or it is an expression within parenthesis.
Looping through data
"end-query" ends the loop in which query results are available (see query-result). The optional "no-loop" clause includes implicit "end-query", and in that case no "end-query" can be used. This is useful if you don't want to access any output columns, but rather only affected rows (in INSERT or UPDATE for example), row count (in SELECT) or error code. "end-query" is also unnecessary for DDL statements like "CREATE INDEX" for instance.
Affected rows
The optional "affected-rows" clause provides the number of <affected rows> (such as number of rows inserted by INSERT). The number of rows affected is typically used for DML operations such as INSERT, UPDATE or DELETE. For SELECT, it may or may not be the same as "row-count" which returns the number of rows from a query. See your database documentation for more.

<affected rows> can be created with optional "define".
Rows returned
The number of rows returned by a query can be obtained in optional <row count> in "row-count" clause. <row count> can be created with optional "define".
Error handling
The error code is available in  <error> variable in optional "error" clause - this code is always "0" if successful. The <error> code may or may not be a number but is always returned as a string value. <error> is allocated memory. In case of error, error text is available in optional "error-text" clause in <error text>, which is allocated memory.

"on-error-continue" clause specifies that request processing will continue in case of error, whereas "on-error-exit" clause specifies that it will exit. This setting overrides database-level on-error for this specific statement only. If you use "on-error-continue", be sure to check the error code.

<error> and <error text> can be created with optional "define".

Note that if database connection was lost, and could not be reestablished, the request will error out (see error_handling).
Naming query
A query can be named with an optional "name" clause by specifying <query name>. By default, a query is assigned a generated name. When a query is named, you can use other statements such as delete-query that reference the name. A query name must be unique and you will receive an error if it is reused with different queries.
run-prepared-query
run-prepared-query is the same as run-query except for a few important differences; see prepared_statements.
Querying when column names are not known
If you do not know the column names (or even how many of them there are), use optional "column-count" clause to obtain the number of columns in <column count>, "column-names" to obtain the list of column names in <column names>, and "column-data" for the actual query results in <column data>. Use optional "define" to create any of them. To get a column count for instance:
run-query @mydb ="select name, lastName yearOfHire from employee" no-loop column-count define col_count
@Number of columns: <<p-num col_count>><br/>

<column names> is an array of strings containing names of query columns. Column names can be obtained only if "unknown-output" is used. If "define" is used, the array of strings is created, otherwise you need to do it yourself, as in:
char **col_names;
run-query @mydb ="select name, lastName yearOfHire from employee" no-loop column-names col_names

Note that column names are the names of query columns, which may or may not match any actual table column names, since query outputs can have aliases (and they must have them if the output is computed). In the following example, the output will be "employeeFirstName" and "employeeLastName" as they are aliases:
run-query @mydb="select firstName employeeFirstName, lastName employeeLastName from employee" unknown-output \
    column-names to define col_names no-loop
@Column names are <<p-web col_names[0]>>  and <<p-web col_names[1]>>

The array of <column names>, as well as each member of this array, are allocated memory.

"column-data" clause will store a query result into an array of strings <column data>. Typical use of column-data is when a query text is constructed on the fly and the exact list of result columns of a query is unknown (see "unknown-output" clause). In that case, when running the query you can obtain the query metadata, such as number of rows (with "row-count" clause), the number of columns (with "column-count") and the output column names (with "column-names"). This way the query result is described and you can interpret the data obtained with "column-data".

"column-data" gets all the query's data laid out in a single data array, organized by repeating rows. For instance, suppose that table queried has 2 columns and the data is stored into "col_data" array. In that case, "col_data[0]" and "col_data[1]" would be the two columns' values from the first row, "col_data[2]" and "col_data[3]" would be the two columns' values from the second row, "col_data[4]" and "col_data[5]" would be the the columns' values from the third row etc. If "define" is used, then the <column data> variable is created. If it's not used, then you have to define the string array yourself, as in:
char **col_data;
run-query @mydb="select firstName employeeFirstName, lastName employeeLastName from employee" unknown-output \
    column-data to col_data

This example obtains the number of rows and columns, as well as column names for a query. In this case the query is "select * from <table name>", where <table name> is provided at run-time - hence you might not know column names of the result set. Based on the number of rows and columns, all data resulting from the query is displayed in a "for" loop:
void get_table_data (const char *table_name)
{
    //
    // Construct the run-time text of SQL
    //
    (( define qry_txt
    @select * from <<p-out table_name>>
    ))

    //
    // Use unknown-output to demonstrate a solution if
    // output columns of a query are difficult to obtain
    //
    //
    // Run the query and obtain number of rows
    // Obtain number of columns, column names, the actual column data
    // Get actual table data
    //
    run-query @mydb = qry_txt unknown-output row-count define row_count no-loop \
        column-count  define col_count column-names define col_names column-data define col-data


    //
    // In a loop, go through all rows, and for each row, display all column info as well
    // as the actual column data from the table.
    //
    num i;
    num j;
    for (j = 0; j <row_count; j++) {
        //
        // Display columns for each row
        //
        for (i = 0; i <col_count; i++) {
            pf-out "colname %s, coldata %s\n", col_names[i], col_data[j*col_count+i]
        }
    }
}

The array of <column data>, as well as each member of this array, are allocated memory.
Notes
"=" and "@" clauses may or may not have a space before the data that follows. So for example, these are both valid:
// No space after "@" and "="
run-query @db ="select firstName, lastName from employee where employeeId='%s'" output firstName, lastName : empid

// Space after "@" and "="
run-query @ db = "select firstName, lastName from employee where employeeId='%s'" output firstName, lastName : empid

Allocated internals
Internal memory used for a query is allocated memory, which can be released with delete-query.
Examples
Select first and last name (output is firstName and lastName) based on employee ID (specified by input parameter empid):
input-param empid
run-query @db = "select firstName, lastName from employee where employeeId='%s'" output firstName, lastName : empid
    @Employee is <<query-result  firstName>> <<query-result  lastName>>
end-query

Prepared query without a loop and obtain error code and affected rows:
run-prepared-query @db = qry no-loop error define ecode affected-rows define arows : stock_name, stock_price, stock_price

When only a single database is used (a single database_config_file for an application), then "@" clause can be omitted:
run-query =myqry no-loop : stock_name, stock_price, stock_price

See also
Database ( begin-transaction   commit-transaction   current-row   database_config_file   database_queries   delete-query   on-error   prepared_statements   query-result   rollback-transaction   run-query  )  SEE ALL (documentation)
SELinux

If you do not use SELinux, you can ignore this.

If you do use SELinux, read this. SELinux is MAC (Mandatory Access Control), which means anything that isn't allowed is prohibited. This is as opposed to DAC, Discretionary Access Control, where everything is allowed except what's prohibited. MAC generally works on top of DAC, and they are expected to work in a complementary fashion. Vely deploys both methods for enhanced security.

Vely comes with a SELinux policy out-of-the-box, which covers its general functioning. However, you can write any code with Vely, and if you are using SELinux, you may run afoul of its other policies, which may not be conducive to your code. In that case, use temporarily a permissive mode (via setenforce), and then audit2allow to get a clue on what is the issue and then take action to allow actions requested.

Note that OpenSUSE package does not come with SELinux policy as of this release, because OpenSUSE at this time does not come with a default base policy and SELinux installation.

General
Vely policy files (.te, .fc files, .if file is empty) can be found here:
ls $(vv -l)/selinux/*.{te,fc}

As a part of installing Vely, the following SELinux types will be installed:
Vely policy:
Vely policy allows normal functioning of Vely features only, but does not introduce any unnecessary privileges to the rest of the system.

Note: Vely installation does not distribute .pp (compile) policy files, because it is not currently part of distro repos (which may change in the future). Due to changes in SELinux and difference in versions installed across derived distros, Vely will compile source .te and .fc files during the installation, ensuring the best possibility of successful SELinux policy setup.
Unix domain sockets
Using Unix domain sockets for Vely FCGI processes to communicate with a web server (see vv) is the default method and no further action is needed.
Unix TCP sockets
Using TCP sockets for Vely FCGI processes to communicate with a web server (see vv) requires you to label such ports as vvport_t, for example if you plan to use port 2109:
sudo semanage port -a -t vvport_t -p tcp  2109

When you no longer need a port, for example if you are switching to another port (for instance 2209), remove the old one and add the new one:
sudo semanage port -d -t vvport_t -p tcp  2109
sudo semanage port -a -t vvport_t -p tcp  2209

Changing or adding directories
If you are adding directories to be used by Vely program, or changing a directory, for example using a different storage instead of /var/lib/vv (see how_vely_works), you need to label files in new directories:
sudo semanage fcontext -a -t vvfile_t "/your/new/dir(/.*)?"
sudo restorecon -R /your/new/dir

To remove context from such directories (if you are not using them anymore), use:
sudo semanage fcontext -d -t vvfile_t "/your/new/dir(/.*)?"
sudo restorecon -R /your/new/dir

See also
General ( deploying_application   how_vely_works   quality_control   rename_files   SELinux   vely   vely_architecture   vely_removal   vf   vv   why_C_and_Vely  )  SEE ALL (documentation)
send-file

Purpose: Send file to client.

send-file <file> [ headers \
    [ content-type <content type> ] \
    [ download [ <download> ] ] \
    [ etag [ <etag> ] ] \
    [ file-name <file name> ] \
    [ ( cache-control <cache control> ) | no-cache ] \
    [ status-id <status id> ] \
    [ status-text <status text> ] \
    [ custom <header name>=<header value> [ , ... ] ]
    ]

When a client requests download of a file, you can use send-file to provide <file>, which is its location on the server, and is either a full path or relative to the application home directory (see vv). Note however that you can never use dot-dot (i.e. "..") in <file> - this is a security measure to avoid path-traversal attacks. Thus the file name should never have ".." in it, and if it does, the program will error out and stop.
Headers
The following are subclauses that allow setting any custom header:
Cookies
Any cookies set prior to send-file (see set-cookie and delete-cookie) will be sent along with the file to the web client.
Examples
To send a document back to the browser and show it, use send-file with the "show" clause:
send-file "/home/vely/files/myfile.jpg" headers content-type "image/jpg"

An example to display a PDF document:
char *pdf_doc="/home/mydir/myfile.pdf";
send-file pdf_doc headers content-type "application/pdf"

If you want to send a file to the client for download (with the dialog), use "download" clause. This way the document is not displayed but the "Save As" (or similar) window shows up, for example to download a "PDF" document:
send-file "/home/user/file.pdf" headers download content-type "application/pdf"

See also
Web ( call-web   out-header   send-file   silent-header  )  SEE ALL (documentation)
set-app

Purpose: Set process data.

set-app process-data <data>

set-app sets information related to an application or a process that runs it. A process may handle one or more requests, hence such data is cross-request and process-wide in scope. Some of this information is used by Vely and some is used by you in your code.

If you wish to set up global_process_data, you can do so by providing <data> in the "process-data" clause, which is a pointer to any type. Internally, this pointer is saved as void*, so it can be cast to anything. <data> can be retrieved later anywhere in any request served by the process with get-app (see "process-data" clause).

Process data has to be unmanaged memory because it is available across requests, see memory_handling.
Examples
To set global process data, for instance in _startup.vely (i.e. in startup_handler):
// Define global process memory pointer
my_type *mydata;

// Create global process memory
manage-memory off
new-mem mydata size sizeof(my_type)
manage-memory on

// Set global process memory
set-app process-data mydata

See global_process_data for more details and an example.
See also
Application information ( get-app   set-app  )  SEE ALL (documentation)
set-cookie

Purpose: Set cookie.

set-cookie <cookie name>=<cookie value> \
    [ expires <expiration> ] \
    [ path <path> ] \
    [ same-site "Lax"|"Strict"|"None" ] \
    [ no-http-only [ <no-http-only> ] ] \
    [ secure [ <secure> ] ]

To set a cookie named <cookie name> to value <cookie value>, use set-cookie statement. A cookie can be set before or after sending out a header (see out-header). However a cookie must be set prior to outputting any actual response (such as with output_statement or p-out for example), or the program will error out and stop.

Cookie's <expiration> date (as a a string, see get-time) is given with "expires" clause. The default is session cookie meaning the cookie expires when client session closes.

Cookie's <path> is specified with "path" clause. The default is the URL path of the request_URL.

Whether a cookie applies to the same site is given with "same-site" clause along with possible values of "Lax", "Strict" or "None".

By default a cookie is not accessible to client scripting (i.e. "HttpOnly") -you can change this with "no-http-only" clause. That will be the case if "no-http-only" clause is used without optional bool expression <no-http-only>, or if <no-http-only> evaluates to true.

Use "secure" if a secure connection (https) is used, in order to specify this cookie is available only with a secure connection. That will be the case if "secure" is used without optional bool expression <secure>, or if <secure> evaluates to true.

Cookies are commonly used for session maintenance, tracking and other purposes. Use get-cookie and delete-cookie together with set-cookie to manage cookies.
Examples
To set a cookie named "my_cookie_name" to value "XYZ", that will go with the reply (back to the client, such as a browser) and expire in 1 year and 2 months from now, use:
get-time to define mytime year 1 month 2
char *my_cookie_value="XYZ";
set-cookie "my_cookie_name"=my_cookie_value expires mytime path "/" same-site "Lax"

A cookie that can be used by JavaScript (meaning we use no-http-only clause):
set-cookie "my_cookie_name"=my_cookie_value no-http-only

See also
Cookies ( delete-cookie   get-cookie   set-cookie  )  SEE ALL (documentation)
set-input

Purpose: Set or overwrite input parameters.

set-input <param name> = <param value>
set-input sets or overwrites an input-parameter. <param name> is a string that holds parameter name and <param value> is a string that holds parameter value.

If an input parameter with <param name> already exists, it's value is overwritten with <param value>.

If an input parameter with <param name> does not exist, a new parameter is created and its value is set to <param value>.

Note that no copy of <param value> is made. If it will go out of scope before it's used, make a copy first (see copy-string).

set-input is useful when input parameters supplied through a request need to be altered or added-to so your code can process them more efficiently, to write less code etc.

Once set-input sets parameter value, using input-param will obtain it as if it was provided through a request URL.

Note that input-param creates a variable, so you cannot use it twice in the same scope. Typically, set-input is used to set input parameter values, while another code block or function will use input-param to obtain those values.
Examples
Set the value of input parameter named "quantity" to "0", which is also the output:
set-input "quantity" = "0"
...
input-param quantity
p-out quantity

Set the value of input parameter named "description" to "not available", which is also the output:
char *name = "description";
set-input name = "not available"
...
input-param description
p-out description

See also
Request information ( get-req   input-param   request-body   set-input   set-req  )  SEE ALL (documentation)
set-req

Purpose: Set request data.

set-req data <data>

set-req sets information used to process a request. Some of this information is used by Vely and some is used by you in your code.

If you wish to set up global_request_data, you can do so by providing <data> in the "data" clause, which is a pointer to any type. Internally, this pointer is saved as void*, so it can be cast to anything. <data> can be retrieved later anywhere during a request with get-req (see "data" clause).
Examples
To set global request data:
my_type *mydata;
...
set-req data mydata

See global_request_data for more details and an example.
See also
Request information ( get-req   input-param   request-body   set-input   set-req  )  SEE ALL (documentation)
silent-header

Purpose: Do not output HTTP headers.

silent-header

silent-header will suppress the output of HTTP headers, such as with out-header, or in any other case where headers are output. The effect applies to current request only; if you use it conditionally, then you can have it on or off dynamically. If you want to suppres the headers across the entire command-line application, use VV_SILENT_HEADER environment variable; see command_line.

Note that you still have to use out-header regardless, as it is expected to be there if you output anything; however its actual output will be suppressed.

There are many uses for silent-header, among them:
Examples
silent-header
out-header

// No headers output 

See also
Web ( call-web   out-header   send-file   silent-header  )  SEE ALL (documentation)
split-string

Purpose: Split a string into pieces based on a delimiter.

split-string \
    ( <string> with <delimiter> to [ define ] <result> \
    | delete <result> )

split-string will find all instances of <delimiter> in <string> and then split it into pieces. The <result> is a pointer to a variable of type "vely_split_str" and it has two members: integer variable "num_pieces" which is the number of pieces <string> was broken into, and string array "pieces", which holds the actual pieces, each null-terminated. If variable <result> does not exist, you can create it with define clause. A delimiter within double quotes ("..") is not counted, i.e. it is skipped.

Note that <string> is altered by placement of null-terminators and will not hold the same value (rather it will hold only the leading portion of what it did before split-string took place). Each element of "pieces" array points to memory occupied by <string>. Hence, split-string does not copy any data and is very fast in performing the kind of parsing described here. You can copy string beforehand if you don't want it altered (see copy-string).

All pieces produced will be trimmed both on left and right. If a piece is double quoted, then double quotes are removed. For instance:
char clist[] = "a , b, \"c , d\" , e"
split-string clist with "," to define res

After this, the variable "res" will be an array of strings with these values:
res->num_pieces is 4
res->pieces[0] points to "a"
res->pieces[1] points to "b"
res->pieces[2] points to "c , d"
res->pieces[3] points to "e"

Also, since <string> is altered, it cannot be a constant - rather it must always be a variable, for example, if you do this with the intention to split this string based on "," as a delimiter:
char *str = "string,to,split";

your program will report an error (SIGSEGV most likely, or segmentation fault). You should do:
char str[] = "string,to,split";

split-string is useful for parsing CSV (Comma Separated Values) or any other kind of separated values, where separator can be any string of any length, for example if you're parsing an encoded URL-string, then "&amp;" may be a separator, as in the example below.
Allocated internals
<result> is allocated memory along with additional internal memory, which can be released if "delete" clause is used on a <result> from a previously executed split-string. See memory_handling for more on when (not) to delete memory explicitly like this; the same rules apply as for delete-mem.
Examples
The following will parse a string containing name/value pairs (such as "name=value") separated by string "&amp;":
// Data to parse - data/value pairs delimited by "&amp;" string, and data and value delimited by equal sign:
char instr[]="x=23&amp;y=good&amp;z=hello_world";

// Split string first into pieces based on "amp;"
split-string instr with "&amp;" to define assignment

// For each of name=value pairs, split it with equal sign
num i;
for (i = 0; i < assignment->num_pieces; i++) {
    split-string assignment->pieces[i] with "=" to define data
    pf-out "Variable %s has value %s\n", data->pieces[0], data->pieces[1]
}

The result is:
Variable x has value 23
Variable y has value good
Variable z has value hello world

See also
Strings ( copy-string   count-substring   lower-string   split-string   trim-string   upper-string   write-string  )  SEE ALL (documentation)
Startup handler

Purpose: Execute your code once before any request handlers do.

void _startup ( ) ...

To specify your code to execute once before any requests are handled, create a source file "_startup.vely" and implement a function "void _startup()", which will be automatically picked up and compiled with your application.

Startup handler will execute just before the first request. It will not execute when the application starts, but when it receives the very first request.

Important: if you need cross-request global variables that would be available for the life of the process, i.e. to any request served by this process, do not use result(s) of any Vely statements, because such memory is released at the end of each request, and thus would become invalid after very first request served by this process. If you must, you can use either global variables or use C's "malloc()" functions. Note however, that this is rarely needed and generally should be avoided.
Examples
Here is a simple implementation of startup handler that just outputs "Hi there!!":
#include <vely.h>

void _startup()
{
    out-header default
    @Hi there!!
}

See also
Requests ( after_request_handler   before_request_handler   building_URL   getting_URL   global_request_data   non_request   normalized_URL   request   request_URL   startup_handler   vely_dispatch_request  )  SEE ALL (documentation)
Statement APIs

Statement APIs, or Vely statements (such as write-file or run-query etc.) are programming statements written within C code in files with .vely extension. They are pre-processed by vv into C code, and then compiled into a native executable. A statement does something useful, for example runs a database query, searches a string with regex, calls a web service, parses JSON, creates a unique file, etc. (see documentation).
Statements vs API
Writing a single Vely statement instead of multiple C API calls is easier to read and maintain, less error prone, safer at run-time and more productive. The functionality, scope and default behavior of each statement are chosen to reflect those benefits for typical practical needs of application development. They are meant to be building blocks that enable productivity and collaboration.

A statement is not a macro or a simple substitution for one or more API calls. A statement can perform multiple functions that are logically connected, but would require use of different API calls and in different configurations to achieve the functionality. For example run-query can perform a simple query with no input or output parameters, or a complex one with them, and with or without a number of other options. Achieving these functionalities with APIs would require different ones used in different ways, increasing chances of human error. Note that sorting out how to do things like this is performed at compile time - no performance is lost at run-time figuring out the best way to achieve Vely statement's stated goal.

Statement name is always in the form of two words with hyphen in between:
along with the clauses supplying the rest of the predicate, such as subject or subordinate clause(s). The two-word model is chosen for simplicity in order to avoid unwieldy do-this-that-way-along-with-something-else or such kind of statements, which unfortunately sometimes proliferate in APIs.

This design mimics natural speech and is typical of declarative languages, as it is easier to write and read than APIs. Just like in a natural language, the action asked in a single statement can be very simple, moderate or involved depending on the need, and the "sentence" used to carry it out is easy to comprehend at a glance, and easy to construct based on "what" needs to be done rather than "how". Even so, the C code generated is still done with performance in mind first and foremost, and the statement design is ultimately always guided by that goal.

Vely statements are the core of the language functionality. Here's a formal description of what they are.
Statement structure
Vely statements generally have three components separated by space(s):
A statement starts with a name, which designates its main purpose. An object argument denotes the object of the purpose stated in the name. Each clause consist of a clause name, which specifies some aspect of the statement's purpose and it may be followed by no additional data, or it may be accompanied with one or more data arguments. A clause may have subclauses, which follow the same structure and are associated with the clause by appearing immediately after it. Most clauses are separated by space(s), however some (like "=" or "@") may not need space(s) before any data; the statement's documentation would clearly specify this.

An object argument must immediately follow the statement's name, while clauses may be specified in any order.

For example, in the following Vely code:
encrypt-data orig_data input-length 6 output-length define encrypted_len password "mypass" salt newsalt to define res binary

encrypt-data is the statement's name, and "orig_data" is its object argument. The clauses are:
The clauses can be in any order, so the above can be restated as:
encrypt-data orig_data to define res password "mypass" salt newsalt output-length define encrypted_len binary input-length 6

Vely documentation provides a concise BNF-like notation of how each statement works, which in case of encrypt-data is (backslash simply allows continuing to multiple lines):
encrypt-data <data> to [ define ] <result> \
    [ input-length <input length> ] \
    [ output-length [ define ] <output length> ] \
    [ binary [ <binary> ] ] \
    ( password <password> \
        [ salt <salt> [ salt-length <salt length> ] ] \
        [ iterations <iterations> ] \
        [ cipher <cipher algorithm> ] \
        [ digest <digest algorithm> ]
        [ cache ]
        [ clear-cache <clear cache> ) \
    [ init-vector <init vector> ]

Optional clauses are enclosed with angle brackets (i.e between "[" and "]"), and data arguments (in general C expressions) are stated between "<" and ">". If only one of a number of clauses may appear, such clauses are separated by "|", and each clause possibly enclosed with "(" and ")" if it consists of more than one keywords or arguments. Generally a clause continues until the next clause, which means until all subclauses and arguments are exhausted.

The most common subclause is an optional "define", which always precedes an output variable, i.e. a variable that stores (one of) the results of the statements. If used, it creates such variable within the statement. It is commonly used to shorten the code written.

Keywords (other than statement names such as encrypt-data above) are generally specific to each statement (or a group of statements in which they are used). So, keyword "salt", for example, has meaning only within encrypt-data statement, where it is used to specify the data for the "salt" clause. In order to have the complete freedom to choose your variable names so they don't clash with keywords, you can simply surround them (or the expressions in which they appear) in parenthesis (i.e. "(" and ")") and use any names you want, without worrying about keywords, for example:
const char *password = "some password";
const char *salt = "0123456789012345";
encrypt-data "some data" password (password) salt (salt) to define (define)
p-out define

In this example, keywords "password", "salt" and "define" are used as variable names as well; and in p-out statement, variable named "define" is used freely, even if it is a keyword for other statements - but it is not for the p-out statement.

It is recommended to use supplied Vely statements over your C code for the same functionality.

Note that while you can use tab characters at the beginning of the line (such as for indentation), as well as in string literals, do not use tabs in Vely code as they are not supported for lack of readability - use plain spaces.
Look and feel
Vely statements have decidedly non-C look and feel, unlike what's common with typical API interface. This is by design. They stand out when reading code in a way that clearly communicates their purpose, with the intent of increased readability and more expressive and condensed functionality. On the other hand, Vely statements are decidedly C, as they are completely integrated with C code and translate to pure C in the end.
Constructs in code blocks
Note that, Vely statements, after translated into C code by vv, are generally made of multiple C statements, hence Vely statements can never be treated as single-line statements. Thus, for example, Vely will emit an error if you write:
if (some condition) vely-statement

if (some condition)
    vely-statement

Instead, write:
if (some condition) {
    vely-statement
}

Integration with C
Vely is loosely integrated with C, with the integration points chosen for maximum practical benefit. It does not parse C to work, mostly because that does not present significant benefits and it would slow down code generation.

Vely does, however, process certain C elements. It processes strings in C expressions used in Vely statements, as well as parenthesis to make sure that statement clauses are parsed properly.

Probably the most important aspect of integrating with gcc is line number reporting. Line numbers and error messages reported by gcc and gdb are referring to source Vely files, making it easy to find the exact source code line of any issue.

Note that at the same time, if you use --c-lines option (see vv), it forces generated C code line reporting. That is rarely necessary because gcc messages are typically detailed and sufficient.
Splitting statement into multiple lines
To split a statement into multiple lines (including string continuations), use a backslash (\), for instance:
encrypt-data orig_data input-length 6 \
    output-length define encrypted_len \
    password "my\
    pass" salt \
    newsalt to define res binary

Note that all statements are always left-trimmed for whitespace. Thus the resulting string literal in the above example is "mypass", and not "my   pass", as the whitespaces prior to line starting with "pass" are trimmed first. Also, all statements are right-trimmed for white space, except if backslash is used at the end, in which case any spaces prior to backslash are conserved. For that reason, in the above example there is a space prior to a backslash where clauses need to be separated.
Error handling
A statement that fails for reasons that are generally irrecoverable will fail, for example out of memory or disk space, bad input parameters etc. Vely philosophy is to minimize the need to check for such conditions by preventing the program from continuing. This is preferable, as forgetting to check usually results in unforeseen bugs and safety issues, and the program should have stopped anyway.

Errors that are correctable programmatically are reported and you can check them, for example when opening a file that may or may not exist.

Overall, the goal is to stop execution when necessary and to offer the ability to handle an issue when warranted, in order to increase run-time safety and provide instant clues about conditions that must be corrected.
Code processing, error reporting, debugging
A statement is pre-processed into C code, which is then compiled into a native executable using gcc compiler. The final C code is a mix of your own code and generated Vely code.

Error reporting by default shows line numbers in your .vely source code files, which generally makes it easy to pinpoint an error. If you want to see generated C code and make error reporting refer to it, use "--c-lines" option of vv utility; this is useful if the error refers to details of generated code. You can also obtain the line number from .vely source file, and then examine generated source code file by looking for #line directives. Generated final C file is located in:
/var/lib/vv/bld/<app name>/__<source file base name>.o.c

For instance if application name is "myapp" and source file is "mycode.vely", then generated code is in file:
/var/lib/vv/bld/myapp/__mycode.o.c

If an error is reported as being on line 43 of file "mycode.vely", then look for lines in the above .c file that look like:
#line 43 "mycode.vely"

The code adjacent to those lines is the generated code for the Vely statement at line 43 in "mycode.vely".

Vely will perform many sanity checks when possible while preprocessing your .vely files, such as enforce the presence and count of required arguments in clauses, check if conflicting clauses are used, report imbalance of opening/closing clauses in statements that use them (such as for example write-string, read-line or with database_queries), reject unknown statements, report incorrect usage of statements and similar. However, most of the checking of your code is ultimately done by gcc (Linux C compiler), such as typing, expression syntax, C language correctness, linkage issues and others; in doing so, gcc will report the correct line number, as stated above (either a line number in your .vely source file, or a generated C code file).

When debugging (such as with using gdb), stepping through your code is similar to error reporting: by default gdb will treat Vely statement as a "single-statement" and step over it as such. If you use "--c-lines" option, then you will be able to step through final C code.
See also
Language ( dot   inline_code   statement_APIs   syntax_highlighting   unused-var  )  SEE ALL (documentation)
stat-file

Purpose: Get information about a file.

stat-file <file> \
    size | type | path | name \
    to [ define ] <variable>

stat-file gets information about <file>, which is either the full path of a file or directory, or a name relative to the application home directory (see how_vely_works).

Clause "size" will store file's size in bytes to <variable>, or it will be VV_ERR_FAILED (if operation failed, likely because file does not exist or you have no permissions to access it).

Clause "type" will store file's type to number <variable>, and it can be either VV_FILE (if it's a file) or VV_DIR (if it's a directory) or VV_ERR_FAILED (if operation failed, likely because file does not exist or you have no permissions to access it).

Clause "path" gets the fully resolved path of the <file> (including symbolic links), and "name" is the name (a basename, without the path). The results of both "path" and "name" clauses are allocated memory. If path cannot be resolved, then <variable> is an empty string.
Examples
To get file size in variable "sz", which is created here:
stat-file "/home/user/file" size to define sz

To determine if the object is a file or a directory:
stat-file "/home/user/some_name" type to define what
if (what == VV_FILE) {
   @It's a file!
} else if (what == VV_DIR) {
   @It's a directory!
} else {
   @Doesn't exist!
}

Get the fully resolved path of a file to string variable "fp", which is created here.
stat-file "../file" path to define fp

See also
Files ( close-file   copy-file   delete-file   file-position   file_storage   file_uploading   lock-file   open-file   read-file   read-line   rename-file   stat-file   temporary_file   uniq-file   unlock-file   write-file  )  SEE ALL (documentation)
Syntax highlighting

For syntax highlighting of Vely programs in Vim, do this once:
vv -m

The above will create a syntax file in your local Vim syntax directory:
$HOME/.vim/syntax/v.vim

and also update your local $HOME/.vimrc file to use this syntax for files with .vely extension. All files updated are local, i.e. they affect only the current user. Each user who wants this feature must issue the above command.

The Vely highlighting syntax is tested with Vim 8.1.
See also
Language ( dot   inline_code   statement_APIs   syntax_highlighting   unused-var  )  SEE ALL (documentation)
Temporary file

To create a temporary file, use uniq-file with a "temporary" clause. Temporary files are the same as any other files in the file_storage (and are organized in the same fashion), except that they are all under the subdirectory named "t":
/var/lib/vv/<app_name>/app/file/t

A temporary file is not automatically deleted - you can remove it with delete-file statement when not needed (or use a periodic shell script to remove old temporary files). The reason for this is that the nature of temporary files varies, and they may not necessarily span a given time frame (such as a lifetime of a request, or a lifetime of a process that serves any number of such requests), and they may be used across number of requests for a specific purpose. Thus, it is your responsibility to remove a temporary file when it's appropriate for your application to do so.

The reason for storing temporary files in a separate directory is to gain a separation of temporary files (which likely at some point can be freely deleted) from other files.

See uniq-file for an example of creating a temporary file.
See also
Files ( close-file   copy-file   delete-file   file-position   file_storage   file_uploading   lock-file   open-file   read-file   read-line   rename-file   stat-file   temporary_file   uniq-file   unlock-file   write-file  )  SEE ALL (documentation)
trace-run

Purpose: Emit trace.

trace-run [ <format>, <expression> [ , ... ] ]

trace-run formats a tracing message according to the <format> string and a list of variable-type <expression>s (in the same way C's printf() does) and then writes the result into current process' trace_file (if enabled, see vv) without any encoding (meaning a string is output exactly as it is).

trace-run can be used without any clauses, in which case a location (file name and line number) is recorded in the trace file - this is useful when you only want to know if the execution passed through code.

If trace-run has any other clauses, then <format> string must be present and there must be at least one <expression> (it means if you want to trace a simple string literal you still have to use "%s" as format).

For tracing to have effect, debugging and tracing must be enabled (see vv). For location of trace files, see how_vely_works.
Examples
// Trace information
trace-run "Program wrote %lld bytes into file %s", num_bytes, file_name

// Trace that program execution passed through here
trace-run

See example_hello_world for an example in debugging and tracing.
See also
Debugging ( debugging   trace-run  )  SEE ALL (documentation)
trim-string

Purpose: Trim a string.

trim-string <string> [ length [ define ] <new length> ] [ result [ define ] <result> ]

trim-string trims <string>, both on left and right. You can optionally get the length of the changed string by using "length" clause, in integer variable <new length>, which can be created with optional "define".

Without "result" clause, trimming is in place, i.e. if necessary the bytes within <string> are shifted to the left, and the result is <string>. With "result" clause,  <result> points to trimmed value within <string>, i.e. there is no movement of memory - this is a faster trim, but the pointer to the result is no longer <string>.
Examples
The variable "str" will be "some string" and "new_len" will be 11:
char str[] = "  some string  ";
trim-string str length define new_len

With "result" clause, the variable "str" will be "some string" and "new_len" will be 11, and "res" will point to (str+2):
char str[] = "  some string  ";
trim-string str length define new_len result define res

See also
Strings ( copy-string   count-substring   lower-string   split-string   trim-string   upper-string   write-string  )  SEE ALL (documentation)
uniq-file

Purpose: Create a new empty file with a unique name.

uniq-file [ define ] <file name> [ temporary ]

One of the common tasks in many applications is creating a unique file (of any kind, including temporary). uniq-file statement does that - it creates a new unique file of zero size, with <file name> being its fully qualified name, which is always within the file_storage. <file name> is allocated memory.

If string variable <file name> does not exist, it can be created with "define" clause. The file itself is created empty. If "temporary" clause is used, then the file created is a temporary_file.

The file has no extension. You can rename it after it has been created to reflect its usage or purpose.

All files created are setup with owner and group read/write only permissions.
Examples
The following creates an empty file with auto-generated name that will be stored in "mydoc" variable. String variable "mydoc" is defined in the statement. The string "some data" is written to a newly created file:
uniq-file define mydoc
write-file mydoc from "some data"

To create a temporary file:
uniq-file define temp_file temporary
...
// use file named "temp_file"
..
delete-file temp_file

See also
Files ( close-file   copy-file   delete-file   file-position   file_storage   file_uploading   lock-file   open-file   read-file   read-line   rename-file   stat-file   temporary_file   uniq-file   unlock-file   write-file  )  SEE ALL (documentation)
unlock-file

Purpose: Unlocks file.

unlock-file id <lock id>

unlock-file will unlock file that was locked with lock-file. <lock id> is the value obtained in lock-file's "id" clause.
Examples
See lock-file.
See also
Files ( close-file   copy-file   delete-file   file-position   file_storage   file_uploading   lock-file   open-file   read-file   read-line   rename-file   stat-file   temporary_file   uniq-file   unlock-file   write-file  )  SEE ALL (documentation)
unused-var

Purpose: Prevent compiler error if variable is not used.

unused-var <variable name>

unused-var prevents C compiler from erroring out if <variable name> is unused. Generally, you don't want to have unused variables - they typically indicate errors or clutter. However, in some cases you might need such variables as a reminder for a future stage of a project, or for some other reason it is unavoidable. In any case, you can use unused-var to shield such instances from causing errors.
Examples
In the following, variable "bw" is created and the number of bytes written is stored in it. Such variable is not used at the moment, however if you would use it in the future and want to keep it, use unused-var to prevent compiler errors:
pf-out bytes-written define bw "Hi %s\n", "world"
unused-var bw

See also
Language ( dot   inline_code   statement_APIs   syntax_highlighting   unused-var  )  SEE ALL (documentation)
upper-string

Purpose: Upper-case a string.

upper-string <string>

upper-string converts all <string>'s characters to upper case.
Examples
The resulting "str" is "GOOD":
char str[]="good";
upper-string str

See also
Strings ( copy-string   count-substring   lower-string   split-string   trim-string   upper-string   write-string  )  SEE ALL (documentation)
utf8-json

Purpose: Convert UTF8 string to JSON text.

utf8-json <utf8> \
    to [ define ] <json> \
    [ length <length> ] \
    [ status [ define ] <status> ] \
    [ error-text [ define ] <error text> ]

utf8-json will convert <utf8> text to <json> text which can be created with optional "define". <json> is allocated memory.

<utf8> is a string that may contain UTF8 characters (as 2, 3 or 4 bytes representing a unicode character). Encoding creates a string that can be used as a value in JSON documents. utf8-json is performed according to RFC7159 (JSON standard) and RFC3629 (UTF8 standard).

Note that hexadecimal characters used in JSON for Unicode (such as \u21d7) are always lowercase. Solidus character ("/") is not escaped, although json-utf8 will correctly process it if the input has it escaped.

<json> is always allocated in utf8-json with storage sufficient to hold it.

The optional length of <utf8> can be specified with <length> in "length" clause. If <length> is not specified, it is calculated as string length of <utf8>. Note that a single UTF-8 character can be anywhere between 1 to 4 bytes. For example "љ" is 2 bytes in length.

The status of encoding can be obtained in optional <status> variable (a number), which can be created with optional "define". <status> is the string length of the result in <json> or -1 if error occurred, in which case the error text can be obtained in <error text> in optional "error-text" clause, which can be created with optional "define".
Examples
Convert UTF8 string to JSON value and verify the expected result:
// UTF8 string 
char utf8_str[] = "\"Doc\"\n\t\b\f\r\t⇗⇘\t▷◮𝄞ᏫⲠш\n/\"()\t";

// Convert UTF8 string to text
utf8-json utf8_str status define encstatus to define json_text

// This is the text expected
(( define expected_result
@\"Doc\"\n\t\b\f\r\t\u21d7\u21d8\t\u25b7\u25ee\ud834\udd1e\u13eb\u2ca0\u0448\n/\"()\t
))

// Make sure conversion was okay, decs is the length of the result (encj string)
if (!strcmp (json_text, expected_result) && encstatus!=-1) {
    @decode-json worked okay
}

See also
UTF8 ( json-utf8   utf8-json  )  SEE ALL (documentation)
Vely architecture

Architecture overview
A Vely program works as a request-reply processor. It can be either an application server or a command-line program that processes GET, POST, PUT, PATCH, DELETE or any other HTTP requests.

When it is an application server, it runs as FastCGI (FCGI) server processes that clients access through reverse proxies (typically web servers like Apache or Nginx). Any number of such Vely FCGI processes can run, including a dynamic number determined by the request load (ranging from 0 resident processes to any maximum number specified). Each Vely FCGI process handles one request at a time, and any number of such processes work in parallel, which means code and libraries used do not have to be thread-safe. Vely application layer is separate from the presentation (eg. web server) and data (eg. database) layers. These layers can run on multiple tiers, separated not only functionally, but physically as well (meaning they can run on different real or virtual hardware), improving scalability, reliability and security.

When a Vely application runs in the command_line, a request is handled by a single execution of a compiled program. This option may be suitable for batch jobs, for use in shell scripts, as well as any other situation where it is more useful or convenient to execute a command-line program. Note that a command-line program can double as CGI (Common Gateway Interface) programs as well; this option may be suitable when high performance is not needed, or for other reasons.

A Vely program can access database, internet/network, file system or any other computing resource.

Vely

Execution flowchart
Before the very first request a Vely program serves, startup code in optional _startup.vely executes. Every request must have an application name and a request name in its request_URL (for instance "do_something"). Before handling this request, an optional before-request handler in _before.vely executes. Vely's request dispatcher will route the request to a function with the same name (in this case "void do_something()") that must reside in a file with the same name (which is do_something.vely). After the code in that file executes, an after-request handler in _after.vely executes (optional). This concludes request processing. Note that any .vely file name that starts with an underscore is a non-request file, that is, it doesn't process any requests and its code is used in other .vely files.

Vely

Performance and stability
Vely applications are native executables by design. This approach avoids performance loss associated with other schemes, such as byte-code, interpreters and similar. Consequently, small memory footprint is achieved with minimal code overhead and by using on-demand dynamic libraries whenever possible; less memory consumption and higher performance allow for better scaling in high-demand environments, with cloud applications and IOT (Internet of Things) being an example.

FastCGI server processes generally stay up across any number of requests, increasing response time. The balance between the number of processes and the memory usage during high request loads can be achieved with adaptive feature of vf, Vely's FastCGI process manager. Since Vely processes running in parallel are single-threaded, programming with Vely does not present challenges of thread-based programming and increases run-time stability.

Vely supports use of prepared database queries with true automatic unlimited reuse across any number of requests; database connections are persistent and stay connected for the life of the server process.  

As far as stability, Vely statement_APIs are designed for safety, ease of use, encapsulation and to some extent abstraction of tasks required, making it more difficult to write unstable code. Handling of allocated memory includes memory tracking and garbage collection, preventing memory leaks which can be fatal to long running processes; similarly, files open with file-handling statements are automatically closed at the end of each request, serving the same purpose. Request-processing and process daemon-izing infrastructure is included. Where possible, use Vely statements and infrastructure instead of pure C in order to get the most of stability enhancing benefits.
From source code to executable
All Vely code is in .vely files, which contain C code with Vely used freely within. To create an executable program, vv utility will preprocess .vely files into 100% C code, and link it. Linkage uses various libraries (database, internet etc.), and those are dynamically linked only if needed, keeping the executable the smallest possible. When code changes, Vely FCGI manager (vf) will restart the server (optional).

Vely

Vely programs are built in a single command-line step, and ready to use immediately (see vv). Flexible architecture means the exact same source code can run both as a server application (such as web application back-end) and a command-line program.

Read more about how_vely_works.
See also
General ( deploying_application   how_vely_works   quality_control   rename_files   SELinux   vely   vely_architecture   vely_removal   vf   vv   why_C_and_Vely  )  SEE ALL (documentation)
Vely dispatch request

"void vely_dispatch_request()" is an automatically generated request-dispatching function in Vely. It uses request name (see request_URL) to call the appropriate request handler.

For example, if the request name is "myreq", then function with signature "void myreq()" will be called - such function must be implemented in "myreq.vely" source code file.

You can implement two hooks into vely_dispatch_request(): one that executes before each request handling (before_request_handler) and one that executes afterwards (after_request_handler).

In terms of debugging, breaking in this function gives you a good starting point to debug the handling of any given request, for instance in gdb:
br vely_dispatch_request

If no request has been recognized (i.e. request name does not match any request-handling .vely source file), then
At the end of the request, all strings allocated by Vely will be freed.

You cannot change the implementation of vely_dispatch_request(), but you can see it in the build directory (see vv).
See also
Requests ( after_request_handler   before_request_handler   building_URL   getting_URL   global_request_data   non_request   normalized_URL   request   request_URL   startup_handler   vely_dispatch_request  )  SEE ALL (documentation)
Vely removal

To remove Vely, use the standard packager you used to install it, such as dnf, apt or zypper, typically with "remove" option, for example:
#fedora
sudo dnf remove vely
#debian
sudo apt remove vely
#openSUSE
sudo zypper remove vely
#ArchLinux (use -s to remove dependencies)
sudo pacman -R vely

Note that /var/lib/vv directory is not removed, as it generally contains application files. You may move such files or delete them as you see fit.
See also
General ( deploying_application   how_vely_works   quality_control   rename_files   SELinux   vely   vely_architecture   vely_removal   vf   vv   why_C_and_Vely  )  SEE ALL (documentation)
Vf

Purpose: Run and manage FastCGI programs.

vf <options> <app name>

vf is a FastCGI (FCGI) program manager. An FCGI program is started as a number of concurrent processes serving application requests, typically from reverse-proxy servers such as Apache or Nginx. Use vf to create Vely applications, including both FCGI and command-line.

A number of options are available to setup and manage the execution of a FCGI program as an application server, which can be accessed either via TCP/IP or a Unix domain socket.

vf is a part of vely package. You can, however, use vf  in any case, whether your FCGI programs are created by Vely or otherwise.

<app name> specifies the name of your application. Each application must have a unique name. <app name> may contain alphanumeric characters and an underscore, must start with a character and its maximum length is 30.

vf runs as a light-weight daemon (often requiring only 100-150K of resident RAM), with a separate instance for each application specified by the <app name>. You must supply a valid FCGI program to run. When vf starts your FCGI program, its current directory is set to /var/lib/vv/<app name>. The permissions context is inherited from the caller, so the effective user ID, group ID and any supplemental groups are that of the caller. You can use tools like runuser to specifically set the permissions context.

vf will re-start FCGI processes that exited or died, keeping the number of processes as specified, unless -n option is used. The number of worker FCGI processes can be specified with a fixed (-w) option, or it can dynamically change based on the load (-d option), including none at all. Hence, it is possible to have no worker processes at all, and they will be started when incoming request(s) come in.

Your FCGI program must handle SIGTERM signal and gracefully exit, meaning exit by completing the request that may be executing when SIGTERM was received. See plain_C_FCGI example.

<options> are:
vf writes log file at /var/lib/vv/<app name>/vflog/log file. This file is overwritten when vf starts, so it contains the log of the current daemon instance only.
Exit code
When starting, vf exits with 0 if successful and 1 if it is already running. If FastCGI executable cannot run, the exit code is -1. When creating application, vf exits with 0 if successful, and -1 if not.
Process control
When vf is told to stop the application (with "-m stop" arguments), it will send SIGTERM signal to all its children. All children processes must complete the current request before exiting, assuming they are currently processing a request; otherwise they must exit immediately. Vely will always do so; if you are using non-Vely FastCGI application, you should make sure this requirement is met to avoid interrupted requests.

If vf is terminated without "-m stop", (for example with SIGKILL signal), then all its chidlren will immediately terminate with SIGKILL as well, regardless of whether they are currently processing any requests or not.
Platforms and requirements
vf is a part of Vely package. See vely.

Examples
Running your application server on startup
If you want your application to run on startup (so you don't have to run it manually), you can add it to systemd configuration. Here is an example (replace <app name> with your application name and <app owner> with the name of the Operating System user under which your application is installed):
[Unit]
Description=Vely FastCGI Program Manager for [<app name>] application.
After=network.target

[Service]
Type=forking
ExecStart=/usr/bin/vf <app name>
ExecStop=/usr/bin/vf -m quit <app name>
KillMode=process
Restart=on-failure
User=<app owner>

[Install]
WantedBy=multi-user.target

The above should be saved in the directory given by the output of the following system command:
pkg-config systemd --variable=systemdsystemunitdir

The file should be saved as <app name>.service (or similar). Once saved, you can use standard systemctl commands to start, stop and restart your service.
See also
General ( deploying_application   how_vely_works   quality_control   rename_files   SELinux   vely   vely_architecture   vely_removal   vf   vv   why_C_and_Vely  )  SEE ALL (documentation)
Vv

Purpose: Builds Vely applications.

vv <options>

vv is a vely tool for application building.
Command-line options
Examples
See also
General ( deploying_application   how_vely_works   quality_control   rename_files   SELinux   vely   vely_architecture   vely_removal   vf   vv   why_C_and_Vely  )  SEE ALL (documentation)
Why C and why Vely?


C
Writing programs in C generally results in fastests and smallest programs, which is the reason why it's widely used in system programming and infrastructure software. C is also the greenest programming language, which means if all programs were written in C, there would be considerably less emissions, water use and pollution, not to mention we would all enjoy faster software that requires less hardware and consequently less use of resources.

On the other hand, the reason why you wouldn't have picked up C in the past may be memory management and low-level constructs. That changes with Vely.
Vely
Vely is a framework for C that lets you rapidly build server-side applications of maximum possible performance with code that's ergonomic, easy to write and read, and considerably safer than C alone, because you do not need to allocate/free memory, or worry about buffer overruns or memory violations. You also get automatic memory allocation/garbage collector and automatic file closing.

Vely statements are embedded in C code and designed to be declarative, i.e. a single line of code performs a task that is meaningful to a human, and without having to know details of how exactly to do it. Such tasks include building strings, querying databases, outputting data, file manipulation, memory structures like hash and fifo, program execution, encryption, encoding, JSON parsing, web stuff like cookies, input parameters, uploading and downloading files, request handling, daemonizing etc. - in short, lots of high-level stuff you need every day and don't want to spend time reinventing the wheel.

Vely generates C underneath for several reasons. It is the shortest route to maximum performance and the smallest memory footprint. C is simple. It also allows usage of virtually any library in existence. And Vely statements are carefully crafted with the goal of generating fast and safe C code on their own. Vely itself is written in C.

Vely's goal is not to write more C code, quite the opposite. By using Vely anywhere possible, C code can be used as a supporting mechanism for Vely statements, such as declaring variables, conditional statements, program flow and usage of external libraries. This means all the important and difficult parts are done by single-line Vely statements. The purpose of C is then just a "programming glue" that binds together your application and business logic.
Simple and practical
Vely statements look nothing like C, or for that matter like many other popular languages. They are simple and you write them inside C code so there is no need to learn anything new about the layer underneath; C is quite simple and well-known.

Most of Vely statements generate a number of C statements. Still, their scope is typically narrow and the generated code is shallow and direct, similar to what an experienced C programmer would write, incurring virtually no loss of performance. Vely's design omits the classic API look and feel on purpose and focuses on simplicity. The arguments are specified in any order by naming their purpose.

Vely is about practical, actual needs people have; it's about productivity, safety and performance. The idea is to not sacrifice performance at all, and to improve productivity and safety significantly.
What it is, and what it isn't
Vely is not a language. It is a better way to API. As a founding layer, C is a great compromise: rapid and easy development on top of a simple programming base with improved safety and arguably the best performance. This is especially true in the Cloud, where smaller and faster means less CPU seconds, less RAM, less money spent, less energy used and less emissions. And Moore's law may or may not be failing, but it will take significantly more funding, time and expenditure of all kinds to keep it going, and at some point it may no longer.

In short, the goal of Vely is to lend the superior performance of C to general-purpose application development, and especially web applications. Regardless of what kind of hardware you run, or whatever kind of software you're designing, ultimately, performance matters.
What does Vely look like
Here's an example that lists employees, writes output to file "employees", and then displays the result in a web page or on the command line - all this in less than 30 lines of code:
void list_employees() {
    out-header default
    @<html><body>

    char *header = make_header(); // write any C code
    p-out header

    // Create report in outmsg string and fill it with database query result
    write-string define outmsg
        // Get data from the database
        run-query ="select name, salary from employees order by name" output name, salary
            @Name: <<query-result name>>
            @<br/>
            @Salary: <<query-result salary>>
            @<br/><br/>
        end-query
    end-write-string

    // Write report to a file and then to the client
    write-file "employees" from outmsg status define st
    if (st<0) {
        @Error in writing file (<<pf-out "%lld", st>>)
        exit-request
    } else {
        p-out outmsg
    }
    @</body></html>
}

Highlighted with Vely's syntax_highlighting using 2html.
See also
General ( deploying_application   how_vely_works   quality_control   rename_files   SELinux   vely   vely_architecture   vely_removal   vf   vv   why_C_and_Vely  )  SEE ALL (documentation)
write-fifo

Purpose: Write key/value pair into a FIFO list.

write-fifo <list> key <key> value <value>

write-fifo adds a pair of key/value pointers to the FIFO <list>, specified with string <key> and <value> (in "key" and "value" clauses, collectively called an "element").

It always adds elements to the end of the list. <value> is a pointer to any type, allowing storage of any kind of data.

Memory pointed by <key> and <value> must not go out of scope or be freed while FIFO is used - if necessary, store a copy (see copy-string for strings). This is because write-fifo does not make copies of <key> and <value>, rather only the pointers to those are stored.
Examples
new-fifo define nf
write-fifo nf key "mykey" value "myvalue"

See also
FIFO ( new-fifo   purge-fifo   read-fifo   rewind-fifo   write-fifo  )  SEE ALL (documentation)
write-file

Purpose: Write to a file.

write-file <file> | ( file-id <file id> ) \
    from <content> \
    [ length <length> ] \
    [ ( position <position> ) | ( append [ <append> ] ) ] \
    [ status [ define ] <status> ]

Without file-id
This is a simple method of writing a file. File named <file> is opened, data written, and file is closed.

write-file writes <content> to <file>, which is either a full path of the file or a path relative to the application home directory (see how_vely_works).

If "append" clause is used without optional boolean expression <append>, or if <append> evaluates to true, the <content> is appended to the file; otherwise the file is overwritten with <content>, unless "position" clause is used in which case file is not overwritten and <content> is written at byte <position>. Note that <position> can be beyond the end of file, and null-bytes are written between the current end of file and <position>.

File is created if it does not exist (even if "append" is used), unless "position" clause is used in which case file must exist.

If "length" is not used, then a string is written to a file, and the number of bytes written is the length of that string. If "length" is specified, then exactly <length> bytes is written and <content> can hold a binary value or a string.

If "status" clause is used, then the number of bytes written is stored to <status>, unless error occurred, in which case <status> has the error code. The error code can be VV_ERR_POSITION (if <position> is negative or file does not support it), VV_ERR_WRITE (if there is an error writing file) or VV_ERR_OPEN if file is not open. Note that no partial data will be written; if all of data cannot be written to the file, then none will be written, and in that case an error of VV_ERR_WRITE will be reported in <status>.

With file-id
This method uses a <file id> that was created with open-file. You can then write (and read) file using this <file id> and the file stays open until close-file is called.

If "position" clause is used, then data is written starting from byte <position>, otherwise writing starts from the current file position determined by the previous reads/writes or as set by using "set" clause in file-position. After each read or write, the file position is advanced by the number of bytes read or written. Position can be set passed the last byte of the file, in which case writing will fill the space between the current end of file and the current position with null-bytes.

If "length" is not used, then a string is written to a file, and the number of bytes written is the length of that string. If "length" is specified, then exactly <length> bytes is written and <content> can hold a binary value or a string.

If "append" clause is used without optional boolean expression <append>, or if <append> evaluates to true, then file pointer is set at the end of file and data written.

If "status" clause is used, then the number of bytes written is stored to <status>, unless error occurred, in which case <status> has the error code. The error code can be VV_ERR_POSITION (if <position> is negative or file does not support it), VV_ERR_WRITE (if there is an error writing file) or VV_ERR_OPEN if file is not open. Note that no partial data will be written; if all of data cannot be written to the file, then none will be written, and in that case an error of VV_ERR_WRITE will be reported in <status>.
Examples
To overwrite file "/path/to/file" with "Hello World":
write-file "/path/to/file" from "Hello World"

To append "Hello World" to file:
char *path="/path/to/file";
char *cont="Hello World";
write-file path from cont append

To write only 5 bytes (i.e. "Hello") and get status (if successful, number variable "st" would be "5"):
char *cont="Hello World";
write-file "file" from cont length 5 status define st

To write a string "Hello" at byte position 3 in the existing "file":
char *cont="Hello";
write-file "file" from cont position 3 status define st

See open-file for an example with "file-id" clause.
See also
Files ( close-file   copy-file   delete-file   file-position   file_storage   file_uploading   lock-file   open-file   read-file   read-line   rename-file   stat-file   temporary_file   uniq-file   unlock-file   write-file  )  SEE ALL (documentation)
write-hash

Purpose: Store key/value pair into a hash table.

write-hash <hash> \
    key <key> \
    value <value> \
    [ status [ define ] <status> ] \
    [ old-value [ define ] <old value> ]
    [ old-key [ define ] <old key> ]

write-hash will store pointers <key> (in "key" clause) and <value> (specified in "value" clause) into hash table <hash>, which must be created with new-hash.

<key> is a string and <value> can be a pointer of any type, allowing storage of any kind of data; collectively they are called an "element". Memory pointed by <key> and <value> must not go out of scope or be freed while the hash is used - if necessary, store a copy (see copy-string for strings); this is because write-hash does not make copies of <key> and <value>, rather only the pointers to those are stored in the hash.

If <key> already exists in the hash table, then the pointer to old value associated with it is returned in the optional <old value> (in "old-value" clause) and the pointer to old key is returned in the optional <old key> (in "old-key" clause) - in this case the optional number <status> (in "status" clause) has a value of VV_ERR_EXIST.

If <key> did not exist, <status> will be VV_OKAY and <old value>/<old key> are NULL. Both <status> and <old value>/<old key> can be created with "define" clause.

Note that while <key> and <old key> will contain matching strings when <status> is VV_ERR_EXIST, the <old key> will contain a pointer to a key used in the previous write-string statement.
Examples
Writing data to hash:
new-hash define h size 1000
write-hash h key "mykey" value "some data"

Writing new value with the same key and obtaining the previous value (which is "some data"):
write-hash h key "mykey" value "new data" status define st old-value define od
if (st == VV_ERR_EXIST) {
    @Previous value for this key is <<p-out od>>
}

See read-hash for more examples.
See also
Hash table ( get-hash   new-hash   purge-hash   read-hash   resize-hash   write-hash  )  SEE ALL (documentation)
write-string

Purpose: Create complex strings.

write-string [ define ] <string>

<any code>

end-write-string [ bytes-written [ define ] <bytes written> ] [ notrim ]

Output of any Vely code (that normally would go to a client) can be written into <string>, which can be created (if it doesn't exist) with "define". In between write-string and end-write-string you can write <any Vely code>. For instance you can use database queries, conditional statements, call C code etc., just as you would for any other Vely code. write-strings can be nested, meaning you can use write-string to write to a different string while within write-string, and presumably use that string to output it within the parent.

<string> is allocated memory.
Length of result
To get the length of the string written, use the optional "bytes-written" clause, in which case <bytes written> will have the number of bytes written, minus the trailing zero byte (i.e. it is the length of the string output). If <bytes written> is not defined, you can create it with "define" within the statement.
Shortcut code
Note that instead of write-string you can also use a shortcut "(("  (and instead of end-write-string you can use "))"  ), for example here a string "fname" holds a full path of a file named "config-install.vely" under the application home directory (see how_vely_works), and "bw" holds the number of bytes written:
get-app directory to define home_dir
(( fname
@<<p-out home_dir>>/config-install.vely
)) bytes-written define bw

Trimming
Just like with all other Vely code, every line is trimmed both on left and write, so this:
(( define mystr
@Some string
))

is the same as:
(( define mystr
        @Some string <whitespaces>
))

write-string (or "((") statement must always be on a line by itself (and so does end-write-string, or "))" statement). The string being built starts with the line following write-string, and ends with the line immediately prior to end-write-string.

All trailing empty lines are removed, for example:
(( define mystr
        @My string
        @
        @
))

the above string would have two trailing empty lines, however they will be removed. If you want to skip trimming the trailing whitespaces, use "notrim" clause in end-write-string.
Referencing result within write-string
The <string> pointer (i.e. the result) is set to an empty string at the beginning of write-string; only after end-write-string completes does it take the computed value. That's because using <string> within write-string is generally inefficient as it's value is copied to the result. If you must use the result recursively (which is not recommended), you can save the pointer to it and use it.
Examples
- Simple
A simple example:
char *my_str="world";
char *my_str1="and have a nice day too!";

write-string define result_str
@Hello <<p-out my_str>> (<<p-out my_str1>>)
end-write-string

p-out result_str

The output is
Hello world (and have a nice day too!)


- Using code inside
Here is using Vely code inside write-string, including database query and conditional statements to produce different strings at run-time:
input-param selector

char *my_str="world";

write-string define result_str
    if-string selector=="simple"
        @Hello <<p-out my_string>> (and have a nice day too!)
    else-if-string selector=="database"
        run-query @db="select name from employee"
            @Hello <<query-result name>>
            @<br/>
        end-query
    else
        @No message
    end-if
end-write-string

p-out result_str

If selector variable is "simple", as in URL
https://mysite.com/<app name>/some_service?selector=simple

the result is
Hello world (and have a nice day too!)

If selector variable is "database", as in URL
https://mysite.com/<app name>/some_service?selector=database

the result may be (assuming "Linda" and "John" are the two employees selected):
Hello Linda
<br/>
Hello John
<br/>

If selector variable is anything else, as in URL
https://mysite.com/<app name>/some_service?selector=something_else

the result is
No message

In the above example, "result_str" variable is defined on the spot, but it can also be defined elsewhere without using "define".

- Using function calls inside
The following uses functions inside write-string (note that "<<.func2();>>" is simply calling C code as inline_code):
void func1 ()
{
    char *result_str;

    write-string result_str
        @<<p-out "Result from func2()">> is <<.func2();>>
    end-write-string
    p-out result_str
}

void func2()
{
    p-out "Hello from func2"
}

The output from func1() is
Result from func2() is Hello from func2


- Nesting
An example to nest write-strings:
write-string define str1
    @Hi!
    write-string define str2
        @Hi Again!
   end-write-string
   p-out str2
end-write-string
p-out str1

The result is
Hi!
Hi Again!


- Returning result from function
The result of write-string can be returned from a function (because it is heap memory), as in this example:
void func1 ()
{
   write-string define result_str
       char *func2_result;
       @<<p-out "Result from func2()">> is <<p-out func2(&func2_result)>>
   end-write-string
   p-out result_str
}

char *func2(char **result)
{
   write-string *result
       @<hr/>
       run-query @db="select firstName from employee"
           @Hello <<query-result firstName>>
           @<br/>
       end-query
       @<hr/>
   end-write-string
   return *result;
}

The output from func1() is:
Result from func2() is <hr/>
Hello Linda
<br/>
Hello John
<br/>
<hr/>

See also
Strings ( copy-string   count-substring   lower-string   split-string   trim-string   upper-string   write-string  )  SEE ALL (documentation)


Copyright (c) 2017-2023 Dasoftver LLC