                  .,::      .:.::::::.  :::::::::::::::::::..   
                  `;;;,  .,;;,;;'```';;,;;;;;;;;'''';;;;``;;;;  
                    '[[,,[[' [[[     [[[\    [[      [[[,/[[['  
                     Y$$$P   "$$c  cc$$$"    $$      $$$$$$c    
                   oP"``"Yo,  "*8bo,Y88b,    88,     888b "88bo,
                ,m"       "Mm,  "*YP" "M"    MMM     MMMM   "W" 
                --------------------------------------------------
                Mystic BBS - MPL Tutor : File Bases and more...
                --------------------------------------------------

  In this tutorial we will explain how we can manipulate the file bases in
Mystic BBS. We will do this by writing a new script. This script finds the 
files in our current file base, checks which of them are text files, according
to our specifications and gives the option to the user to preview them. It's 
similar to the Mystic Gallery, but more advanced and for files that are in our
file bases. So lets begin and explain the code...

---[ Code ]-------------------------------------------------------------------

Uses Cfg              // Declare the Units we need
Uses Fbase
Uses Fgroup
Uses User
  
const searchx = 20      // Constants for our little GUI
const searchy = 24
const searchcol = '|15'
const max_list = 200

Const Fext = 'pdf;txt;doc;diz;nfo;ans;asc'  // These are the files that we
                                            // search for. You can add more.

Type                            // This is the format of a File base in 
  RecFileBase = Record          // Mystic. You can find it in the 
    Index      : Word;          // records.112 file inside the docs folder
    Name       : String[40];    
    FtpName    : String[60];
    FileName   : String[40];
    aDispFile   : String[20];
    Template   : String[20];
    ListACS    : String[30];
    FtpACS     : String[30];
    DLACS      : String[30];
    ULACS      : String[30];
    HatchACS   : String[30];
    SysOpACS   : String[30];
    Path       : String[80];
    DefScan    : Byte;
    Flags      : LongInt;
    Created    : LongInt;
    NetAddr    : Byte;
    EchoTag    : String[30];
  End;

Type                          // This is the format of the file listing
  RecFileList = Record        // inside a file base, for Mystic. It is also
  FileName  : String[70];     // included in the records.112 file
  Size      : LongInt;
  DatTim    : LongInt;
  Uploader  : String[30];
  Flags     : Byte;
  Downloads : LongInt;
  Rating    : Byte;
  DescPtr   : LongInt;
  DescLines : Byte;
End;

Var Files  : RecFileList      // Declare our Variables
Var Fbase  : Recfilebase
Var CurFBaseName: string
Var CurFBaseIDX: word              

Var TotalAreas:byte
Var Temp      :byte
Var Temp2     :byte  
Var Area      :array[1..max_list] of String
Var Idx       :Array[1..max_list] of byte
Var TopPage   :byte
Var BarPos    :byte
Var Done      :Boolean
Var Ch        :Char
Var Ch2       :Char
Var More      :byte
Var LastMore  :byte
var brec      :RecFileBase
var mmbacs    :string
var i         :word
var search    :string
var search_idx:integer
var ii        : integer
var kk        : char
------------------------------------------------------------------------------

So far we declared what we will use in our script. Below we begin to write our
procedures and the actual code.

---[ Code ]-------------------------------------------------------------------

Procedure BarON         // This procedure displays the Selected Item.
begin
  GotoXY (21, 9 + BarPos - TopPage)
  Write ('|15|17 ' + PadRT(StripMCI(Area[BarPos]), 39, ' ') + '|16')
end

Procedure BarOFF        // ... and this the non selected items
begin
  GotoXY (21, 9 + BarPos - TopPage)
  Write ('|07 ' + PadRT(Area[BarPos], 39, ' '))
  Write (StrREP(' ', 61 - WhereX))
end 

Procedure DrawPage     // Draws the items on the screen. 12 per page.
begin
  Temp2 := BarPos
  For Temp := 0 to 11 do begin 
    BarPos := TopPage + Temp
    BarOFF
  end
  BarPos := Temp2
  BarON
end 

Procedure DrawScreen      // A simple procedure to refresh the graphics
Begin
  DispFile (CfgTextPath + 'xq-tp')
  DrawPage
End
------------------------------------------------------------------------------

Those were the procedures for the drawing of the GUI, pretty straight forward.
Now we will see how to explore the file bases.

The following function retrieves the data of a file base that we specify its
name and stores that data in a RecFileBase record type.

---[ Code ]-------------------------------------------------------------------

function fbasebyname(name:string; var rec:RecFileBase ):boolean
var
  fptr: file              // Declare our file variable
  datfile:string;         // Use this to store our filename which is the
                          // data/fbases.dat file.
  brec2 : RecFileBase     // Here we store the filebase data
  res : boolean
  reccount:integer
  
begin
res:=false
datfile:=CfgdataPath+'fbases.dat'   // Take the path of the fbases.dat file
If FileExist(datfile) Then Begin    // Does the file exist?
    fAssign(Fptr,datfile,66)        // If yes, then open it, for reading...
    fReset(Fptr)
      While Not fEof(Fptr) Do Begin     // Read the file to the end of it
        fillchar(brec2, sizeof(brec2), #0);   // Clear our Record Variable
        fReadrec(Fptr,brec2)            // Read one record/base 
        if brec2.name=name then begin   // If the name of the file base is the
                                        // same with the one we want then 
                                        // assign the data to our Variable
                                        // and set the result to true.
          res:=true
          rec:=brec2
        end
      end
    fClose(Fptr)                     // Close the file
  end
  fbasebyname:=res                   // Assign the result to our function
end

------------------------------------------------------------------------------

Now we need to find which files are in that file base and retrieve the data
for each file.

---[ Code ]-------------------------------------------------------------------

Function ReadListEntry(FN:String;q:Integer):Boolean
Var Ret  : Boolean=False
Var Fp  : File        // Declare our File Variable
Begin
  fAssign(Fp,CfgDataPath+FN+'.dir',66)  // Mystic saves the listing of each 
                                        // file base in a .DIR file which has
                                        // as its name, the name of the file
                                        // base. These files are stored in the
                                        // Data folder of mystic.
  fReset(Fp)                            // Open the file for reading
  If IoResult = 0 Then Begin            // As long we don't get an error...
    fSeek(Fp,(q-1)*SizeOf(Files))       // proceed and read the file listing
    If Not fEof(Fp) Then Begin          // If we don't reached the end of the
                                        // file read the data of each file 
                                        // listed in that base and store them
                                        // to our Variable, which is Files
                                        // We declare that earlier.
      fReadRec(Fp,Files)
      Ret:=True
    End
    fClose(Fp)                          // Close the file, after finish.
  End  
  ReadListEntry:=Ret
End

------------------------------------------------------------------------------

So with those two procedures we can find all the files that are stored in a
particular file base. But we don't want any file base, but only the current
file base that the users is in. We get that file base with the help of the 
MCI2Str function, which returns the result of any Pipe Code. To get our 
current file base we use the FB pipe code. 

---[ Code ]-------------------------------------------------------------------

  CurFBaseName:=mci2str('FB')               // Get and store the name of our
                                            // current file base to the
                                            // CurFBaseName variable
------------------------------------------------------------------------------    

After finding the current file base, we need to read its data, description etc

---[ Code ]-------------------------------------------------------------------

  fbasebyname(curfbasename,fbase)   // So we use the FBaseByName function we
                                    // wrote earlier. All data can be accessed
                                    // from the FBase variable
    
------------------------------------------------------------------------------

Now we have to check all the files inside that file base and see which of them
are text files that we can display.
    
---[ Code ]-------------------------------------------------------------------
  i:=0
  While ReadListEntry(fbase.filename,I+1) Do Begin   // Get the first, second
                                                     // Nth file of the base
    if pos(upper(justfileext(files.filename)),upper(fext))>0 
      and totalareas<=max_list then begin        // Is the file extension of
                                                 // that file in our search 
                                                 // criteria? If yes add it 
                                                 // to our list.
      TotalAreas := TotalAreas + 1
      Area[TotalAreas] := files.filename
      idx[TotalAreas] := i
     // writeln(files.filename)
    end
    I:=I+1
  End
  
------------------------------------------------------------------------------

To check if a file has the right extension we use the Pos and the JustFileExt
functions. First we get the file extension of the file with the JustFileExt
function and then, after capitalizing the extension, so that we can make a non
case sensitive search, we check if that file extension is inside our criteria,
that we have stored in the FExt Constant in the beginning of our script, with 
the Pos function.

So we have found the correct files and we listed them on the screen. Now we 
can give to the User the ability to preview them. For simple text files is a 
simple process... we just open those files with the Internal File Viewer of
Mystic. We do that with the following command:

      menucmd('GV','ansiviewtxt;ansiviewh;0;'+fbase.path+area[barpos])

menucmd                 : A function that executes a Pipe Code
GV                      : Is the Gallery Viewer Command
ansiviewtxt             : Is a template file for the Viewer
ansiviewh               : Is the Help File for the viewer
fbase.path+area[barpos] : Is the filename of the file we want to view


But we have allowed the script to also search for PDF files... how we can 
preview them in Mystic? We will use a Linux utility that converts PDF files to
Text files for viewing them in the Terminal. The utility is pdftotext. If you
are a Windows user, just find another utility that does the same thing. 

So when we have a PDF file, first we convert it and then view it with the 
Mystic File Viewer, like the other files. To do that we give this command:

      MenuCmd('DD','pdftotext "'+fbase.path+area[barpos]+'" /tmp/pdf.txt')
      
menucmd                 : A function that executes a Pipe Code
DD                      : Pipe Command that executes an external app      
pdftotext               : Is the utility we need to convert the PDF file
fbase.path+area[barpos] : This is the file we want to convert      
/tmp/pdf.txt            : ...and we save it in a temporary folder in our pc      
      
Now we can view the converted file (/tmp/pdf.txt) with the command:

            MenuCmd('GV','ansiviewtxt;ansiviewh;0;/tmp/pdf.txt')


This is the part of the code that we check of which type our selected file is
and preview it. We do that when the user presses the Enter key.

---[ Code ]-------------------------------------------------------------------

  If Ch = Chr(13) Then begin      // Did the user pressed the Enter key?
    case upper(justfileext(area[barpos])) of
      'PDF' : begin
                MenuCmd('DD','pdftotext "'+fbase.path+area[barpos]+
                '" /tmp/pdf.txt')
                MenuCmd('GV','ansiviewtxt;ansiviewh;0;/tmp/pdf.txt')
              end
      'TXT' : begin
                menucmd('GV','ansiviewtxt;ansiviewh;0;'+fbase.path+
                area[barpos])
              end
      'DIZ' : begin
                menucmd('GV','ansiviewtxt;ansiviewh;0;'+fbase.path+
                area[barpos])
              end
      'NFO' : begin
                menucmd('GV','ansiviewtxt;ansiviewh;0;'+fbase.path+
                area[barpos])
              end
      'ASC' : begin
                menucmd('GV','ansiviewtxt;ansiviewh;0;'+fbase.path+
                area[barpos])      
              end
      'ANS' : begin
                menucmd('GV','ansiviewtxt;ansiviewh;0;'+fbase.path+
                area[barpos])      
              end
      'DOC' : begin
                menucmd('GV','ansiviewtxt;ansiviewh;0;'+fbase.path+
                area[barpos])      
              end
    end
    DrawScreen
  End

------------------------------------------------------------------------------

You will find the complete script below, just copy paste it and add it to your
BBS... i will also upload it as a separate script/mod for the Mystic BBS. I 
hope that you found this tutorial very useful and write more scripts yourself.
You can make various things by knowing which are the files of a file base, 
what is their size, date and more. Just use your imagination and think what 
a user would want from a BBS. 

---[ CODE ]-------------------------------------------------------------------

Uses Cfg
Uses Fbase
Uses Fgroup
Uses User

const searchx = 20
const searchy = 24
const searchcol = '|15'
const max_list = 200

Const Fext = 'pdf;txt;doc;diz;nfo;ans;asc'

Type
  RecFileBase = Record
    Index      : Word;          // change to longint?
    Name       : String[40];         // change to 70?
    FtpName    : String[60];
    FileName   : String[40];
    aDispFile   : String[20];
    Template   : String[20];
    ListACS    : String[30];
    FtpACS     : String[30];
    DLACS      : String[30];
    ULACS      : String[30];
    HatchACS   : String[30];
    SysOpACS   : String[30];
    Path       : String[80];
    DefScan    : Byte;
    Flags      : LongInt;
    Created    : LongInt;
    NetAddr    : Byte;
    EchoTag    : String[30];
  End;

Type
  RecFileList = Record
  FileName  : String[70];
  Size      : LongInt;
  DatTim    : LongInt;
  Uploader  : String[30];
  Flags     : Byte;
  Downloads : LongInt;
  Rating    : Byte;
  DescPtr   : LongInt;
  DescLines : Byte;
End;

Var Files  : RecFileList      
Var Fbase  : Recfilebase
Var CurFBaseName: string
Var CurFBaseIDX: word              

Var TotalAreas:byte
Var Temp      :byte
Var Temp2     :byte  
Var Area       :array[1..max_list] of String
Var Idx       :Array[1..max_list] of byte
Var TopPage   :byte
Var BarPos    :byte
Var Done      : Boolean
Var Ch         :Char
Var Ch2        :Char
Var More      :byte
Var LastMore  :byte
var brec :  RecFileBase
var mmbacs:string
var i:word
var search   :string
var search_idx:integer
var ii : integer
var kk : char

Procedure BarON
begin
  GotoXY (21, 9 + BarPos - TopPage)
  Write ('|15|17 ' + PadRT(StripMCI(Area[BarPos]), 39, ' ') + '|16')
end

Procedure BarOFF
begin
  GotoXY (21, 9 + BarPos - TopPage)
  Write ('|07 ' + PadRT(Area[BarPos], 39, ' '))
  Write (StrREP(' ', 61 - WhereX))
end 

Procedure DrawPage
begin
  Temp2 := BarPos
  For Temp := 0 to 11 do begin 
    BarPos := TopPage + Temp
    BarOFF
  end
  BarPos := Temp2
  BarON
end 

Procedure DrawScreen
Begin
  DispFile (CfgTextPath + 'xq-tp')
  DrawPage
End

Function GetFBaseIDX(S:String):Integer
Var z,Ret : Integer = 0
Begin
  z:=z+1
  While GetFBase(z) And Ret = 0 Do Begin
    If StripMCI(S) = StripMCI(FBaseName) Then Begin
      Ret:=z
    End
    z:=Z+1
  End 
  GetFBaseIDX:=Ret
End

Function ReadListEntry(FN:String;q:Integer):Boolean
Var Ret  : Boolean=False
Var Fp  : File
Begin
  fAssign(Fp,CfgDataPath+FN+'.dir',66)
  fReset(Fp)
  If IoResult = 0 Then Begin
    fSeek(Fp,(q-1)*SizeOf(Files))
    If Not fEof(Fp) Then Begin
      fReadRec(Fp,Files)
      Ret:=True
    End
    fClose(Fp)
  End  
  ReadListEntry:=Ret
End

function fbasebyname(name:string; var rec:RecFileBase ):boolean
var
  fptr: file
  datfile:string;
  brec2 : RecFileBase
  res : boolean
  reccount:integer
  
begin
res:=false
datfile:=CfgdataPath+'fbases.dat'
If FileExist(datfile) Then Begin
    fAssign(Fptr,datfile,66)
    fReset(Fptr)
      While Not fEof(Fptr) Do Begin
        fillchar(brec2, sizeof(brec2), #0);
        fReadrec(Fptr,brec2)
        if brec2.name=name then begin
          res:=true
          rec:=brec2
        end
      end
    fClose(Fptr)
  end
  fbasebyname:=res
end

var datapath:string

begin

getthisuser

If Graphics = 0 Then begin
  MenuCmd ('FA', '')
  Halt
End

TotalAreas := 0
Temp       := 1

search:='';
search_idx:=0

  CurFBaseName:=mci2str('FB')
  CurFBaseIdx:=getfbaseidx(CurFBaseName)
  fbasebyname(curfbasename,fbase)
    
  i:=0
  While ReadListEntry(fbase.filename,I+1) Do Begin
    if pos(upper(justfileext(files.filename)),upper(fext))>0 and 
       totalareas<=max_list then begin
      TotalAreas := TotalAreas + 1
      Area[TotalAreas] := files.filename
      idx[TotalAreas] := i
     // writeln(files.filename)
    end
    I:=I+1
  End

If TotalAreas = 0 Then begin
  WriteLn (GetPrompt(37))
  pause
  Halt
End

 DispFile ('xq-tp')

TopPage  := 1
BarPos   := 1
Done     := False
More     := 0
LastMore := 0

DrawPage

Repeat
  More := 0
  Ch   := ' '
  Ch2  := ' '
  

  If TopPage > 1 Then begin
    More := 1
    Ch   := Chr(244)
  End

  If TopPage + 11 < TotalAreas Then begin
    Ch2  := Chr(245)
    More := More + 2
  End

  If More <> LastMore Then begin
    LastMore := More
    GotoXY (35, 21)
    Write (' |08(|07' + Ch + Ch2 + ' |15m|07ore|08) ')
  End

  Ch := ReadKey
  If IsArrow Then begin
  //HOME key
  if ch = chr(71) then begin
  search_idx:=1
    TopPage := 1
    BarPos  := 1
    drawpage
    end
  //END Key
  if ch = chr(79) then begin
  search_idx:=1
    if TotalAreas > 12 then begin
      TopPage := TotalAreas - 11
      BarPos  := TotalAreas
    end else begin
      BarPos  := TotalAreas
    end
    drawpage
    end
  
    If Ch = Chr(72) Then begin
    search_idx:=1
      If BarPos > TopPage Then begin
        BarOFF
        BarPos := BarPos - 1
        BarON
        end
      Else
      If TopPage > 1 Then begin
        TopPage := TopPage - 1
        BarPos  := BarPos  - 1
        DrawPage
      End
    end
  
    If Ch = Chr(73) Then begin
    search_idx:=1
      If TopPage - 12 > 0 Then begin
        TopPage := TopPage - 12
        BarPos  := BarPos  - 12
        DrawPage
        end
      Else begin
        TopPage := 1
        BarPos  := 1
        DrawPage
      End
    end
  
    If Ch = Chr(80) Then begin
    search_idx:=1
      If BarPos < TotalAreas Then
        If BarPos < TopPage + 11 Then begin
          BarOFF
          BarPos := BarPos + 1
          BarON
          end
        Else
        If BarPos < TotalAreas Then begin
          TopPage := TopPage + 1
          BarPos  := BarPos  + 1
          DrawPage
        End
      End        
  
    If Ch = Chr(81) Then begin
    search_idx:=1
      If TotalAreas > 12 Then
        If TopPage + 12 < TotalAreas - 12 Then begin
          TopPage := TopPage + 12
          BarPos  := BarPos  + 12
          DrawPage
          end
        Else
        begin
          TopPage := TotalAreas - 11
          BarPos  := TotalAreas
          DrawPage
        End
      Else
      begin
        BarOFF
        BarPos := TotalAreas
        BarON
      End
    End    
  ch:=#0
  end else
  If Ch = Chr(26) Then begin
    DispFile (CfgTextPath + 'xq-tph')
    //If Not IsNoFile Then begin
      Ch := ReadKey
      DrawScreen
    //End
    end
  Else
  If Ch = Chr(27) Then begin
    Done := True
  End else
  If Ch =chr(4) Then begin
    if fileexist(fbase.path+area[barpos]) then begin
      menucmd('GT',
      '|#V#1#20#10# Download File #Y-Yes             ,N-No             #')
      kk:=readkey
      if upper(kk)='Y' then MenuCmd ('F3', fbase.path+area[barpos])
      clrscr
      DrawScreen
    end
  end else
  If Ch = Chr(13) Then begin
    //writeln(fbase.path)
    case upper(justfileext(area[barpos])) of
      'PDF' : begin
                MenuCmd('DD','pdftotext "'+fbase.path+area[barpos]+
                '" /tmp/pdf.txt')
                MenuCmd('GV','ansiviewtxt;ansiviewh;0;/tmp/pdf.txt')
              end
      'TXT' : begin
                menucmd('GV','ansiviewtxt;ansiviewh;0;'+fbase.path+
                area[barpos])
              end
      'DIZ' : begin
                menucmd('GV','ansiviewtxt;ansiviewh;0;'+fbase.path+
                area[barpos])
              end
      'NFO' : begin
                menucmd('GV','ansiviewtxt;ansiviewh;0;'+fbase.path+
                area[barpos])
              end
      'ASC' : begin
                menucmd('GV','ansiviewtxt;ansiviewh;0;'+fbase.path+
                area[barpos])      
              end
      'ANS' : begin
                menucmd('GV','ansiviewtxt;ansiviewh;0;'+fbase.path+
                area[barpos])      
              end
      'DOC' : begin
                menucmd('GV','ansiviewtxt;ansiviewh;0;'+fbase.path+
                area[barpos])      
              end
    end
    DrawScreen
  End else if ch=chr(8) then begin
    delete(search,length(search),1)
    gotoxy(searchx,searchy)
    write(searchcol+upper(search)+' ')  
    for ii:=1 to max_list do begin
      if pos(search,upper(area[ii]))>0 then begin
        TopPage := ii
            BarPos  := ii
            search_idx:=ii
            DrawPage
      end
    end 
  end else
  if ch = chr(1) then begin
    for ii:=search_idx+1 to max_list do begin
    if pos(search,upper(area[ii]))>0 then begin
      TopPage := ii
            BarPos  := ii
            search_idx:=ii
            DrawPage
            break
    end
  end 
  end else
  if ch = chr(25) then begin
    gotoxy(searchx,searchy)
  write(strrep(' ',length(search)))
  search:='';
  search_idx:=0
  end else
  if ch>=chr(32) and ch<=chr(128) then begin
  search:=search+upper(ch)
  gotoxy(searchx,searchy)
  write(searchcol+upper(search))  
  for ii:=1 to max_list do begin
    if pos(search,upper(area[ii]))>0 then begin
      TopPage := ii
            BarPos  := ii
            search_idx:=ii
            DrawPage
            break
    end
  end 
  end
Until Done

GotoXY (1, 23)

end


------------------------------------------------------------------------------

    ___ _          _       _                     
   /   (_)___  ___| | __ _(_)_ __ ___   ___ _ __ 
  / /\ / / __|/ __| |/ _` | | '_ ` _ \ / _ \ '__|
 / /_//| \__ \ (__| | (_| | | | | | | |  __/ |   
/___,' |_|___/\___|_|\__,_|_|_| |_| |_|\___|_|   
                                                
You are free to use this tutor as you need. Just give a mention to me (xqtr).
Credits goes to g00r00, the programmer behind Mystic BBS software and to Mystic
Guy, (Avon) of the Agency BBS, for their great job, keeping BBSing alive... 

This script uses some external files. If you download it, as a package, the
files are in that package or else you can use your own ANSI graphics.

Sorry for any typos... English is not my native language...                                            

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  ______         ____  ____ _____    
 /_  __/___     / __ )/ __ ) ___/    
  / / / __ \   / __  / __  \__ \     
 / / / /_/ /  / /_/ / /_/ /__/ / _ _ 
/_/  \____/  /_____/_____/____(_|_|_)
                                     
                                   __     __           ____  ____ _____
        ____  _____   ____  ____  / /_   / /_____     / __ )/ __ ) ___/
       / __ \/ ___/  / __ \/ __ \/ __/  / __/ __ \   / __  / __  \__ \ 
 _ _ _/ /_/ / /     / / / / /_/ / /_   / /_/ /_/ /  / /_/ / /_/ /__/ / 
(_|_|_)____/_/     /_/ /_/\____/\__/   \__/\____/  /_____/_____/____/
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=   
