Writing your own programs to run from the menu system.
------------------------------------------------------

The menu interface is fairly simple, but allows you to easily add your own
programs to the bbs. All programs run from the menu are run with exec_w
so the calling program waits for completion. Programs are passed 2 channels,
input and output, and a command line starting with the bbs-line number of the
caller, followed by a space, followed by an optional command line.

example: In the file "menu_main0"  under ":keys" is the line

E info_prg editorial

If a bbs user on line 1 presses 'E' from the main menu, the menu program
will look up "info_prg" in the "system_dat" file. This returns (in the supplied
version) "win1_pbox_exe_info_exe", so the menu program runs the line

EXEC_W "win1_pbox_exe_info_exe",#0,#1;"1 editorial"

If you are running under SMSQ, the program (in this case info_exe) need not
be executable, it could be an Sbasic file.

Within the program, #0 is the input channel, #1 is the output channel, and cmd$
(in this case) is set to "1 editorial"

As you know the line-number of the caller, you can look up information about
the caller by asking the server for values held in "line-variables". See
"linevars_txt" for a full list.
An example might be
CLIENT "PBOX":REQUEST "PBOX","GETLINE 1 NAME",a$
where a$ will be set to the name of the caller on line 1.

Because only two channels are passed, 'C' programs do not get a STDERR channel.
If you use routines which need STDERR, please open it to a temp file or a
nul device. Alternatively, use libmin and pick up the channels using Qdos calls
(example follows).

Example routines.
-----------------

Here is the source to "cltag_prg", the program which clears the taglist on
demand. It starts with some simple procedures and functions to call the server.
These are not mandatory, but make the code simpler. You could put direct
"request" lines into the program if you prefer.
Note that the program contains no embedded text to print (except log messages)
and all text messages are requested from the server. The server gives you the
relevant message in the users preferred language. This is another reason why
all programs need to know which bbs-line called them.
Please adhere to this in your own programs as it makes language support very
simple.
Note that while it is possible to look up values in language or system files
yourself, it's much easier to let the server do it for you.
All errors returned by the server (variable not found etc.) begin with a
minus char, ie "- Error finding fred", so you can simply test for errors by
looking if the first char is '-'. The server never returns an empty string.
Note that because all variable values are picked up from the server, there is
no need to set either prog_use or data_use to anywhere in particular.
Please adhere to this in your own programs.
Note also that the program has a timeout for all user input, an will force a
disconnect if the user does not respond in a preset time, ("timeout" setting
stored in "system_dat") or if the line is dropped (signalled by EOF on the
input channel). This means your bbs serial port needs to support EOF on carrier
loss.

rem Clear taglist for Pbox
JOB_NAME 'CLtag v1.00'
:
def proc abort
  freeclient 'pbox'
  stop
end def
:
def proc setline(a$)
loc ans$
request 'pbox','setline '&lno$&' '&a$,ans$
end def
:
def proc log(a$)
loc ans$
request 'pbox','log '&lno$&' '&a$,ans$
end def
:
def fn get_line$(a$)
loc ans$
request 'pbox','getline '&lno$&' '&a$,ans$
ret ans$
end def
:
def fn sysmsg$(a$)
loc ans$
request 'pbox','sysmsg '&lno$&' '&a$,ans$
ret ans$
end def
:
def fn sysvar$(a$)
loc ans$
request 'pbox','sysvar '&a$,ans$
ret ans$
end def
:
def fn getkey
loc l,k$
rep l
  k$=inkey$(#0,inactive):rem inactivity timeout
  if k$=''
     setline 'exitcmd : logoff_prg inactive'
     if eof(#0)
       log 'Carrier lost'
     else
       log 'Inactivity timeout in CLTAG '&where$
     endif
     abort
  endif
  if k$<>chr$(10): return code(k$)
end rep l
end def
:
client 'pbox'
a$=sysvar$('inactive')
if a$(1)='-':inactive=9000:else:inactive='0'&a$
where$=''
sp=' ' instr cmd$
if sp = 0
  lno$=cmd$
else
  lno$=cmd$(1 to sp-1)
endif
lf$=chr$(13)&chr$(10)
log 'Clear taglist'
presskey$=sysmsg$('presskey')
yesno$=sysmsg$('yesno')
cltag$=sysmsg$('cltags')
yhave$=sysmsg$('yhave')
ntag$=sysmsg$('ntags')
notag$=sysmsg$('notags')
done$=sysmsg$('done')
abort$=sysmsg$('aborted')
n$=get_line$('tagn')
if n$<>0
  print#1;yhave$&' '&n$&' '&ntag$&lf$&cltag$;
  where$='confirm clear'
  a=getkey
  if (chr$(a) instr yesno$ =1) or (chr$(a) instr yesno$ =2)
    delete 'ram1_taglist'&lno$
    setline 'tagn 0'
    setline 'tagk 0'
    print#1;done$&lf$;
  endif
else
  print#1;notag$&lf$;
endif
setline 'exitcmd = menu_prg curr'
abort


Now an example in 'C'
---------------------

Here is the source to "info_exe" which demonstrates how to pick up the
channels and command-line for a "libmin" program, and how to use the server
from 'C'.

 /*
        Info   - play a text file with/without 'more' prompts
 */
 
#include <stdio.h>
#include <stdlib.h>
#include <qdos.h>
#include <qptr.h>
#include <unistd.h>
#include <string.h>
#include <csrvthg.h>
#include <database.h>

char _prog_name[] = "Pbox Info";
char _version[] = " v1.00 ";
long ipch;                      /* input channel  */
long opch;                      /* output channel */
long timeout = 9000;            /* inactivity timeout */
char inactive[81];
char cmdline[81];
char yesno[5];
char exitmsg[81];
char sysdir[81];
char errfile[81];
char moremsg[81];
char presskey[81];
char language;
char lf[] = { '\r', '\n' };
int  line = 0;


void reverse(char s[])  /* reverse string s in place, used by ltoa */
{
  int c,i,j;
  for(i=0, j=strlen(s)-1; i<j; i++, j-- )
  {
    c = s[i];
    s[i] = s[j];
    s[j] = c;
  }
}

void ltoa(long n, char s[])  /* Convert a long integer to ascii text */
{                 /* avoids using printf and knocks 10k off the program size */
 int i, sign;

 if ((sign = n) < 0 )
    n = -n;
 i = 0;
 do {
       s[i++] = n % 10 + '0';
    }  while ((n /= 10) > 0);
 if (sign < 0)
    s[i++] = '-';
    s[i] = '\0';
    reverse(s);
}

char getkey(void)    /* fetch a key or timeout, ignoring line feeds */
{
char c;
int ferr;

  while(1)
  {
    ferr = io_fbyte( ipch, timeout, &c );
    if (ferr) return(0);
    if (c != '\n') return(c);
  }
}

void sysmsg(char *varname, char *ans, int sz)  /* ask for a text message */
{
char msg[80];
char lno[4];
char *a;
short rlen;

  strcpy(msg,"sysmsg ");
  ltoa(line,lno);               /* server needs line nr so it gets the */
  strcat(msg,lno);              /* message in the right language */
  strcat(msg," ");
  strcat(msg,varname);
  a=Request ("pbox", msg, strlen(msg), &rlen);
  strncpy(ans,a,sz);
  ans[sz] = '\0';
  free(a);
}

void getline(char *varname, char *ans, int sz) /* read a line-variable  */
{
char msg[80];
char lno[4];
char *a;
short rlen;

  strcpy(msg,"getline ");
  ltoa(line,lno);
  strcat(msg,lno);
  strcat(msg," ");
  strcat(msg,varname);
  a=Request ("pbox", msg, strlen(msg), &rlen);
  strncpy(ans,a,sz);
  ans[sz] = '\0';
  free(a);
}

void sysvar(char *varname, char *ans, int sz) /* read varible from system_dat */
{
char msg[80];
char *a;
short rlen;
  strcpy(msg,"sysvar ");                /* NOTE no line number needed */
  strcat(msg,varname);
  a=Request ("pbox", msg, strlen(msg), &rlen);
  strncpy(ans,a,sz);
  ans[sz] = '\0';
  free(a);
}

void log(char *value)   /* write a message to the log file */
{
char msg[80];
char lno[4];
char *a;
short rlen;

  strcpy(msg,"log ");
  ltoa(line,lno);               /* which line did this */
  strcat(msg,lno);
  strcat(msg," ");
  strcat(msg,value);
  a=Request ("pbox", msg, strlen(msg), &rlen);
  free(a);
}

void setline(char *value)       /* set a line variable */
{
char msg[80];
char lno[4];
char *a;
short rlen;

  strcpy(msg,"setline ");
  ltoa(line,lno);
  strcat(msg,lno);
  strcat(msg," ");
  strcat(msg,value);
  a=Request ("pbox", msg, strlen(msg), &rlen);
  free(a);
}

int main (int ac, char **av)
{
char *sp;                       /* stack pointer  */
short nc;                       /* no of channels */
short mlen;                     /* cmdline length */
long flch = -1;                 /* file ID        */
char *a;
int n = -1;
char reply[81];
char fname[41];
char lang;
short rlen;
char key;
int ferr;
char more;
short lines;
short shown;

    nc = ac;                    /* keep compiler quiet  */
    sp = *(av + 1);             /* supplied sp          */
    nc = *((short *) sp);       /* no of channels       */
    sp += 2;                    /* advance              */
    if (nc != 2) exit(-15);     /* abort if no channels */
    ipch = *((long *) sp);      /* read qdos channel id */
    sp += 4;                    /* advance              */
    opch = *((long *) sp);      /* read qdos channel id */
    sp += 4;                    /* advance              */
    mlen = *((short *) sp);     /* command line length  */
    sp += 2;
    a = sp+mlen;
    *a = '\0';

    if(mlen == 0) exit(-15);
    while(*sp == ' ') sp++;                     /* skip leading whitespace */
    if(strchr(sp,' ') == NULL) exit(-15);       /* if not >1 param, error  */

    line = atoi(sp);                            /* 1st param is line number */
    while(*sp != ' ') sp++;                     /* skip past number */
    while(*sp == ' ') sp++;                     /* and whitespace */
    strncpy(cmdline,sp,80);                     /* and read rest of command */
    cmdline[80] = '\0';

    if((n=UseSrvThg (CLNT, "pbox")) == 0)
    {
      sysvar("sysdir", sysdir, sizeof(sysdir)); /* get variables needed */
      sysmsg("yesno", yesno, sizeof(yesno));
      sysmsg("errfile", errfile, sizeof(errfile));
      sysmsg("more", moremsg, sizeof(moremsg));
      sysmsg("presskey", presskey, sizeof(presskey));
      getline("more", reply, 2);
      more = reply[0];
      getline("lines", reply, 10);
      lines = (short)atoi(reply);
      getline("lang", reply, 10);
      reply[1] = '\0';

      setline("exitcmd = menu_prg curr");

      if(sysdir[0] == '\0')
      {
        log("-Error unable to access sysdir");
        FreeSrvThg (CLNT, "pbox");
        return(0);
      }
      strcpy(fname, sysdir);
      strcat(fname,"info_");
      strcat(fname,cmdline);
      strcat(fname,reply);

      ferr = 0;   /* signal 'no error' (yet) */

      flch = io_open(fname,1);          /* try to open correct language file */
      if (flch < 0)
      {
        a = fname;
        a += strlen(fname);
        a -= 1;
        *a = '0';                       /* if it fails, try language 0 */
        flch = io_open(fname,1);
      }
      if (flch < 0)
      {
        *a = '\0';
        flch = io_open(fname,1);        /* if that fails, try no language */
      }
      if (flch < 0)                     /* if it still fails, give up */
      {
        strcpy(reply, "-Error unable to open \"");
        strcat(reply,fname);
        strcat(reply,"\"");
        log(reply);
        ferr = -7;          /* signal "not found" and "dont press a key" */
      }
      else
      {
        io_sbyte(opch, -1, 12);
        strcpy(reply, "Info: ");
        strcat(reply,fname);
        log(reply);             /* this is what we're doing */
      }
      shown = 0;

      while(ferr >= 0)
      {
        ferr = io_fline(flch, -1, reply, 80);
        if (ferr > 0)
        {
          io_sstrg(opch, -1, reply, ferr -1);
          io_sstrg(opch, -1, lf, 2);
          shown++;
          if(more != '0')               /* more prompts are wanted */
          {
            if(shown == lines-1)        /* have we shown a screenfull */
            {
              io_sstrg(opch, -1, moremsg, strlen(moremsg));
              key = getkey();
              io_sbyte(opch, -1, key);
              io_sstrg(opch, -1, lf, 2 );
              if(yesno[2]==key || yesno[3]==key) /* if you said NO  */
                 ferr = -1;                      /* force loop exit */
              shown = 0;
            }
          }
        }
      }
      if (ferr != -1) /* -1=NO to response to MORE prompt */
      {
        io_sstrg(opch, -1, presskey, strlen(presskey));
        key = getkey();
        io_sstrg(opch, -1, lf, 2 );
      }
      io_close(flch);
    }
    FreeSrvThg (CLNT, "pbox");
    return(0);
}
int (*_Cstart) () = main;
