Funzioni per la gestione della command line

PARSEOPT.H

/******************************************************************************

    Barninga_Z! - OPTLIB

    filename - PARSEOPT.H

        getswitch()    - legge lo switch character DOS di default
        parseopt()     - analizza e memorizza le opzioni (low level)
        parseoptions() - analizza e memorizza le opzioni
        setswitch()    - stabilisce lo switch character DOS di defaul

******************************************************************************/

#ifndef __PARSEOPT__

#define  ILLEG_S           "Illegal option"	// : nella cmd line
#define  ERROR_S           "Unknown option"	// opzione non in optionS
#define  ERRCHAR           ((char)-1)	// opzione errata

struct OPT {	// usato per le opzioni, gli argomenti e i valori di convalida
    char opt;	// restituiti dalle funzioni. A storeopt() deve essere passato
    char *arg;	// un puntatore ad una struttura OPT. Se e' invocata
    int val;	// parseopt() l'array di strutture e' allocato in modo
};	// automatico e ne e' restituito il puntatore.

struct VOPT {	// template per array strutture contenenti le opzioni e i
    char opt;	// puntatori alle funzioni di validazione
    int (*fun)(struct OPT *tmp,int no);
};


int cdecl getswitch(void);
struct OPT *cdecl parseopt(int argc,char **argv,char *optionS,char sw,char 
	*illegalS, 
	char *errorS,struct VOPT valfuncs[]);
struct OPT *cdecl parseoptions(int argc,char **argv,char *optionS,
	struct VOPT valfuncs[]);
int cdecl setswitch(char sw);

#define __PARSEOPT__
#endif

/****************** FINE DEL FILE PARSEOPT.H ************************************/

OK, OK, ne ho avuto abbastanza! Torniamo alla lettura...

Funzioni di libreria

/******************************************************************************

    Barninga_Z! - OPTLIB

    file - parseopt.c

    funzioni:
    
        getswitch    - legge lo switch character DOS di default
        gopError     - ritorna da storeopt() se vi e' un errore nella command line
        parseopt     - analizza e memorizza le opzioni (low level)
        parseoptions - analizza e memorizza le opzioni
        setswitch    - stabilisce lo switch character DOS di default
        storeopt     - analizza e memorizza le opzioni (internal only)

******************************************************************************/

#pragma  warn -pia	// evita warning per assegnamenti con test implicito
 
#include <alloc.h>
#include <string.h>

#include "parseopt.h"

#define  EINVFNC           1	// Invalid function number
#define  EINVAL            19	// Invalid argument

// i prototipi di storeopt() e gopError() sono qui e non in PARSEOPT.H perche'
// si tratta di funzioni che l'utente non ha bisogno di chiamare direttamente
// in pratica si tratta di routines di servizio per le funzioni high-level

int cdecl gopError(struct OPT *cmd);
int cdecl storeopt(int argc,char **argv,char *optionS,char sw,struct OPT *cmd,
	char *illegalS,char *errorS);


int pascal __IOerror(int dosErr);	// funzione di libreria; non documentata

static int optind;	// indice della prossima opzione. E' inzializzata a 1 in 
	// ingresso ed utilizzata da storeopt(). Deve essere
	// riazzerata in uscita per consentire chiamate multiple a
	// parseopt()


/*----------------------------------------------------------------------------*

getswitch()        Restituisce lo switch character di default

SINTASSI     int cdecl getswitch(void);

INCLUDE      parsopt.h

PARAMETRI    Nessuno.

SCOPO        Ottiene il  carattere che  il DOS  abilita per  default ad
             introdurre gli  switches (opzioni)  sulla command line dei
             programmi. Se  è usata  per valorizzare il parametro sw di
             parseopt() o  storeopt(), tutte  le opzioni  sulla command
             line devono  essere precedute  dal carattere restituito da
             getswitch(). E' inoltre invocata da parseoptions().

RESTITUISCE  -1    funzione non supportata dal DOS.

             altro lo switch character di default per il DOS.

SORGENTE     getdossw.c

NOTE         Nessuna.

*----------------------------------------------------------------------------*/

int cdecl getswitch(void)
{
    asm {
        mov ax,0x3700;
        int 0x21;
        cmp al,0xFF;
        je notsupported;
        mov al,dl;
        xor ah,ah;
        jmp endfunc;
    }
notsupported:
    _AX = __IOerror(EINVFNC);
endfunc:
    return(_AX);
}


/*----------------------------------------------------------------------------*

gopError()         Ritorna da storeopt() in caso di errore - INTERNAL

SINTASSI     int cdecl gopError(struct OPT *cmd);

INCLUDE      parsopt.h

PARAMETRI    cmd           puntatore   all'elemento    dell'array    di
                           strutture OPT  (template in  storeopt.h) nel
                           quale l'opzione verrebbe memorizzata.

SCOPO        Ritorna da  storeopt() quando  questa individua  un errore
             nella command  line (es.:  opzione sconosciuta). Memorizza
             il valore  -1 nel  campo  val  della  struttura  e  chiama
             __IOerror().

RESTITUISCE  l'opzione (anche se non valida).

SORGENTE     storeopt.c

NOTE         Il programma non deve invocare questa funzione; essa è una
             funzione di servizio per storeopt().

*----------------------------------------------------------------------------*/

static int cdecl gopError(struct OPT *cmd)
{
    cmd->val = __IOerror(EINVAL);
    return((int)cmd->opt);
}


/*----------------------------------------------------------------------------*

parseopt()         Analizza la command line - LOW LEVEL

SINTASSI     int cdecl parseopt(int argc,char **argv,char *optionS,char sw,
	char *illegalS,char *errorS,struct VOPT *valfuncs);

INCLUDE      parsopt.h

PARAMETRI    argc          numero di  parametri sulla command line + 1.
                           E' generalmente argc parametro di main().

             argv          array  di   puntatori  ai   parametri  della
                           command line. E' generalmente argv parametro
                           di main().

             optionS       puntatore alla  stringa contenente  tutti  i
                           caratteri    opazione    riconosciuti    dal
                           programma.

             sw            lo  switch   character  che  introduce  ogni
                           opzione (o gruppo di opzioni).

             illegalS      puntatore alla  stringa  rappresentatnte  il
                           messaggio di  errore corrispondente  all'uso
                           non lecito  dei due  punti (:) nella command
                           line.

             errorS        puntatore  alla   stringa  utilizzata   come
                           messaggio  di   errore  per  un'opzione  non
                           compresa in optionS.

             valfuncs      puntatore  ad   un  array   di  struct  VOPT
                           (template definito in parsopt.h). L'array ha
                           un elemento per ogni opzione che si desidera
                           testare o  convalidare. Ogni  elemento è una
                           struct VOPT, la quale ha due campi: il primo
                           (char opt) contiene il carattere-opzione; il
                           secondo  (int   (*fun)(struct  OPT   *,int))
                           contiene il puntatore alla funzione che deve
                           essere invocata  per testare l'opzione. Tale
                           funzione  deve   essere  di   tipo  int   ed
                           accettare  due  parametri,  un  puntatore  a
                           struct OPT  (template  in  parsopt.h)  e  un
                           int).  Il   primo   punta   alla   struttura
                           valorizzata    da    storeopt()    estraendo
                           l'opzione dalla  command  line,  il  secondo
                           rappresenta la  posizione dell'opzione nella
                           command  line.  L'array  DEVE  avere,  quale
                           ultimo elemento,  una struct  VOPT in cui il
                           campo opt  è NULL:  la funzione  puntata dal
                           campo  fun   viene  invocata   per   ciascun
                           parametro   non-opzione   incontrato   sulla
                           command line.  Inoltre, se  il campo  opt di
                           uno  qualunque   degli  elementi  dell'array
                           contiene  il  carattere  ERRCHAR (parsopt.h)
                           la funzione puntata dal corrispondente campo
                           fun   viene   invocata    quando   l'opzione
                           restituita  da storeopt() è  sconosciuta  o,
                           comunque, in caso di errore.

SCOPO        Scandisce e  memorizza  in  un  array  di  struct  VOPT  i
             parametri incontrati  sulla command  line, per la sintassi
             della quale  si  veda  storeopt().  La  parseopt()  invoca
             storeopt() finché  tutte i  parametri della  command  line
             sono stati  scanditi (opzionalmente testati) e memorizzati
             nell'array. Alloca  automaticamente memoria  per  l'array,
             che al termine contiene una struct OPT per ogni parametro.

RESTITUISCE  NULL  in caso di errore di allocazione.


SORGENTE     parsopll.c

NOTE         I  campi   della  prima   struct  OPT   dell'array   hanno
             significati particolari:  il campo  opt contiene  l'indice
             del  (elemento   dell'array   corrispondente   al)   primo
             parametro non-opzione  nella command  line, se ve ne sono;
             il  campo arg contiene argv[0];  il campo  val contiene il
             numero totale di argomenti non-opzione nella command line.
             Ancora,  il  campo  opt  delle  struct   OPT   relative  a
             parametri  non-opzione sono  valorizzati così:  0  per  il
             primo trovato, 1 per il secondo, etc..

*----------------------------------------------------------------------------*/

struct OPT *cdecl parseopt(int argc,char **argv,char *optionS,char sw,
	char *illegalS,char *errorS,struct VOPT valfuncs[])
{
    struct OPT *cmd = NULL, *tmp = NULL;
    int no, i, fl, carg;
    char option;
    extern int optind;

    if(!(cmd = (struct OPT *)realloc(cmd,sizeof(struct OPT))))
        return(NULL);
    optind = 1;
    for(no = 1;(carg = storeopt(argc,argv,optionS,sw,cmd,illegalS,errorS)) > 0;
	no++) {
        if(!(tmp = (struct OPT *)realloc(cmd,(no+1)*sizeof(struct OPT)))) {
            optind = 0;
            return(NULL);
        }
        cmd = tmp;
        (tmp += no)->opt = cmd->opt;
        tmp->arg = (*cmd->arg == ':') ? cmd->arg + 1 : cmd->arg;
        option = (!(tmp->val = cmd->val)) ? tmp->opt : ERRCHAR;
        for(i = 0; (valfuncs+i)->opt; i++)
            if((valfuncs+i)->opt == option)
                if((valfuncs+i)->fun)
                    tmp->val = (*((valfuncs+i)->fun))(tmp,no);
    }
    cmd->opt = no;
    cmd->arg = argv[0];
    cmd->val = 0;
    for(i = 0; (valfuncs+i)->opt; i++);
    for(fl = 0, carg = -carg; carg < argc; carg++) {
        if(!(tmp = (struct OPT *)realloc(cmd,(no+1)*sizeof(struct OPT))))
            free(cmd);
        cmd = tmp;
        (tmp += no++)->opt = (char)cmd->val++;
        tmp->arg = (*argv[carg] == sw && !fl) ? fl++, argv[carg] + 1 : argv[carg];
        tmp->val = ((valfuncs+i)->fun) ? (*(valfuncs+i)->fun)(tmp,carg) : 0;
    }
    optind = 0;
    return(cmd);
}


/*----------------------------------------------------------------------------*

parseoptions()     Analizza i parametri della commnad line

SINTASSI     int cdecl parseoptions(int argc,char **argv,
	char *optionS,struct VOPT *valfuncs);

INCLUDE      parsop.c

PARAMETRI    argc          numero di  parametri sulla command line + 1.
                           E' generalmente argc parametro di main().

             argv          array  di   puntatori  ai   parametri  della
                           command line. E' generalmente argv parametro
                           di main().

             optionS       puntatore alla  stringa contenente  tutti  i
                           caratteri    opazione    riconosciuti    dal
                           programma.

             valfuncs      puntatore  ad   un  array   di  struct  VOPT
                           (template definito in parsopt.h). L'array ha
                           un elemento per ogni opzione che si desidera
                           testare o  convalidare. Ogni  elemento è una
                           struct VOPT, la quale ha due campi: il primo
                           (char opt) contiene il carattere-opzione; il
                           secondo  (int   (*fun)(struct  OPT   *,int))
                           contiene il puntatore alla funzione che deve
                           essere invocata  per testare l'opzione. Tale
                           funzione  deve   essere  di   tipo  int   ed
                           accettare  due  parametri,  un  puntatore  a
                           struct OPT  (template  in  parsopt.h)  e  un
                           int).  Il   primo   punta   alla   struttura
                           valorizzata    da    storeopt()    estraendo
                           l'opzione dalla  command  line,  il  secondo
                           rappresenta la  posizione dell'opzione nella
                           command  line.  L'array  DEVE  avere,  quale
                           ultimo elemento,  una struct  VOPT in cui il
                           campo opt  è NULL:  la funzione  puntata dal
                           campo  fun   viene  invocata   per   ciascun
                           parametro   non-opzione   incontrato   sulla
                           command line.  Inoltre, se  il campo  opt di
                           uno  qualunque   degli  elementi  dell'array
                           contiene  il  carattere  ERRCHAR (parsopt.h)
                           la funzione puntata dal corrispondente campo
                           fun   viene   invocata    quando   l'opzione
                           restituita  da storeopt() è  sconosciuta  o,
                           comunque,  in  caso   di   errore.    Vedere
                           storeopt() circa la struct OPT.

SCOPO        Analizza la  command line e ne memorizza i parametri in un
             array di  struct OPT  dopo averli (opzionalmente) testati.
             Vedere storeopt()  e parseopt().  Invoca  parseopt()  dopo
             avere valorizzato  il parametro sw con lo switch character
             di default  del DOS (getswitch()), illegalS con la stringa
             "Illegal option" e errorS con la stringa "Unknown option".

RESTITUISCE  NULL  in caso di errore di allocazione.


SORGENTE     parsopll.c

NOTE         I  campi   della  prima   struct  OPT   dell'array   hanno
             significati particolari:  il campo  opt contiene  l'indice
             del  (elemento   dell'array   corrispondente   al)   primo
             parametro non-opzione  nella command  line, se ve ne sono;
             il  campo arg contiene argv[0];  il campo  val contiene il
             numero totale di argomenti non-opzione nella command line.
             Ancora,  il  campo  opt  delle  struct   OPT   relative  a
             parametri  non-opzione sono  valorizzati così:  0  per  il
             primo trovato, 1 per il secondo, etc..

*----------------------------------------------------------------------------*/

struct OPT *cdecl parseoptions(int argc,char **argv,char *optionS,
	struct VOPT valfuncs[])
{
    static char *illegalS = ILLEG_S;
    static char *errorS = ERROR_S;
    char sw;

    return(((sw = (char)getswitch()) < 0) ? NULL :
	parseopt(argc,argv,optionS,sw,illegalS,errorS,valfuncs));
}


/*----------------------------------------------------------------------------*

setswitch()        Imposta lo switch character di default per il DOS

SINTASSI     int cdecl setswitch(char sw);

INCLUDE      parsopt.h

PARAMETRI    sw            il nuovo  switch character  di  default  del
                           DOS.

SCOPO        Imposta il  nuovo switch character di default del DOS. Per
             sapere semplicemente  qual è lo switch character attuale è
             possibile usare getswitch().

RESTITUISCE  il vecchio switch character, oppure, in caso di errore:
             -1    funzione non  supportata dal  DOS (__IOerror() setta
                   errno e doserrno).


SORGENTE     setdossw.c

NOTE         Nessuna

*----------------------------------------------------------------------------*/

int cdecl setswitch(char sw)
{
    asm {
        mov ax,0x3700;
        int 0x21;
        cmp al,0xFF;
        je notsupported;
        push dx;
        mov ax,0x3701;
        mov dl,byte ptr sw;
        int 0x21;
        pop dx;
        cmp al,0xFF;
        je notsupported;
        mov al,dl;
        xor ah,ah;
        jmp endfunc;
    }
notsupported:
    _AX = __IOerror(EINVFNC);
endfunc:
    return(_AX);
}


/*----------------------------------------------------------------------------*

storeopt()         Memorizza in struct gli argomenti della cmd line

SINTASSI     int cdecl storeopt(int argc,char **argv,char *optionS,
                                char sw,struct OPT *cmd,char *illegalS,
                                                         char *errorS);

INCLUDE      parsopt.h

PARAMETRI    argc          numero di  parametri sulla command line + 1.
                           E', solitamente, argc parametro di main().

             argv          array  di   puntatori  ai   parametri  nella
                           command   line.    E',   solitamente,   argv
                           parametro di main().

             optionS       puntatore  alla   stringa  contenente  tutti
                           caratteri-opzione      riconosciuti      dal
                           programma.

             sw            lo switch character.

             cmd           puntatore  alla   struct  OPT  (template  in
                           parsopt.h)  nella   quale  i  dati  relativi
                           all'opzione correntemente  processata devono
                           essere memorizzati.  Detta struct  OPT è uno
                           delgi  elementi   dell'array   allocato   da
                           parseopt(). Una struct OPT è composta di tre
                           elementi: il  primo (char  opt) conterrà  il
                           carattere-opzione; il  secondo  (char  *arg)
                           punterà all'argomento  dell'opzione (NULL se
                           l'opzione non  accetta argomenti);  il terzo
                           (int val)  varrà normalmente  0: in  caso di
                           errore (opzione  sconosciuta o  digitata  in
                           modo scorretto  o con  argomento non valido)
                           sarà posto  a -1.  Esso è inoltre utilizzato
                           per memorizzare  il valore  restituito dalla
                           funzione   di    validazione    dell'opzione
                           invocata da parseopt().

             illegalS      puntatore alla  stringa usata come argomento
                           del   carattere   due   punti   (:)   quando
                           utilizzato  come  opzione  (illecita)  nella
                           command line.

             errorS        puntatore alla  stringa usata come argomento
                           dei  caratteri-opzione   non   presenti   in
                           optionS.

SCOPO        memorizza in  una  struttura  le  opzioni  presenti  nella
             command line.  La sintassi, molto vicina a quella adottata
             come standard nei sistemi Unix, è la seguente:

             option : sw optLetter argLetter argument

             dove

                 -  sw è lo switch character

                 -  non ci sono spazi tra lo switch character e ogni
                    optLetter o argLetter.

                 -  optLetter e argLetter non sono segni di
                    punteggiatura.

                 -  optLetter e argLetter devono comparire in optionS.

                 -  argLetter, se presenti, devono essere seguite in
                    optionS da ':'.

                 -  argument è una stringa che termina con uno spazio e
                    può essere preceduta da uno spazio. Può includere
                    lo switch character.

                 -  maiuscole e minuscole non sono equivalenti.

             Sulla command line possono comparire più clusters (gruppi)
             di opzioni,  ciascuno dei quali è introdotto dallo switch.
             Tutti i  clusters di  pozioni devono  comparire prima  dei
             parametri non-opzione (qualunque cosa non introdotta dallo
             switch, ad eccezione delle stringhe argomenti di opzioni).
             Una argLetter  o optLetter  può  comparire  più  volte:  è
             compito  del   programmatore   scegliere   se   ciò   vada
             considerato errore.

             La  stringa   optionS  consente  il  riconoscimento  delle
             optLetter e  argLetter valide.  In essa  ogni argLetter  è
             seguita dai  due punti  (:). La  storeopt() restituisce la
             optLetter o argLetter processata; un valore minore di zero
             se non vi sono più opzioni sulla command line.

             Lo switch isolato tra spazi è un errore.

             Due switch  characters  consecutivi  (ad  es.:  --  o  //)
             vengono considerati  il primo  parametro  non-opzione,  il
             primo dei due switch è rimosso e storeopt() restituisce un
             valore negativo.  Se  vengono  successivamente  incontrate
             altre coppie  di switch characters sono lasciate immutate.
             Pertanto tale  combinazione può  essere utilizzata  se  il
             primo parametro  non-opzione inizia con lo switch. Es.: se
             PROG è  il nome del programma, A è un'opzione valida, -5 è
             il primo argomento, --ABC-- è il secondo, -4 il terzo  e -
             è lo  switch, affinché  la command  line sia  interpretata
             correttamente occorrerà scriverla come segue:

             PROG -A --5 --ABC-- -4

             La optind  e' inizialmente 1 e rappresenta sempre l'indice
             del prossimo argomento di argv[], che  storeopt()  non  ha
             ancora analizzato. Se e' utilizzato SWSW allora optind  e'
             incrementata al successivo argomento  prima  che  getopt()
             restituisca il suo valore cambiato di segno (fine opzioni)

             Il carattere  due punti (:) può separare una argLetter dal
             suo argomento  sulla command line: esso viene ignorato. Se
             l'argomento inizia  con il  due punti,  esso va  ripetuto.
             Esempio:

             -T:4     --->    argLetter = T,   argomento = 4

             -T::4    --->    argLetter = T,   argomento = :4

             Se è incontrata una lettera non inclusa in optionS, essa è
             comunque restituita  da storeopt(), ma nel campo val della
             struct OPT viene memorizzato un valore negativo e non 0.
             Esempio: se  il DOS switch è '/' (DOS default) e optionS è
             "A:F:PuU:wXZ:" allora 'P', 'u', 'w', e 'X' sono optLetter,
             mentre 'A', 'F', 'U', 'Z' sono argLetter. Una command line
             può essere:

                 PROG  /uPFPi /X /A L /f UnFile AltraStringa

             dove:

                 -  'u' e 'P' sono restituite come opzioni.

                 -  'F' è restituita con "Pi" come proprio argomento.

                 -  'X' è un'altra opzione.

                 -  'A' è restituita con "L" come argomento.

                 -  'f' non è presente in optionS, pertanto è
                    restituita memorizzando -1 nel campo val della
                    struct OPT. Essa è testata dalla funzione puntata
                    dal campo fun della struct VOPT che ha il carattere
                    ERRCHAR nel campo opt (se tale struct è definita
                    nell'array).

                 -  "UnFile" non è un'opzione e viene testata con la
                    funzione puntata dall'elemento fun dell'ultima
                    struct VOPT dell'array.

                 -  idem dicasi per "AltraStringa".

RESTITUISCE  L'opzione processata, anche se non presente in optionS: in
             questo caso  il campo  val della  struct OPT  è  negativo;
             esso,   cambiato    di   segno,    rappresenta    l'indice
             dell'elemento di  argv[] successivo  a quello  attualmente
             processato.

SORGENTE     storeopt.c

NOTE         I campi della prima struct OPT nell'array di strutture OPT
             hanno un  significato speciale  (essi sono  valorizzati da
             parseopt() o  parseoptions(), non  da storeopt()).  Vedere
             parseopt() o parseoptions() per maggiore dettaglio.

-----------------------------------------------------------------------------*/

int cdecl storeopt(int argc,char **argv,char *optionS,char sw,struct OPT *cmd,
	char *illegalS,char *errorS)
{
    extern int optind;	// numero della prossima opzione. Inizializzata a 1 da
    static char *letP;	// parseopt() in entrata e da essa riazzerata in uscita
    char *optP;

    while(argc > optind) {	//while e' usato per evitare una goto; non e' un vero loop
        cmd->val = 0;
        if(!letP) {
            if((!(letP = argv[optind])) || (*letP++ != sw))
                break;
            if(*letP == sw)
                break;
        }
        if(!(cmd->opt = *letP++)) {
            if(argv[++optind]) {
                letP = NULL;
                continue;
            }
            break;
        }
        if(cmd->opt == ':') {
            cmd->arg = illegalS;
            return(gopError(cmd));
        }
        if(!(optP = strchr(optionS,cmd->opt))) {
            cmd->arg = errorS;
            return(gopError(cmd));
        }
        if(*(optP+1) == ':') {
            optind++;
            if(!(*letP)) {
                if (argc <= optind)
                    return(gopError(cmd));
                letP = argv[optind++];
            }
            cmd->arg = letP;
            letP = NULL;
        }
        else {
            if(!(*letP)) {
                optind++;
                letP = NULL;
            }
            cmd->arg = NULL;
        }
        return((int)cmd->opt);
    }
    cmd->arg = letP = NULL;
    return((int)(cmd->opt = -optind));
}

OK, OK, ne ho avuto abbastanza! Torniamo alla lettura...