|
Appunti informatica |
|
Visite: 1590 | Gradito: | [ Grande appunti ] |
Leggi anche appunti:Linguaggi interpretati e compilatiLinguaggi interpretati e compilati Si è detto che il linguaggio di programmazione C come cesareC come Cesare Il Cesare in questione è proprio Caio Giulio Cesare, il noto imperatore romano Contenuto del floppy diskContenuto del floppy disk Il floppy disk allegato costituisce una raccolta di |
Si intende, per command line, la riga di testo digitata al prompt di sistema: essa contiene un comando DOS (che può essere il nome di un programma), completo degli eventuali parametri da avviamento. Ad esempio, la command line
pippo /a /b file0 *.dat
lancia il programma PIPPO passandogli quattro parametri, separati tra loro da spazi.
E' noto che il linguaggio C mette a disposizione un metodo semplice e standardizzato per accedere ai parametri della command line: allo scopo è sufficiente dichiarare la funzione main() con due parametri, nell'ordine un intero e un array di stringhe (un array di puntatori a carattere), convenzionalmente chiamati argc e argv (arguments counter e arguments vector):
void main(int argc, char **argv)
.
La variabile argc contiene il numero di parole, separate da spazi, presenti sulla command line (incluso, dunque, il nome del programma); gli elementi di argv referenziano le parole componenti la command line (argv[0] punta al nome del programma, completo di pathname ). Ogni sequenza di caratteri compresa tra due o più spazi (o tabulazioni) è considerata un parametro (se si intende passare al programma una stringa contenente spazi come un unico parametro è sufficiente racchiuderla tra doppi apici, 'come questa') e le wildcard non vengono espanse (con riferimento all'esempio sopra riportato, argv[4] in PIPPO contiene '*.dat
Aggiungendo un terzo parametro a main() è possibile avere a disposizione anche le stringhe che costituiscono l'environment del programma
void main(int argc, char **argv, char **envp)
.
Come argv, anche envp (environment pointer) è un array di puntatori a stringa, o meglio un array di puntatori a carattere. Vedere anche pag. e seguenti.
Il lavoro di parsing (cioè di scansione) della command line e di valorizzazione di inzializzazione delle stringhe e dei rispettivi puntatori è svolto da due funzioni di libreria, non documentate in quanto implementate per uso interno: nelle librerie del C Borland i loro nomi sono _setargv__() e _setenvp__()
Appare evidente che _setargv__() e _setenvp__() svolgono un compito utile solo se effettivamente il programma ha necessità di conoscere le stringhe che compongono l'environment e, rispettivamente, la command line. Esse vengono tuttavia invocate dal modulo di startup (pag. ) in ogni caso: è possibile accrescere l'efficienza dei programma che non necessitano di tali informazioni con un semplice stratagemma.
#pragma option -k- /* evita stack frame nelle funzioni senza parametri */
void _setargv__(void) /* sostituisce la _setargv__() di libreria */
void _setenvp__(void) /* sostituisce la _setenvp__() di libreria */
void main(void) /* non usa command line ed environment */
Naturalmente si può dichiarare una sola funzione fittizia, qualora l'altra sia necessaria:
#pragma option -k- /* evita stack frame nelle funzioni senza parametri */
void _setenvp__(void) /* sostituisce la _setenvp__() di libreria */
void main(int argc, char **argv) /* non usa l'environment */
La dichiarazione di entrambe le funzioni _set() riduce di alcune centinaia di byte la dimensione dell'eseguibile (.COM o .EXE) e ne rende leggermente più rapido il caricamento.
Entrambe _setargv__() e _setenvp__() sono eseguite prima di main(), siano esse le funzioni di libreria o quelle dichiarate nel sorgente: in questo caso nulla vieta al programmatore di crearne versioni personalizzate, non necessariamente 'nullafacenti'.
Una versione più sofisticata di _setargv__() è quella implementata nel modulo WILDARGS.OBJ (fornito con il compilatore): essa è in grado di espandere le wildcards e restituire in argv l'elenco completo dei file corrispondenti ai nomi 'collettivi' specificati sulla command line. Se il file PIPPO.C è compilato con:
bcc pippo.c wildargs.obj
il vettore argv ricavato dalla command line dell'esempio di pag. può avere più di quattro elementi, in quanto *.dat è espanso in tante stringhe quanti sono i file .dat effettivamente presenti nella directory. Solo se non vi è alcun file .dat argv ha cinque elementi ed argv[4] è ancora '*.dat
Esiste un solo object file WILDARGS.OBJ per tutti i modelli di memoria (pag.
Le variabili argc e argv e il file WILDARGS.OBJ costituiscono gli strumenti standard per la gestione della command line: essi rendono disponibili gli elemeti (stringhe) che la compongono. Chi desideri andare 'al di là' del C, ed accedere direttamente alla riga di comando digitata al prompt , deve vedersela con il Program Segment Prefix (vedere pagina
Infatti, nel PSP, il byte ad offset 80h memorizza la lunghezza della command line (escluso il nome del programma ed incluso il CR terminale); ad offset 81h si trova la riga di comando (a partire dal primo spazio o tabulazione seguente il nome del programma), che include il carattere di CR
Il mini‑programma dell'esempio seguente, compilato sotto Borland C++ 2.0, stampa la command line, o meglio la sequenza di argomenti passati al programma tramite questa.
#include <stdio.h>
#include <dos.h> // per MK_FP()
#include <string.h> // per _fstrncpy()
void main(void)
Si noti che main() non ha parametri: la command line viene infatti letta direttamente nel PSP. La variabile cmdlen viene valorizzata con la lunghezza della command line: il doppio cast è necessario in quanto detto dato è costituito, nel PSP, da un solo byte; il cast di MK_FP() (pag. ) a (int far *) avrebbe l'effetto di restituire una word in cui il byte più significativo è, in realtà, il primo carattere della riga di comando (di solito un blank). Il buffer cmdline è dimensionato a 127 byte: infatti tale è la massima lunghezza della command line (incluso il CR terminale, che deve essere sostituito con un NULL per ottenere una stringa stampabile dalle funzioni C). Per copiare in cmdline la porzione di PSP di nostro interesse è utilizzata _fnstrcpy(), che è equivalente a strncpy() ma accetta puntatori far . Se le librerie del compilatore utilizzate non includono una versione far della strncpy() è necessario copiare la stringa byte per byte, ad esempio con un ciclo while
.
register i = 0;
.
while((cmdline[i] = *(char far *)MK_FP(_psp,i+0x81)) != (char)0x0D)
i++;
cmdline[i] = NULL;
.
Dal momento che la seconda metà (gli ultimi 128 byte) del PSP è utilizzata dal DOS come DTA (Disk Transfer Address) di default per il programma, è indispensabile che le operazioni descritte siano le prime eseguite dal programma stesso, onde evitare che la command line sia sovrascritta.
Nei sistemi Unix la command line è spesso utilizzata per fornire al programma invocato direttive, dette opzioni, di modifica degli eventuali default di funzionamento. Nel tempo, la modalità di elencazione delle opzioni ha raggiunto, sia pure in modo non esplicitamente formale, un livello di standardizzazione tale da poter parlare di una vera e propria sintassi delle opzioni, in parte ripresa e spesso seguita anche in ambiente DOS.
I parametri di main() argc e argv costituiscono solo una base di partenza per implementare una gestione Unix‑like delle opzioni di command line: il resto è lasciato alla buona volontà del programmatore. Proviamo a scendere nel dettaglio: lo scopo è definire una sintassi per le command line option analoga a quella comunemente seguita nei sistemi Unix, e realizzare un insieme di funzioni libreria in grado di implementare in qualunque programma la gestione delle opzioni secondo quella sintassi.
Va innazitutto precisato che per opzione si intende un carattere alfanumerico, detto optLetter, preceduto sulla command line da un particolare carattere, detto switch character, che ha proprio lo scopo di identificare la optLetter quale opzione. In ambiente DOS lo switch character è, solitamente, la barra ' '; nei sistemi Unix è usato il trattino ' '. Il programma può prevedere che l'opzione supporti un parametro, detto argomento, rappresentato da una sequenza di caratteri qualsiasi, nel qual caso il carattere alfanumerico che rappresenta l'opzione è detto argLetter (e non optLetter).
Il programma riconosce le optLetter e le argLetter in quanto esse sono elencate in una stringa, convenzionalmente denominata optionS, in base alle regole elencate di seguito:
| |
|
Le optLetter e le argLetter non possono essere segni di interpunzione. |
|
Se il programma deve considerare equivalenti maiuscole e minuscole è necessario inserire in optionS sia la maiuscola che la minuscola per ogni optLetter o argLetter. |
|
Ogni argLetter è seguita dal carattere ' |
|
Per le argLetter non è data alcuna indicazione sugli argomenti validi: spetta al programma valutarli ed accettarli o segnalare eventuali errori. |
Lo switch character e la optLetter (o argLetter seguita dal proprio argomento) costituiscono un option cluster (gruppo di opzione), nell'ambito del quale valgono le regole seguenti:
|
Lo switch deve essere preceduto da uno spazio e seguito immediatamente da optLetter o argLetter. |
|
Uno switch isolato tra due spazi è un errore di sintassi. |
|
Uno switch seguito da un carattere non compreso tra le optLetter e le argLetter riconosciute dal programma è un errore di sintassi. |
|
Una optLetter può essere seguita da uno spazio o dalla successiva optLetter o argLetter. |
|
Una argLetter deve essere seguita dal proprio argomento, che non può essere uno spazio. |
|
Tra una argLetter e l'argomento può esservi uno spazio oppure un carattere ' |
|
Se un argomento inizia con il carattere ' ', questo deve essere ripetuto. |
|
Un argomento può contenere (anche quale carattere iniziale) lo switch character; se contiene spazi deve essere compreso tra virgolette. |
|
Un argomento deve essere seguito da uno spazio. |
|
Maiuscole e minuscole non sono equivalenti, tanto in optLetter e argLetter quanto negli argomenti. |
|
Gli option cluster devono essere elencati tutti all'inizio della command line; la prima stringa presente in essa non introdotta dallo switch character è considerata il primo dei parametri non‑option. |
|
Se il primo parametro non‑option inizia con lo switch character, questo deve essere ripetuto due volte. |
|
La ripetizione, nella command line, della medesima opzione è sintatticamente lecita: spetta al programma valutare se ciò costituisca un errore. |
Spaventati? Non è il caso: tale insieme di regole è complesso solo apparentemente. In effetti esso formalizza una realtà probabilmente già nota. Vediamo un paio di esempi.
Se optionS 'A:F:PuU:wXZ:', nella command line:
PROG /uPFPi /X /AL /f UnFile AltraStringa
PROG è il nome del programma e sono individuate le optLetter 'u P' e 'X', nonché le argLetter 'F' (il cui argomento è 'Pi') e 'A' (con l'argomento 'L'), mentre la lettera 'f' non è un'opzione valida; le stringhe 'UnFile' e 'AltraStringa' costituiscono validi parametri non‑option.
Se optionS AT:J:', nella command line
PROG -A -T:4 -AJ::k -T2 --123- -w- - Parola 'Due parole'
PROG è il nome del programma ed è riconosciuta la optLetter 'A', presente due volte; sono inoltre individuate le argLetter 'J' (con l'argomento ':k') e 'T', anch'essa presente due volte (con gli argomenti, rispettivamente, ' ' e ' '). Il primo parametro non‑option è la stringa ' '; i successivi sono le stringhe '‑w‑ Parola' e 'Due parole'. Il parametro ' ' è valido, in quanto non è il primo non‑option parameter (nel qual caso dovrebbe essere digitato come due trattini).
Se optionS è, ancora, 'AT:J:', nella command line
PROG -J -J 'UnaSolaParola' -A
PROG è il nome del programma ed è riconosciuta la argLetter 'J', il cui argomento è '‑J'. Il primo non‑option argument è la stringa 'UnaSolaParola'. La stringa '‑A' rappresenta il secondo parametro non‑option e non viene riconosciuta quale optLetter in quanto tutto ciò che segue il primo non‑option argument è comunque considerato tale a sua volta.
Sulla scorta delle regole descritte è possibile realizzare le funzioni necessarie per una gestione Unix‑like della command line. Di seguito è presentato il listato di PARSEOPT.H, header file incluso nel sorgente delle funzioni e necessario anche per la realizzazione di programmi che le richiamano. Esso contiene i prototipi delle funzioni, i templates delle strutture appositamente definiti ed alcune costanti manifeste.
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 ; // automatico e ne e' restituito il puntatore.
struct VOPT ;
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 ** ** ** ** ********/
L'interfaccia utente di alto livello è costituito dalla parseoptions(), che gestisce al proprio interno, secondo la sintassi descritta poco sopra, tutte le operazioni necessarie per processare le opzioni specificate sulla riga di comando del programma.
I suoi primi due parametri sono gli ormai noti argc e argv, che devono quindi essere dichiarati parametri di main(); il terzo parametro è la stringa optionS, che, come descritto, contiene l'elenco delle optLetter e delle argLetter. Il quarto parametro è l'indirizzo di un array di strutture di tipo VOPT, il cui template è definito, come si vede, in PARSEOPT.H. Ogni struttura è formata da due campi:
struct VOPT
Il campo opt è un carattere e rappresenta una optLetter o una argLetter, mentre il campo fun, puntatore a funzione, contiene l'indirizzo della funzione di validazione dell'optLetter o argLetter: parseoptions(), quando viene individuata sulla command line la optLetter o argLetter del campo opt, lancia la funzione il cui indirizzo è contenuto in fun, passandole un puntatore a struttura OPT (di cui diremo tra breve) e un intero esprimente la posizione dell'opzione sulla command line. La funzione a cui punta fun è definita dall'utente. Vediamo un esempio: se la optionS contiene la optLetter 'a' e abbiamo definito la funzione valid_a(), che deve essere invocata quando ‑a è specificata sulla riga di comando, si ha:
#include <STOREOPT.H>
char *optionS = 'abc:';
int valid_a(struct OPT *tmp,int no)
int valid_err(struct OPT *tmp,int no)
int valid_glob(struct OPT *tmp,int no)
struct VOPT valfuncs[] = ,
.
,
.
void main(int argc,char **argv)
Vediamo il significato di ogni elemento dell'array (nell'esempio optArray) e, per ogni elemento, il significato dei singoli campi. La prima struttura OPT presente in optArray optArray[0]) contiene informazioni sugli elementi successivi. In particolare, il campo opt contiene l'indice , nell'array stesso, dell'elemento relativo al primo non‑option item presente sulla command line; in altre parole optArray[optArray[0].opt] è la struttura OPT che, nell'array, si riferisce al primo non‑option item. Il campo arg contiene sempre argv[0], cioè il puntatore alla stringa che rappresenta il path completo del programma. Il campo val contiene il numero dei non‑option items presenti sulla riga di comando; se non ve ne sono, entrambi i campi opt e val sono inizializzati a
Nell'array optArray troviamo poi un elemento per ogni optLetter e argLetter incontrata sulla command line. Nel caso di una optLetter, il campo opt contiene l'optLetter stessa, il campo arg NULL e il campo val contiene l'intero restituito dalla funzione di validazione (campo fun della struct VOPT). Nel caso di una argLetter, invece, il campo arg punta ad una stringa contenente l'argomento fornito alla argLetter stessa sulla command line. Si noti che arg è sempre puntatore a stringa, anche nel caso in cui l'argomento della optLetter sia un numero: in tal caso occorre utilizzare l'appropriata funzione di conversione (ad esempio atoi()) per ottenere il valore numerico.
Se la optLetter o argLetter incontrata non si trova in errorS, il campo arg contiene il puntatore alla stringa ERROR_S definita in PARSEOPT.H. Nella riga di comando, i due punti (' ') isolati sono interpretati come opzione illecita ed il campo arg contiene la stringa ILLEGAL_S (anch'essa definita in PARSEOPT.H). In entrambi i casi appena descritti, il campo val assume valore
Si noti ancora che parseoptions() assume che lo switch character utilizzato sia quello di default per il sistema operativo: in DOS esso è la barra (' '); se si preferisce utilizzare un altro carattere per introdurre le opzioni, ad esempio il trattino (' ') secondo la convenzione Unix, occorre modificare il default mediante una chiamata a setswitch()
Ipotizziamo ora che il listato d'esempio faccia parte di un programma chiamato PROG ed invocato con la seguente riga di comando:
PROG /a /c14 /b pippo 'Qui, Quo, Qua' pluto
Al ritorno da parseoptions() optArray contiene 6 elementi, valorizzati come segue:
optArray[0].opt |
|
è l'indice dell'elemento di optArray relativo al primo non‑option item, se ve ne sono; altrimenti esso esprime il numero di elementi di cui si compone optArray |
optArray[0].arg |
pathname di PROG |
E' argv[0] |
optArray[0].val |
|
Indica che vi sono 3 non‑option item. Essi sono referenziati da optArray[4] optArray[5] e optArray[6]; si osservi inoltre che , ricavabile con (optArray[0].opt+optArray[0].val‑1), è l'indice dell'ultimo elemento dell'array. Infine, argv[argc-optArray[0].val] è il primo non‑option item in argv |
optArray[1].opt |
'a' |
Prima opzione sulla command line. |
optArray[1].arg |
NULL |
a' è una optLetter: non ha argomento. |
optArray[1].val |
valore restituito da valid_a() |
Se 'a' non fosse un'opzione valida, o fosse specificata in modo errato, il campo conterrebbe il valore della costante manifesta ERRCHAR |
optArray[2].opt |
'c' |
Seconda opzione sulla command line. |
optArray[2].arg |
|
c' è una argLetter: ' ' è il suo argomento, sempre sotto forma di stringa. |
optArray[2].val |
valore restituito da valid_c() |
Se 'c' non fosse un'opzione valida, o fosse specificata in modo errato, il campo conterrebbe il valore della costante manifesta ERRCHAR |
optArray[3].opt |
'b' |
Terza opzione sulla command line. |
optArray[3].arg |
NULL |
b' è una optLetter: non ha argomento. |
optArray[3].val |
valore restituito da valid_b() |
Se 'b' non fosse un'opzione valida, o fosse specificata in modo errato, il campo conterrebbe il valore della costante manifesta ERRCHAR |
optArray[4].opt |
|
Primo non‑option item sulla command line. |
optArray[4].arg |
'pippo' |
Il non‑option item stesso, come stringa. |
optArray[4].val |
valore restituito da valid_glob() |
Sempre |
optArray[5].opt |
|
Secondo non‑option item sulla command line. |
optArray[5].arg |
'Qui, Quo, Qua' |
Il non‑option item stesso, come stringa. |
optArray[5].val |
valore restituito da valid_glob() |
|
optArray[4].opt |
|
Primo non‑option item sulla command line. |
optArray[4].arg |
'pluto' |
Il non‑option item stesso, come stringa. |
optArray[4].val |
valore restituito da valid_glob() |
|
E' il momento di descrivere le funzioni di validazione, quelle, cioè, i cui puntatori sono contenuti nei campi fun degli elementi dell'array valfuncs. Va precisato subito che il codice di dette funzioni dipende dalle esigenze del programma. Generalmente, ad ogni optLetter e argLetter corrisponde una appropriata funzione di validazione, ma nulla vieta di utilizzare una medesima funzione per controllare più opzioni: è sufficiente che i campi fun a queste corrispondenti siano tutti inizializzati con lo stesso puntatore. Analoghe considerazioni valgono a proposito della funzione richiamata in caso di opzione errata, e di quella richiamata una volta per ogni non‑option item.
Ad esempio, la funzione valid_err() potrebbe visualizzare un messaggio di errore e interrompere il programma, tramite la funzione di libreria exit()
Tutte queste funzioni, comunque, devono restituire un intero al fine di valorizzare secondo le esigenze del programmatore i campi val degli elementi dell'array di strutture OPT; inoltre tutte ricevono in ingresso, come parametri, il puntatore ad una struct OPT (la quale altro non è che l'elemento dell'array optArray corrispondente a quell'opzione) in cui i campi opt e arg sono già valorizzati come mostrato nella tabella sopra esposta, mentre il campo val contiene . Può risultare utile, nelle funzioni di validazione delle argLetter, utilizzare il campo arg per effettuare i necessari controlli di validità dell'argomento associato all'argLetter stessa. L'intero che le funzioni di validazione ricevono come secondo parametro esprime la posizione dell'argLetter (o optLetter) sulla command line, e può essere utilizzato per le opportune verifiche qualora la posizione dell'opzione sia importante.
Nei programmi si ha spesso la necessità di modificare il comportamento dell'algoritmo a seconda che un'opzione sia stata o meno specificata: in questi casi può essere utile un flag, inizializzato dalla funzione di validazione e controllato laddove occorra nel corso dell'elaborazione. Un metodo efficiente di implementare tale tecnica di gestione delle opzioni è rappresentato da una variabile globale in cui ogni bit è associato ad una opzione. La funzione di validazione pone a il bit; questo è poi verificato da altre funzioni del programma, che possono accedere a quella variabile, proprio in quanto globale.
Tornando al nostro esempio, le funzioni di validazione potrebbero agire su un intero senza segno:
unsigned int optBits;
int valid_a(struct OPT *tmp,int no)
int valid_b(struct OPT *tmp,int no)
int valid_c(struct OPT *tmp,int no)
int valid_glob(struct OPT *tmp,int no)
int valid_err(struct OPT *tmp,int no)
I bit della variabile globale optBits sono associati alle singole opzioni: in particolare, il bit 0 corrisponde all'optLetter 'a', il bit 1 all'optLetter 'b' e il bit 2 all'argLetter 'c'; si noti che le costanti utilizzate per valorizzarli sono potenze di (in particolare, elevato ad esponente pari al numero del bit). In tal modo è possibile con un'operazione di OR su bit (composta con l'assegnamento) modificare il singolo bit desiderato. In qualunque altro punto del programma può utilizzare un test analogo al seguente:
if(optBits & 2).
per verificare se l'opzione 'b' è stata specificata. L'uso di costanti manifeste come
#define OPTION_A 1
#define OPTION_B 2
#define OPTION_C 4
facilita notevolmente la vita.
Si noti che in luogo di una variabile integral si può utilizzare un campo di bit, con il vantaggio, tra l'altro, di non essere costretti ad utilizzare le operazioni di AND e OR su bit, in quanto ogni campo rappresenta di per sé un'opzione
struct OPTBITS optBits
.
optBits.optionA = 1;
.
if(optBits.optionA)
.
Si noti che valid_c(), in caso di errore, chiama valid_err() per interrompere il programma. Inoltre, la scelta dei nomi delle funzioni di validazione è, ovviamente, libera: quelli utilizzati nell'esempio non sono vincolanti, né rappresentano un default.
Va ancora osservato che parseoptions() è perfettamente compatibile con WILDARGS.OBJ (pag. ): se come non‑option item sono specificati uno o più nomi di file, essi verranno trattati nel modo consueto: l'argv che parseoptions() riceve come secondo parametro contiene già i puntatori ai nomi generati dall'espansione delle wildcard ' e '
Ed ecco, finalmente (?), il sorgente completo delle funzioni, listate in ordine alfabetico, che realizzano il meccanismo sin qui descritto. Per un esempio pratico di utilizzo vedere pag.
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)
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)
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[])
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++)
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[])
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)
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)
if(!(cmd->opt = *letP++))
break;
}
if(cmd->opt == ':')
if(!(optP = strchr(optionS,cmd->opt)))
if(*(optP+1) == ':')
cmd->arg = letP;
letP = NULL;
}
else
cmd->arg = NULL;
}
return((int)cmd->opt);
}
cmd->arg = letP = NULL;
return((int)(cmd->opt = -optind));
La funzione storeopt() è il cuore del meccanismo. Essa è progettata per scandire una stringa alla ricerca di optLetter e argLetter e, per queste ultime, isolare l'argomento fornito. La storeopt() analizza una sola stringa ad ogni chiamata, perciò deve essere utilizzata all'interno di un loop che provveda a gestire opportunamente i puntatori contenuti in argv. A ciò provvede parseopt(), che, al ritorno da storeopt() si occupa di lanciare la funzione di validazione dell'opzione mediante l'indirezione del puntatore contenuto nel campo fun della struttura di template VOPT
(*(valfuncs+i)->fun)(tmp,carg)
I parametri tmp e carg, coerentemente con il prototipo delle funzioni di validazione, sono il puntatore alla struct OPT e l'intero rappresentante la posizione dell'opzione sulla command line.
Se storeopt() restituisce una condizione di errore (tramite gopError() parseopt() ricerca nell'array valfuncs un elemento il cui campo opt sia inizializzato con il valore della costante manifesta ERRCHAR e, se questo esiste, lancia la funzione di validazione corrispondente (valid_err() nell'esempio di poco fa).
Quando storeopt() segnala, tramite la restituzione di un valore negativo, che non vi sono più optLetter e argLetter, parseopt() considera i restanti elementi di argv come non‑option items: se il campo fun dell'ultimo elemento dell'array valfuncs non è NULL viene lanciata la funzione da esso indirizzata una volta per ogni non‑option item (la funzione è sempre la stessa, ma cambiano i valori dei campi della struct OPT di cui essa riceve il puntatore.
Ad ogni iterazione parseopt() alloca la memoria necessaria per aggiungere all'array di strutture OPT quella corrispondente all'item della command line attualmente processato; al termine dell'elaborazione essa restituisce l'indirizzo dell'array oppure NULL in caso di errore (fallita allocazione della memoria).
Oltre ai parametri richiesti dalla parseoptions(), la parseopt() necessita dello switch character (il carattere che introduce le optLetter e argLetter) e delle due stringhe da utilizzare come campi arg per le opzioni errate e illecite. Risulta evidente, a questo punto, che parseoption() è semplicemente un 'guscio' di alto livello per parseopt(), alla quale passa, oltre ai parametri ricevuti, anche quelli appena elencati, fornendone valori di default. In particolare, per le due stringhe sono utilizzate le costanti manifeste ERROR_S e ILLEGAL_S definite in PARSEOPT.H, mentre per lo switch character è utilizzato il valore restituito dalla getswitch(), che richiede al DOS il carattere di default.
Al riguardo, sono necessarie alcune precisazioni. Come si è detto, lo switch character di default è la barra in DOS e il trattino in Unix: ciò implica che se si desidera realizzare in ambiente DOS un'interfaccia il più possibile Unix‑like occorre dimenticarsi di parseoptions() e chiamare direttamente parseopt(), avendo cura di fornirle come parametro lo switch character desiderato. In alternativa è possibile modificare l'impostazione di default del DOS mediante la setswitch(), prima di chiamare parseoptions()
La setswitch() richiede come parametro il carattere che si desidera impostare come nuovo default e restuisce il precedente default ( in caso di errore). La getswitch() non richiede parametri e restituisce il default attuale. Le due funzioni si basano sull'int 21h, servizio 37h, subfunzioni 00h (GetSwitchChar) e 01h (SetSwitchChar); va sottolineato che detto servizio non è ufficialmente documentato e, pertanto, potrebbe non essere disponibile in tutte le versioni di DOS: in particolare, a partire dal DOS 5.0, la subfunzione 01h è ignorata (non determina la restituzione di un errore, ma non ha comunque alcun effetto) ed è perciò necessario utilizzare direttamente parseopt() se si desidera utilizzare il trattino come switch character.
Int 21h, Serv. 37h, Subf. 00h: Get switch character
Input |
AH AL |
37h 00h |
Output |
AL DL |
FFh in caso di errore (funzione non supportata). Attuale switch character, se AL non è FFh |
Note |
|
Se non vi è errore, le versioni di DOS fino alla 4.x restituiscono 00h in AL; dalla 5.0 in poi AL 2Fh |
Int 21h, Serv. 37h, Subf. 01h: Set switch character
Input |
AH AL DL |
37h 01h Nuovo switch character |
Output |
AL |
00h se OK, FFh in caso di errore (funzione non supportata). |
Note |
|
Questa chiamata è ignorata dal DOS a partire dalla versione 5.0. |
Va infine osservato che il listato potrebbe essere suddiviso in più sorgenti, uno per ogni funzione (con la sola eccezione di gopError() e storeopt(), da riunire in un unico file ); dalla compilazione si otterrebbero così più file .OBJ, da inserire in una libreria. Se ne avvantaggerebbero gli eseguibili incorporanti le funzionalità descritte, dal momento che in essi bverrebbero inclusi dal linker solo i moduli necessari.
Solo a partire dalla versione 3.0 del DOS. Se il programma è invocato sotto versioni precedenti argv[0] contiene la stringa 'C'
Il compilatore, diligentemente, inserisce un riferimento esterno (che il linker risolve con una ricerca in libreria) solo per quelle funzioni il cui codice non si trova nel sorgente.
Un puntatore al PSP è, per definizione, far ed impone l'uso di _fstrncpy(), mentre il buffer cmdline near: ciò spiega il cast (char far *)cmdline. La _fstrncpy() è la versione per puntatori a 32 bit della strncpy() (vedere pag. e seguenti).
La principale differenza tra dette regole e la consuetudine Unix è che quest'ultima, generalmente, non ammette l'uso del carattere ' ' quale separatore tra una argLetter e relativo parametro.
Non è un carattere, ma un numero a 8 bit. Ciò significa che per esprimere il numero 4, il campo opt non contiene , ma il carattere ASCII
Appunti su: |
|