# opsi-script Local Functions

### uib gmbh

## opsiscript Local Functions

## Was ist das Problem

Wiederverwendbarkeit von Code wird beschränkt durch:

- Globale Variablen
- Räumliche Trennung von Aufruf und Deklarartion von Sektionen

## Vorstellung: Local Functions in opsi-script

Seit Version 4.12 kennt opsi-script auch lokale Funktionen.

Ein Beispiel:

    DefFunc myFunc(val $str1$ : string, $str2$ : string) : string
            set $result$ = $str1$ + $str2$
    endfunc

## Konzept

Ziel dieser Erweiterung ist die Umsetzung folgender Konzepte:

- Funktionen mit Rückgabewert
- Frei definierbare Aufrufparameter
- Lokale Variablen
- Geschachtelte Funktionen
- Rekursive Aufrufe
- Primäre und sekundäre Sektionen innerhalb von Funktionen

## Funktionen mit Rückgabewert:

Die Funktionen haben einen Rückgabewert
welcher vom Typ `string` oder `stringlist` ist.

Der Aufruf eine solchen Funktion kann überall da erfolgen,
wo ein Stringausdruck bzw.eine Stringliste erwartet wird.

## Frei definierbare Aufrufparameter:

Einer Funktion können Parameter übergeben werden.

- Diese Parameter werden bei der Deklaration der Funktion definiert.
  `DefFunc myFunc(val $str1$ : string, ref $list$ : stringlist) : string`
- Die Aufrufparameter können vom Typ `string` oder `stringlist` sein.
- Die Aufrufparameter stehen innerhalb der Funktion als lokale Variablen zur Verfügung.
- Die Aufrufparameter können als CallByValue oder per callByReference übergeben werden.

## CallByValue:

CallByValue ist der Default.

- Das bedeutet: wird keine Aufrufmethode explizit angegeben, so wird CallByValue verwendet.
- Soll CallByValue explizit angegen werden, so erfolgt dies über das Schlüsselwort `val`.
- CallByValue bedeutet, das beim Aufruf der Inhalt einer beim Aufruf verwendeten Variable auf die Aufrufvariable kopiert wird.
  Es wird also der Wert übergeben
- Ein CallByValue Parameter kann mit einer Variable, Konstanten oder Funktion aufgerufen werden.
- Ein Variable welche zum Aufruf verwendet wird, kann durch die Funktion nicht geändert werden

## CallByReference:

CallByReference ist nicht der Default.

- CallByReference muß über das Schlüsselwort `ref` explizit angegeben werden.
- CallByReference bedeutet, dass beim Aufruf eine Verbindung zwischen der aufrufenden Varibalen und des lokalen Aufrufparameters erstellt wird.
- Eine Änderung der lokalen Variable des Aufrufparameters, wirkt sich direkt auf die beim Aufruf verwendete Variable aus.
- Ein CallByReference Parameter kann nur mit einer Variable aber nicht mit einer Konstanten oder Funktion aufgerufen werden.

## Lokale Variablen:

Eine Funktion enthält lokale Variablen.

- Implizit gibt es die Aufrufparameter als lokale Variablen.
- Implizit gibt es die Variable `$result$` welche vom Typ des Rückgabewertes ist.
- Darüberhinaus können weitere Variablen innerhalb der Funktion definiert werden.
- All diese Variablen sind lokal, d.h. sie sind nur innerhalb dieser Funktion sichtbar.
- Eine lokale Variable mit dem selben Namen einer globalen Variable, verdeckt innerhalb der Funktion die entsprechende globale Variable.

## Geschachtelte Funktionen:

- Eine lokale Funktion kann wiederum eine oder mehrere Definitionen von lokalen Funktionen enthalten.
- Diese Funktionen sind nur innerhalb der Funktion sichtbar in der sie definiert sind.

## Rekursive Aufrufe:

- Eine Funktion kann sich selbst rekursiv aufrufen.

## Sektionen innerhalb von Funktionen:

- Der Funktionskörper kann eigene Sektionen enthalten.
- Dies können sowohl primare (sub) Sektionen sein, als auch sekundäre (Files, Registry, …) Sektionen.
- Diese sind lokal zu dieser Funktion also nur innerhalb der Sektion sichtbar.

## Syntax

Definition

    DefFunc <func name>([calltype parameter type][,[calltype parameter type]]) : type
    <function body>
    endfunc

## Dabei ist:

- `DefFunc` das Schlüsselwort zur Beginn der Definition einer lokalen Funktion.
- <func name> der frei gewählte Name der Funktion.
- calltype ist der Aufruftyp [`val` | `ref`]. Wird kein Aufruftyp angegeben, + so gilt `val` als gesetzt.
- parameter ist der freigewählte Name des Aufrufparameters, welcher unter diesem Namen innerhalb der Funktion als lokale Variable zur Verfügung steht.
- type ist der Datentyp des Parameters bzw. der Funktion und entweder `string` oder `stringlist`;

- <function body>: ist der Körper der Funktion,
  welcher dem opsi-script syntax genügen muß.
  In diesem Teil gibt es die automatisch erzeugte lokale Variable `$result$`, welche den Datentyp der Funktion hat (String/Stringliste) und dazu dient den Rückgabewert aufzunehmen.
- `endfunc` zeigt als Schlüsselwort das Ende einer Funktionsdefinition an.

## Beispiel 1

Einfache Funktion welche zwei Strings miteinander verbindet:
(defined_functions0.opsiscript)

    [actions]
    DefVar $mystr$
    DefVar $str1$
    set $str1$ = 'ha'

    DefFunc myFunc(val $str1$ : string, $str2$ : string) : string
            set $result$ = $str1$ + $str2$
    endfunc

    set $mystr$ = myFunc("he","ho")
    set $mystr$ = myFunc("he",timeStampAsFloatStr)
    set $mystr$ = myFunc("he",$str1$)

Erwartete Ergebnisse:

- heho
- he42921.809
- heha

## Beispiel 2

Funktion vom Type `stringlist` welcher ein `string` und eine `stringlist` übergeben werden:
(defined_functions1.opsiscript)

    [actions]
    DefVar $mystr$
    DefVar $str1$
    DefStringlist $list1$
    DefStringlist $list2$

    set $str1$ = 'ha'

    DefFunc myFunc1(val $str1$ : string, $list1$ : stringlist) : stringlist
            set $result$ = createStringlist($str1$ , takeString(2,$list1$))
    endfunc

    set $list2$ = splitstring("/etc/opsi/huhu","/")
    set $list1$ = myFunc1("hi",$list2$)

Erwartete Ergebnisse:

- $list1$ = [hi,opsi]

## Beispiel 3

Funktion vom Type `string` welcher ein `string` übergeben wird:
(defined_functions2.opsiscript)

    [actions]

    DefFunc myFunc2($str1$ : string) : string
            if $str1$ > "0"
                    set $result$ = "true"
            else
                    set $result$ = "false"
            endif
    endfunc

    if stringtobool(myfunc2("1"))
            comment "true"
    else
            comment "false"
    endif

Erwartete Ergebnisse:

- true

## Beispiel 4

Funktion vom Type `string` welcher ein `string` übergeben wird mit lokaler Variable:
(defined_functions3.opsiscript)

    [actions]
    DefVar $mystr$

    DefFunc myFunc3($str1$ : string) : string
            DefVar $locstr1$
            set $locstr1$ = '123'
            set $result$ = $locstr1$ + $str1$
    endfunc

    set $mystr$ = myFunc3("he")

Erwartete Ergenisse:

- 123he

## Beispiel 5

Funktion vom Type `string` welcher ein `string` übergeben wird mit lokaler Variable und geschachtelter Funktion:
(defined_functions4.opsiscript)

    [actions]
    DefVar $mystr$

    DefFunc myFunc4($str1$ : string) : string
            DefVar $locstr1$

            DefFunc myFunc5($str1$ : string) : string
                    set $result$ = 'inner' + $str1$
            endfunc

            set $locstr1$ = '123'
            set $result$ = $str1$ + myFunc5($locstr1$)
    endfunc

    set $mystr$ = myFunc4("outer")

Erwartete Ergebnisse:

- outerinner123

## Beispiel 6

Einfache Funktion vom Type `string` welcher ein `string` by reference übergeben wird mit lokaler Variable:
(defined_functions6.opsiscript)

    [actions]
    DefVar $mystr$
    DefVar $str1$
    DefVar $str2$

    set $str1$ = 'ha'
    set $str2$ = 'hi'

    DefFunc myFunc6(ref $str1$ : string) : string
            DefVar $locstr1$
            set $locstr1$ = '123'
            set $str1$ = 'setinlocal'
            set $result$ = $locstr1$ + $str1$
    endfunc

    set $mystr$ = myFunc6($str2$)
    set $mystr$ = $str1$ + $str2$

Erwartete Ergebnisse:

- 123setinlocal
- hasetinlocal

## Beispiel 7

Funktion vom Type `stringlist` welcher eine Variable vom Type `stringlist` mit call by reference übergeben wird mit lokalen `stringlist` Variable:
(defined_functions7.opsiscript)

    [actions]
    DefVar $mystr$
    DefStringlist $list1$
    DefStringlist $list2$

    set $list2$ = splitstring("/etc/opsi/huhu","/")

    DefFunc myFunc7(ref $list1$ : stringlist) : stringlist
            DefStringlist $loclist1$
            set $loclist1$ = splitstring("/a/b/c","/")
            set $list1$ = createStringList('setinlocal')
            set $loclist1$ = addListToList($loclist1$,$list1$)
            set $result$ = $loclist1$
    endfunc

    set $list1$ = myFunc7($list2$)
    comment "$list2$ index 0: " + takestring(0,$list2$)

Erwartete Ergenisse:

- $list1$ = [,a,b,c,setinlocal]
- setinlocal

## Beispiel 8

Funktion vom Type `stringlist` welcher ein `string` übergeben wird mit lokaler Variable und lokaler sekundärer Sektion:
(defined_functions8.opsiscript)

    [actions]
    DefStringlist $list1$

    DefFunc myFunc8($str1$ : string) : stringlist
            DefStringlist $loclist1$
            set $loclist1$ = getoutstreamfromsection("shellInAnIcon_test")
            set $result$ = $loclist1$

            [shellinanicon_test]
            set -x
            $str1$
    endfunc

    if GetOS = 'Linux'
            set $list1$ = myFunc8('set -x ; pwd')
    else
            set $list1$ = myFunc8('echo %cd%')
    endif

Erwartete Ergebnisse: Linux: $list1$ = [+ pwd, /home/uib/gitwork/lazarus/opsi-script]

Windows:

    The value of the variable "$list1$" is now:
    (string   0)
    (string   1)W:\opsi-script-test\CLIENT_DATA\standalone-tests>echo W:\opsi-script-test\CLIENT_DATA\standalone-tests
    (string   2)W:\opsi-script-test\CLIENT_DATA\standalone-tests

## opsi-script: Libraries of Local Functions

Hier geht es um die Auslagerung von local functions in externe Dateien (libraries) und um die Möglichkeit diese aus unterschiedlichen Scripten heraus aufzurufen.

## Syntax

    importLib <string expr>

Dabei ist `<string expr>`:

    <file name>[.<file extension>][::<function name>]

- Wenn keine .<file extension> (Dateierweiterung) übergeben wird, so wird `.opsiscript` als Default verwendet.
- Wenn kein ::<function name> über geben wird, so werden alle Funktionen der angegebenen Datei importiert.

<file name> ist:

- Ein kompletter Pfad zu einer Datei. [W/L]
- Eine Datei in `%ScriptPath%` [W/L]
- Eine Datei in `%opsiScriptHelperPath%\lib` [W]
  Entspricht: %ProgramFiles32Dir%\opsi.org\opsiScriptHelper\lib
- Eine Datei in `%ScriptPath%/../lib` //since 4.11.5.2 [W/L]
- Eine Datei in `%WinstDir%\lib` [W]

Die Prüfung erfolgt in dieser Reihenfolge. Die erste Datei die gefunden wird, wird genommen.

## Beispiel 1

Einfache Funktion welche zwei Strings miteinander verbindet:

Die Library Datei myfirstlib.opsiscript:

    DefFunc myFunc(val $str1$ : string, $str2$ : string) : string
            set $result$ = $str1$ + $str2$
    endfunc

Das Programm:
(defined_functions_lib1.opsiscript)

    [actions]
    DefVar $mystr$
    DefVar $str1$

    importLib "%scriptpath%\myfirstlib.opsiscript"

    set $str1$ = 'ha'

    set $mystr$ = myFunc("he","ho")
    set $mystr$ = myFunc("he",timeStampAsFloatStr)
    set $mystr$ = myFunc("he",$str1$)

Erwartete Ergebnisse:

- heho
- he42921.809
- heha

## importlib Varianten

Die Zeile
`importLib "%scriptpath%\myfirstlib.opsiscript"`
könnte auch folgende Form haben:

- `importLib "myfirstlib.opsiscript"`
  denn `%scriptpath%` ist im Suchpfad;
- `importLib "myfirstlib"`
  denn die extension `.opsiscript` ist der Default;
- `importLib "myfirstlib.opsiscript::myFunc"`
  Würde gezielt nur diese Funktion importieren.

## opsi-script: Local Functions / Libraries: Missing Features

Folgende bekannte Wünsche sind momentan noch offen:

- proceduren (void functions)
  Funktionen die keinen Rückgabewert haben
- function()
  Funktionen die keine Parameter haben
- boolean functions
  Funktionen die (true / false) zurückliefern
  Workaround: Stringfunktion welche ("true"/"false") zurückliefert und diese mit stringToBool() wandeln.

- config: debug_lib
  Das Ausschalten des Logging für Libraries funktioniert noch nicht
- syntax Fehlermeldungen mit richtiger Quellenangabe
  Bei Syntax Fehlern in Libraries wird noch nicht die korrekte Zeile in der korrekten Datei angezeigt
- importlib innerhalb von functions
  Lädt momentan die Library als lokale Funktion

## opsi-script: UIB Library of Local Functions

opsi-script Library im opsi-winst Paket

Seit opsi-script 4.12.0.13 enthält das Unterverzeichnis `lib` Librarydateien welche von Standard opsi Produkten verwendet werden:

- uib_backend.opsiscript
- uib_bootutils.opsiscript
- uib_strlistutils.opsiscript
- uib_exitcode.opsiscript

Diese Library wird direkt von uib gepflegt und dient hauptsächlich für interne Zwecke.

## Project: Community opsi-script Library

Wir starten hiermit den Aufbau einer öffentlichen opsi-script Bibliothek die zum Austauch innerhalb der opsi Community dienen soll.

## Übersicht und Anforderungen

- Struktur: Gliederung in Dateien nach Funktionsgruppen
- Namespace
- Dokumentation der Funktionen
- Zentrale Versionskontrolle, Bereitstellung und Download
- Test der Funktionen

## Gliederung nach Funktionsgruppen

- Gliederung in Dateien nach Funktionsgruppen
- Vorschläge ?

## Namespace

Namespace:

- Ob und wie sollen die Library-Dateien am Namen erkennbar sein ?
  z.B. opsi als Post- oder Prefix ?
- Ob und wie sollen die Funktionen aus dieser Library am Namen erkennbar sein ?
  z.B. opsi als Post- oder Prefix ?

## Dokumentation der Funktionen

Damit Funktionen einer Library auch verwendet werden, müssen sie nach Funktion und Syntax auffindbar sein.

- D.h. sie müssen dokumentiert und in einer zentralen Dokumentation aufgefürt sein.
- Dazu wird zur Zeit ein Werkzeug entwickelt, welches auf Basis von Kommentaren innerhalb der Funktion eine Dokumentation in den Formaten HTML, PDF, ASCIIDOC erstellen kann.

## Zentrale Versionskontrolle, Bereitstellung und Download

- Wir planen die Bereitstellung der Library in unserem öffentlichen Versionskontrollsystem GIT.
- Zur Verteilung soll die Library natürlich auch als opsi-Paket Bereitgestellt werden

## Test der Funktionen

Für die Funktionen müssen Tests bereit stehen:

- Diese sind als Beispiele der Dokumentation hilfreich
- Überprüfung für die Entwicklenden der Funktionen
- Überprüfung für die zentrale opsi-script Entwicklung

← →

/

Go to slide: #