# DuskCC standard library

While DuskCC's core language tries to stay very close to ANSI C, we allow
ourselves significant deviations from ANSI when it comes to the standard
library. Dusk's I/O is different from a regular UNIX, it handles counted strings
rather than null-terminated strings, etc. The standard library needs
adjustements.

The standard library lives in /comp/c/lib.fs. That library is not loaded
automatically when you load /comp/c/cc.fs. If you need that library, you should
ensure that it's loaded with `?f<< /comp/c/lib.fs`. Then, in you C unit, you
do `#include /comp/c/lib.h`.

## Standard Library API

### Proxied Forth words

void abort();
void quit();
void noop();
void abortmsg(char *msg);
uint min(uint a, uint b);
uint max(uint a, uint b);
void emit(char c);
void stype(char *str);
int zstrlen(char *zstr);
int Console();                            --> console :self
void* allot(uint n);                      --> here# + allot
void* allot0(uint n);                     --> here# + allot0
void memset(void *addr, char c, int n)    --> cfill
void memcpy(void *dst, void *src, uint n) --> cmove

int llend(int ll);
void llappend(int ll, int elem);
int llcnt(int ll);

uint le_fetch32(uint addr);             --> le@
ushort le_fetch16(uint addr);           --> 16b le@
void le_store32(uint addr, uint val);   --> le!
void le_store16(uint addr, ushort val); --> 16b le!
uint be_fetch32(uint addr);             --> be@
ushort be_fetch16(uint addr);           --> 16b be@
void be_store32(uint addr, uint val);   --> be!
void be_store16(uint addr, ushort val); --> 16b be!

### Strings

uchar strlen(char *str)

  Return the length of string `str`. Same as "c@".

uint strequal(char* s1, char *s2)

  Returns 1 if s1 and s2 are equal, otherwise 0. Same as "s=".
  Note that the name of this function is purposefully not strcmp(), which is
  absent from this API. The idea is too avoid porting errors while at the same
  time avoiding this weirdness of having the concept of "s1 < s2", which is way
  too ambiguous to be a core function.

int isdigit(char c)

  Returns 1 if char is in the 0-9 range, 0 otherwise.

### I/O

The I/O API is a simple layer over the system I/O. `hdl` is a regular I/O
handle.

int fgetc(void* hdl)                        --> IO :getc
void fputc(void* hdl, char c)                --> IO :putc
char* freadline(void* hdl)                   --> IO :putback
int fopen(char *path)                        --> Path :find# + Path :open
int fread(void* hdl, void *addr, int n)      --> IO :read
int freadbuf(void* hdl, void **addr, uint n) --> IO :readbuf
int fwrite(void* hdl, void *addr, int n)     --> IO :write
int fwritebuf(void* hdl, void *addr, int n)  --> IO :writebuf
void fseek(void* hdl, int pos)               --> File :seek
void fclose(void* hdl)                       --> IO :close
void fputs(void* hdl, char *str)             --> IO :puts
void puts(char *str)                         --> console :puts
(see below for details about printf/scanf)
void fprintf(void* hdl, char *fmt, ...)      --> IO :printf
void printf(char *fmt, ...)                  --> console :printf
char* sprintf(char *fmt, ...)                --> sprintf
int fscanf(void* hdl, char *fmt)             --> see below
int scanf(char *fmt)                         --> fscanf(console readio)
int sscanf(char *fmt, char *str)

### freadbuf

This function is special because :readbuf returns two arguments. The return
value of freadbuf() is "read-n", but "a" is returned through a pointer to an
address which you supply as an argument. If :readbuf is successful, that the
resulting address is written there. Otherwise, nothing is written.

### printf/scanf and variants

fprintf behaves a bit like the standard one, but supports less formatting
arguments.

The I/O handle goes first, followed by the formatting string, then followed by a
variable number of arguments which must match the number of formatting
placeholder in `fmt`. For example, `printf("%b %w %d", 1, 2, 3)` prints
`01 0002 3`.

printf formatting arguments are described in [doc/lib/fmt].

fscanf doesn't exist in sys/io yet and is only and CC stdlib. It supports only
the `%d` placeholder.  It has a funky API because it *pushes* a variable number
of arguments, corresponding to the number of placeholders in the format string,
to PS. It then pushes a 1 in case of scan success.

If it couldn't properly scan the input stream, it pushes *no* argument, only a 0
to indicate failure.

For example, `sscanf("%d %d %d", "5 6 7")` results in PS receiveing elements
`5 6 7 1`, `1` being on top. `sscanf("%d %d %d", "5 6 foo")` yields only a `0`
to PS.

Therefore, when you use sscanf, you need to use "pspop()".

printf and scanf each have `f` and `s` variants. Without a variant, printing and
scanning is done through Console. The `f` variant allow us to choose another
target I/O handle.

For the `s` variant, we have a static buffer wrapped around a `MemIO` that is
used. The resulting string is a memory in that buffer and is overwritten at each
`sprintf` and `sscanf` call.

### DSTYPE, DPRINTF, DCPRINTF

DSTYPE (debug stype) is a macro for conditionally calling stype based on the
"ccdebug" value. Disabling this value results in nothing being generated,
making the presence of the DSTYPE macros come at null cost when debug prints
are disabled. By default, the value of "ccdebug" is 0.

The selection is done at the moment where [comp/c/lib.h] is #includ-ed, so what
you'll typically want to do is to add "1 to ccdebug" right before you
call cc<< on your C unit (which must of course #include comp/c/lib.h).

The DSTYPE macro takes one argument: the string to print. Such a call can look
like this:

  DSTYPE "hello world!\\n"

String escapes have to be double-escaped because the first escape is for the
macro argument. In the "\n" case, it's actually not required because a literal
LF will work fine, but if you want to insert a '"' character, you'll actually
need to double-escape it.

Following the same logic, there's DPRINTF which is the same as DSTYPE but for
printf. This macro has 2 arguments: the format string and the "rest". Example:

  DPRINTF "hello %d %x\\n" "foo, bar"

And then, there's DCPRINTF, that is, a "conditional" printf, that is, which has
a runtime conditional check. This has 3 arguments, with the first being the
runtime condition. Example:

  DCPRINTF "a==42" "hello %d %x\\n" "foo, bar"

which renders as:

  if (a==42) printf("hello %d %x\n", foo, bar);

Here's a reference of actual macro values:

DSTYPE   stype("%<");
DPRINTF  printf("%<", %<);
DCPRINTF if (%<) printf("%<", %<);

### pspush() and pspop()

Immediate functions "void pspush(int val)" and "int pspop()" allow for direct
control over PS. This gives you the ability to pop or push a variable number of
arguments from/to PS.

These functions, however, mess with arguments and return values. You can't use
"pspop()" if your function has arguments and you can't use "pspush()" if it has
a return value. Also, "pspop()" shouldn't be used in complex expressions, only
"straight". Example:

    void foo() {
        int a = pspop();
        int b = pspop();
        int x = 42;
        pspush(a-b+x);
    }
    1 2 foo . --> prints "43"

### nbelem()

nbelem() is an immediate function that returns the number of elements in an
array. It errors out if the expression doesn't yield an array. Example:

  int x[42];
  return nbelem(x); // returns 42

  int *x;
  return nbelem(x); // error! not an array

### Others

void qsort(uint *addr, uint count)
  Sorts an array of `count` integers at address `addr` using the QuickSort
  algorithm.

void assert(uint n)
  If "n" is zero, abort with message "assertion error".
