Batch Simple Plugin System Concept

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Message
Author
Adrianvdh
Posts: 177
Joined: 16 May 2013 13:00

Re: Batch Simple Plugin System Consept

#16 Post by Adrianvdh » 07 Jan 2014 15:01

Hi penpen, it seems it is just you and me on this project not anyone else, because it was a community effort :)

I vaguely understand your system, but I summarized mine again and wanted to know if it was possible to actually do:

main.bat:

Code: Select all

@echo off
:mainlabel
echo\
echo Hi This is line one
echo Hi This is line two
echo Hi This is line three
echo Hi This is line four
Insertion: echo It works^^!
Insertion: pause
echo Hi This is line five
pause
exit

Output:
*space*
Hi This is line one
Hi This is line two
Hi This is line three
Hi This is line four
It works!
Press any key to continue . . .
Hi This is line five
*close*

:scanner
REM Scans for plugins and stores them into a variable, something like:
for a% in plugins/*.bat, if contains a MY_PROGRAM_PLUGIN text please recognize as a plugin
set plugins=a%%


REM Functions
:callfunction
REM This function will insert the invisible:
call :function within 'plugin.bat

REM into the 'mainlabel' @ line 5

:help
REM Lists the arguments and functions that are built into the 'main.bat' file
REM obviously written but the plugin developer.


:getWinKey
//That code

exit /b


SamplePlugin.bat

Code: Select all

::MY_PROGRAM_PLUGIN (AKA MY_BATCH_FILE_NAME_TEXT)
set version=1.0

call :callcuntion within 'main.bat :function, mainmenu 5 n

:function
echo It works^^!
pause
exit /b

:help
SamplePlugin.bat [/list|/call functionName parameter(s)]"
echo   /list               returns a list of all exported functions"

penpen
Expert
Posts: 2009
Joined: 23 Jun 2013 06:15
Location: Germany

Re: Batch Simple Plugin System Consept

#17 Post by penpen » 07 Jan 2014 17:37

Hmm, every time i thought i have understood, what you want to do ... :
Adrianvdh wrote:

Code: Select all

echo Hi This is line four
Insertion: echo It works^^!
Insertion: pause
echo Hi This is line five

penpen wrote:If the main.bat is not a batch file with self-modifying code, then there should be no (big) problem:
So you WANT to create a self modifying code. :!:
That is all other but not really a plugin system... .

I'm not sure if this meets your own requirements:
Adrianvdh wrote:The reason such a system should exist in batch is for many reasons.
A few:

1. The main batch file could be under license and modification is denied, but allowing code to be inserted from the community for development is allowed.
2. If the author allows any sort of plugins, but denies a certain function to his/her batch file the community can implement it themselves.
3. The main batch file could be retired from development but is under a modification is denied license but code insurances is allowed.
4. I just want to see if it was possible to do it in batch, and trying to get a learning experience from it.


Yes, i think this is possible.
At least this is a relatively simple case of self modifying code (that does not mean it were simple).

BUT you have to be really carefull:
For example when inserting lines to a running batch file, you must ensure that after you have finished, your program flow is not interrupted:

Code: Select all

@echo off
cls
setlocal enableDelayedExpansion
goto :next

:finally
   rem lazy save/restore using copy and a temporary file.
   copy main.txt main.bat > nul
   del main.txt
   goto :eof

:next
   copy main.bat main.txt > nul
   set "text=   if ^!N^! LEQ 20 (echo(^!N^!&set /A "N+=1"&(echo ^!text^! >> main.bat)) else (goto :finally)"
   set "N=1"
   (echo !text!)>>main.bat

penpen

Aacini
Expert
Posts: 1913
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: Batch Simple Plugin System Consept

#18 Post by Aacini » 07 Jan 2014 21:31

Aacini wrote:Forget for a moment the examples and explain in plain English: What is the purpose and benefits of such system? It will be used as a replacement or as a companion of a set of libraries (or none of the above)?


Adrianvdh wrote:The reason such a system should exist in batch is for many reasons.
A few:

1. The main batch file could be under license and modification is denied, but allowing code to be inserted from the community for development is allowed.
2. If the author allows any sort of plugins, but denies a certain function to his/her batch file the community can implement it themselves.
3. The main batch file could be retired from development but is under a modification is denied license but code insurances is allowed.
4. I just want to see if it was possible to do it in batch, and trying to get a learning experience from it.

Use your imagination.

As I said you before, I need a specific example of the way the plugin would be used in the Batch file (not examples of the applications that could be developed with it) as reference frame in order to produce working code and not just abstract ideas, but you had not provided anyone so I used my imagination.

The first example below use the plugin as extensible code that extend the program features; the plugin code is considered an addition of the already installed program features and is used in the same way (I think this is the definition of "plugin"). A plugin file must have "Any Name.plugin.bat" name format and its code is physically included into the original program, so the file is renamed to "Any Name.plugin.installed" after the installation and then it is no longer used. All feature routines used in the main file are called in the same way via a chain that pass through all routines and the appropriate one is selected with a numeric value. When the main program run it initializes all routines via ":initialize" subroutine that must define an unique identifier for itself via "set /A index+=1" and "set feature[%index%]=UniqueID" commands. To call the subroutine of a certain feature, just set "index" variable to the desired numeric value and call the subroutine.

Main1.bat:

Code: Select all

@echo off
setlocal EnableDelayedExpansion

rem Main program for *extensible code* plugin example

rem Install existent plugin files
for %%a in (*.plugin.bat) do (
   type "%%a" >> "%~F0"
   ren "%%a" "%%~Na.installed"
)

rem Initialize all features
set index=0
:initialize
if %index% equ 0 goto initialize
set features=%index%

rem Show the installed features menu
:loop
echo/
echo Installed features:
for /L %%i in (1,1,%features%) do echo    %%i- !feature[%%i]!
set index=0
set /P "index=Enter the desired one: "
if %index% equ 0 goto :EOF
rem Call the function in the feature selected by index: predefined or plugin
call :function Param1 Param2
goto loop


rem First predefined feature
:initialize
set /A index+=1
set feature[%index%]=First predefined
goto initialize
:function
set /A index-=1
if %index% gtr 0 goto function
echo I am the function of "First predefined" feature, my parameters are: %*
exit /B


rem Second predefined feature
:initialize
set /A index+=1
set feature[%index%]=Second predefined
goto initialize
:function
set /A index-=1
if %index% gtr 0 goto function
echo I am "Second predefined" feature. My parameters: %*
exit /B

additional 1.plugin.bat:

Code: Select all

rem First plugin feature
:initialize
set /A index+=1
set feature[%index%]=First plugin
goto initialize
:function
set /A index-=1
if %index% gtr 0 goto function
echo First plugin feature. My parameters are: %*
exit /B

additional 2.plugin.bat:

Code: Select all

rem Second plugin feature
:initialize
set /A index+=1
set feature[%index%]=Second plugin
goto initialize
:function
set /A index-=1
if %index% gtr 0 goto function
echo Second plugin feature. My parameters are: %*
exit /B

Example output:

Code: Select all

C:\>dir /b
additional 1.plugin.Vat
additional 2.plugin.Vat
Main1.bat
Main2.bat
normalFile.txt
plugin.xyz.bat
rareFile.xyz
unsupportedFile.abc

C:\>Main1.bat

Installed features:
   1- First predefined
   2- Second predefined
Enter the desired one: 1
I am the function of "First predefined" feature, my parameters are: Param1 Param2

Installed features:
   1- First predefined
   2- Second predefined
Enter the desired one: 0

C:\>ren *.vat *.bat

C:\>Main1.bat

Installed features:
   1- First predefined
   2- Second predefined
   3- First plugin
   4- Second plugin
Enter the desired one: 3
First plugin feature. My parameters are: Param1 Param2

Installed features:
   1- First predefined
   2- Second predefined
   3- First plugin
   4- Second plugin
Enter the desired one: 0

C:\>dir /b
additional 1.plugin.installed
additional 2.plugin.installed
Main1.bat
Main2.bat
normalFile.txt
plugin.xyz.bat
rareFile.xyz
unsupportedFile.abc


The second example below use the plugin as a selectable functions library that provide the same set of functions in all plugins. In this case the appropriate set of functions is requested in the main program and the matching plugin is enabled in a way that is transparent for the rest of the main program code. The example below "open" the file given in the parameter and can open files with ".txt" extension by itself, but it will also open any other file type if a matching plugin file exist with the name "plugin.ext.bat".

Main2.bat:

Code: Select all

@echo off
setlocal EnableDelayedExpansion

rem Main program for *selectable functions library* plugin example

set fileExt=%~X1
if %fileExt% equ .txt goto begin
if exist plugin%fileExt%.bat goto begin
echo Unsupported file type: %fileExt%
goto :EOF

:begin
rem Delimit the context where library functions will be called
(
rem Enable the plugin as function library, if needed
if %fileExt% neq .txt ren "%0" _.bat & ren plugin%fileExt%.bat "%0"
rem Call the function to open a file
call :openFile %1
rem Disable the plugin
if %fileExt% neq .txt ren "%0" plugin%fileExt%.bat & ren _.bat "%0"
)
goto :EOF


:openFile
echo Open native supported .txt files:
type %1
exit /b

plugin.xyz.bat:

Code: Select all

:openFile
echo Open .xyz file through plugin:
type %1
exit /b

Example output:

Code: Select all

C:\>Main2.bat normalFile.txt
Open native supported .txt files:
Contents of normal .txt file.

C:\>Main2.bat rareFile.xyz
Open .xyz file through plugin:
This is the contents of RAREFILE.XYZ file!

C:\>Main2.bat unsupportedFile.abc
Unsupported file type: .abc


Antonio

penpen
Expert
Posts: 2009
Joined: 23 Jun 2013 06:15
Location: Germany

Re: Batch Simple Plugin System Consept

#19 Post by penpen » 08 Jan 2014 08:05

Aciini wrote:The first example below use the plugin as extensible code that extend the program features; the plugin code is considered an addition of the already installed program features and is used in the same way (I think this is the definition of "plugin").

If i have learned it right, then your first example is an addin.
I assume the second one meets the requirements of a plugin, although it is a rarely used version, where the main program just loads the plugin:
I'm not sure if it is allowed to replace the main program, but as the main performs this, i tend to see it as a plugin system.

I think it is better to give short definitions of what we are talking about, as it seems they art not clear;
i'm misusing quotes to divide them, so it more readable:
Addin wrote:An extension that is integrated into an application (or hardware).
In the sense of: The content of some/All files of the application is altered, files may be added.

A Static Addin is integrated into an application by an external installer ensuring that the application is not running.

A Dynamic Addin is integrated into an application by the application itself at run-time.
Addon wrote:An optional external module that extends an existing application (or hardware). It is allowed to access the services of the application, and to create new functions.
The applications files will not be changed, but it is allowed to store (add) the files of the addon to the applications directory/subdirectory/subdirectories.

A Static Addon is loaded on the applications startup.
A Dynamic Addon is loaded on the applications runtime.
Plugin wrote:An optional external module that extends an existing application (or hardware).
It is NOT allowed to access the services of the application.
It is allowed to create new functions and use own libraries.
It may be detected at runtime but it is also allowed to install them, while the application is currently not running.
The application accesses the plugin via one/multiple predefined interfaces.
Best be memorized when thinking of a laptop:
A internal graphics card is a static addin.
A laptop battery is a dynamic addin.
A removable power cable is a static addon.
An extension USB cable is a dynamic addon.
A gamepad is a plugin.

But there are also hybrid hardware:
An USB memory stick is both a plugin and a dynamic addon.
(...)

Also there are hybrid types:
Although it is technically wrong, a hybrid type of addon and plugin oftenly is called plugin, too.
(...)

penpen

Adrianvdh
Posts: 177
Joined: 16 May 2013 13:00

Re: Batch Simple Plugin System Consept

#20 Post by Adrianvdh » 08 Jan 2014 10:16

Ahh, Do you understand my system (polite way of saying :))

The 'main.bat' has functions that it need to operate within 'main.bat'
But 'main.bat' can use 3rd party developed code in a form of a plugin.

The 'main.bat' has the routine to support plugins, but the plugins contain the 3rd party functions.
As the 'main.bat' and 'plugin.bat' can share functions, they are both plugins and work BiDi. But 'main.bat'
does not need to require plugins to execute, but the plugins need to 'main.bat' to run.

I sorry if I am being impolite a bit, but I am just frustrated, because I don't understand how your systems are intertwine to mine, and how they fully function. :)

Regards,
Adrian

EDIT: Sorry I posted this before I read your post about plugins, addins and addons
Last edited by Adrianvdh on 08 Jan 2014 10:29, edited 1 time in total.

Adrianvdh
Posts: 177
Joined: 16 May 2013 13:00

Re: Batch Simple Plugin System Consept

#21 Post by Adrianvdh » 08 Jan 2014 10:29

*Deep breathes, deep breathes*

After I read penpens definition of Addins, Addons and plugins, I realize I am truly mistaken.
Man, this whole time, I can't believe it, damn, penpen you have saved a lot of time of explaining, etc.
Sorry for wasting a lot of time as well, because I admit I didn't know what the difference between a plugin and an
addin were.

What I need is this for the system to work:

An optional external module that extends an existing application (or hardware).
It IS allowed to access the services of the application.
It is allowed to create new functions and use own libraries.
The application accesses the plugin via one/multiple predefined interfaces.

penpen
Expert
Posts: 2009
Joined: 23 Jun 2013 06:15
Location: Germany

Re: Batch Simple Plugin System Consept

#22 Post by penpen » 08 Jan 2014 15:58

Adrianvdh wrote:What I need is this for the system to work:

An optional external module that extends an existing application (or hardware).
It IS allowed to access the services of the application.
It is allowed to create new functions and use own libraries.
The application accesses the plugin via one/multiple predefined interfaces.
I think, you should reread my above post with the SampleMain.bat, Sample.dll.bat, and plugins\SamplePlugin.bat.

Maybe i should write it in this way (i'm misusing quotes for definitions... again :D ):
an existing application wrote:application main entry point: SampleMain.bat
application services: Sample.dll.bat
offers: plugin contract (see above post)
one/multiple interfaces: the call command (with no forbidden command line argument combination)
if you see it in this way, then no more is possible.
optional modules wrote:an optional external module: plugins\SamplePlugin.bat

The system fulfills the following requirements:
(plugins\SamplePlugin.bat) extends (SampleMain.bat, Sample.dll.bat, plugin contract, the call command).
(plugins\SamplePlugin.bat) IS allowed to access (Sample.dll.bat).
(plugins\SamplePlugin.bat) is allowed to create new functions (1) and use own libraries (2).
(SampleMain.bat, Sample.dll.bat, plugin contract, the call command) accesses the (plugins\SamplePlugin.bat) via (the call command).

(1),(2) Currently not implemented.
There are no restrictions to the content of the plugin except the first line should be a specific comment,
so you are able to create functions and batch-dlls. An example of how it could be implemented is (SampleMain.bat, Sample.dll.bat).

penpen

Edit: Added the "one Interface" line within the Definition of "an existing application".
On re-read i came to the conclusion that the call command could be seen as only one interface.

Adrianvdh
Posts: 177
Joined: 16 May 2013 13:00

Re: Batch Simple Plugin System Consept

#23 Post by Adrianvdh » 08 Jan 2014 18:21

penpen wrote:
Adrianvdh wrote:So you better divide the functionality:
- one or multiple "dll-batch" files to be called from other batches (main and plugins) to execute the "dll-batch"'s functions
- a main batch file that manages plugins, and may call functions from dll-batches
- plugin.bat that is called from main, and may call functions from dll-batches


So basically dll-batch stores the functions for main.bat to use i.e. 'getWinKey'. And then 'main.bat' will call the functions from dll-batch, so dll-batch is a plugin itself, then 'plugin.bat' itself is an addin? But also 'plugin.bat' has access to dll-batch and vice versa, but plugin.bat has no access to 'main.bat'?

That is some what I got from that.

Sorry to ask you this, but could you simplify your description and explain again, also the code :)
I just don't understand you can't we have 'main.bat' and *'addin.bat'* both are BiDi?

Ed Dyreen
Expert
Posts: 1569
Joined: 16 May 2011 08:21
Location: Flanders(Belgium)
Contact:

Re: Batch Simple Plugin System Consept

#24 Post by Ed Dyreen » 08 Jan 2014 20:29

Adrianvdh wrote:1. The main batch file could be under license and modification is denied,
Very odd, the whole reason for batch to come into existance was to allow administrators to chain commands to allow repetitive tasks to be automated. By design open source to allow modifications be made easily. Intended for administrators with no background in programming.

A modification denial license seems impractical.

Aacini
Expert
Posts: 1913
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: Batch Simple Plugin System Consept

#25 Post by Aacini » 09 Jan 2014 12:12

I think that the core purpose of this forum is to solve Batch file programming problems, not the academic definitions of terms...

If someone have a Batch programming problem, he/she should describe the problem and ask for a solution. In my opinion, the OP should not describe an abstract method that could be used as a possible solution for a non stated problem and request "to build a system like this" in a batch file, then urge the community for answers to finally said "I realize I am truly mistaken" :!:

(I apologize for grumbling, but I feel that I wasted my valuable time in this matter... :evil: )

Antonio

penpen
Expert
Posts: 2009
Joined: 23 Jun 2013 06:15
Location: Germany

Re: Batch Simple Plugin System Consept

#26 Post by penpen » 09 Jan 2014 14:07

Adrianvdh wrote:So basically dll-batch stores the functions for main.bat to use i.e. 'getWinKey'. And then 'main.bat' will call the functions from dll-batch,
Yes.

Adrianvdh wrote:so dll-batch is a plugin itself,
No, the dll-batch is not an optional modul, so it is no addon and no plugin.

Adrianvdh wrote:then 'plugin.bat' itself is an addin?
No, in the upper case it is a hybridid type: Addon and plugin.
If it won't access the dll-batch then it were a pure plugn.

Adrianvdh wrote:But also 'plugin.bat' has access to dll-batch,
Yes, although, i think you've meant access to the dll-batches functions: Even in that case: Yes.

Adrianvdh wrote:and vice versa,
If you meant to the files: Yes, as it knows there may be files in the plugins menue.
If you meant its functions, then it is a clear NO:
How should the dll-batch predict which functions will be implemented in which addons/plugins somewhen in future?
As the functionality was split the dll-batch cannot access any functions from the application (except the Services it defines):
So the dll-batch cannot access the plugin system.

Adrianvdh wrote:but plugin.bat has no access to 'main.bat'?
The plugin.bat has access to the main.bat, but not to it's functions.

Adrianvdh wrote:Sorry to ask you this, but could you simplify your description and explain again, also the code :)
The dll-batch creates some stand alone services, and exports it to any other applications;
you can call them somehow.
The main batch offers a plugin system (defined by the contract), and an addon system (batch-dll).
All plugins must fulfill the contract, and therefore they must be accessable via the pre defined interface.

In the above example system:
You can list all exported functions of the "Sample.dll.bat" via "Sample.dll.bat /list".
You can call an exported function of the "Sample.dll.bat" via "Sample.dll.bat /call functionName parameters]".

Of course the SampleMain.bat works standalone, but it allows to replace some functions by an addon or plugin: Pluginable1, Pluginable2, Pluginable3.
Therefore it calls these functions via "call :callfunction :Pluginable1, parameters".

The plugin contract is defined by:
- the plugins must have "@rem " as the first 4 bytes followed by "MY_BATCH_FILE_NAME_TEXT " followed by the name of the function that should be replaced followed by <\r\n>.
- the plugins have to be be placed within the SampleMain.bat's subfolder named "plugins"
The interface is not mentioned; but the plugin is assumed to be a batch file, so it naturally offers this interface:
Must be callable using the call command.

So "plugin\SamplePlugin.bat" is the only accepted plugin in the example, as ist first line is: "@rem MY_BATCH_FILE_NAME_TEXT :Pluginable2";
This plugin will replace the function ":Pluginable2", so the main calls "plugin\SamplePlugin.bat" instead: The params weren't changed.

In this case the main and dll-batch were bundled, so it is technically no plugin system, but a hybrid addon/plugin system.
If the dll were not bundled, then the above system is a pure plugin system.

Adrianvdh wrote:I just don't understand you can't we have 'main.bat' and *'addin.bat'* both are BiDi?
Then you don't have a plugin: I hadn't invented the definitions above.
I can't remember who has created this definitions, but i think it is an organization like IEEE, VDE, or ... .

Just a side note:
I think Aacinis first system is more what you wanted to do, or to be more precise
i think you wanted to create some sort of temporarily self patching system:
Even this should be possible with batch, but you must limit how the batch files were created:
For example all lines must be readable using "set /P" and writebale via "echo", so no line is allowed to end with a special key (tab, escape) and other limitations.
Another options (much more easier to implement, but not only a little bit odd) were to save the main batch file within a subfolder "data", but splitted in single lines.
And then use a kind of header (first line should contain all needed infos of the plugin) so you can use copy /B "data\main.1" + /B "data\main.2", ... .
The problem in doing it in another way, if you want to implement it in batch is,
that you have to read it multiple times and analyze it to build a patched main version,
in addition you have to somehow marked added code, so you are able to unpatch it at the end.
Another problem is, when running multiple instances you must (somehow) ensure,
that no other running instance is affected (DEFINITELY N O T a trivial system.

penpen

Adrianvdh
Posts: 177
Joined: 16 May 2013 13:00

Re: Batch Simple Plugin System Concept

#27 Post by Adrianvdh » 10 Jan 2014 06:16

So penpen?
What does your system do exactly?
OK, there is a 'main.bat' that has access to "batch-dll.bat" and we have 'plugin.bat'

So "batch-dll.bat" stores all of 'main.bat''s functions. But then we have 'plugin.bat' which has access to "batch-dll.bat"'s functions.

So how would 'plugin.bat' affect 'main.bat' I don't understand.

penpen
Expert
Posts: 2009
Joined: 23 Jun 2013 06:15
Location: Germany

Re: Batch Simple Plugin System Concept

#28 Post by penpen » 10 Jan 2014 14:13

I'm not sure if i understand what you are meaning:
The application consists of the main batch (SampleMain.bat) and the batch-dll (Sample.dll.bat).
The batch-dll contains the services of the application (not all functions from SampleMain.bat: Only all public functions of the application).
The application also allows to call plugins to be called instead of the functions: Pluginable1, Pluginable2, Pluginable3.

A batch file (*.bat) within the folder "plugins" that should be called instead of the function Pluginable 2 has to use this first line.

Code: Select all

@rem MY_BATCH_FILE_NAME_TEXT :Pluginable2

The main batch stores the calls to the plugin functions in a variable:

Code: Select all

set "PluginFunctions[1]=call :Pluginable3"
set "PluginFunctions[2]=call :Pluginable1"
set "PluginFunctions[3]=call :Pluginable2"
The main batch (containing the plugin manager/loader) scans all first lines of the files in the folder "plugins".
If it find such a line in one of those batch files (plugins) then it replaces the calls, in this example:

Code: Select all

set "PluginFunctions[3]=call SamplePlugin.bat"

The main batch encapsulates the calls to the Pluginable functions via:

Code: Select all

call :callfunction :Pluginable2, default 2 n
This has the reasons that it you can extend any function easily to a plugin function,
by adding ":callfunction " to all of their calls via replace functionality of most editors:
for example: Ctrl+H in edit.

penpen

penpen
Expert
Posts: 2009
Joined: 23 Jun 2013 06:15
Location: Germany

Re: Batch Simple Plugin System Concept

#29 Post by penpen » 10 Jan 2014 14:16

Hiho,

Adrianvdh wrote:So penpen?
What does your system do exactly?
OK, there is a 'main.bat' that has access to "batch-dll.bat" and we have 'plugin.bat'

So "batch-dll.bat" stores all of 'main.bat''s functions. But then we have 'plugin.bat' which has access to "batch-dll.bat"'s functions.

So how would 'plugin.bat' affect 'main.bat' I don't understand.
Please ask more specific, as i only could guess what you wanted to know, and
i'm not sure if i have answered your question with my post.

Regards,
penpen.

Adrianvdh
Posts: 177
Joined: 16 May 2013 13:00

Re: Batch Simple Plugin System Concept

#30 Post by Adrianvdh » 10 Jan 2014 14:36

Ahh, Sorry but I am really frustrated about this because I am struggling to understand.
So the "batch-dll" stores all the public functions OF 'main.bat'.
So a plugin can replace a function within the "batch-dll"?

In your system, what does this main exactly?:

Code: Select all

call :callfunction :Pluginable2, default 2 n

The "default 2 n" because I don't know.

And does the 'main.bat' have to be aware of the plugin? Because it seem to only support 3 plugins "Pluginable1, Pluginable2, Pluginable3"

I am sorry, this must be irritable and frustrating for you to, really. But I am having a hard time to understand this. Thank you for
being so patent with me :)

Regards,
Adrian

Post Reply