Appunti per Scuola e Università
humanisticheUmanistiche
Appunti e tesine di tutte le materie per gli studenti delle scuole medie riguardanti le materie umanistiche: dall'italiano alla storia riguardanti le materie umanistiche: dall'italiano alla storia 
sceintificheScientifiche
Appunti, analisi, compresione per le scuole medie suddivisi per materie scientifiche, per ognuna troverai appunti, dispense, esercitazioni, tesi e riassunti in download.
tecnicheTecniche
Gli appunti, le tesine e riassunti di tecnica amministrativa, ingegneria tecnico, costruzione. Tutti gli appunti di AppuntiMania.com gratis!
Appunti
informatica
CComputerDatabaseInternetJava
Linux unixReti


AppuntiMania.com » Informatica » Appunti di c » La command line

La command line




Visite: 1474Gradito:apreciate 5-stela [ Grande appunti ]
Leggi anche appunti:

Linguaggi interpretati e compilati


Linguaggi interpretati e compilati Si è detto che il linguaggio di programmazione

C come cesare


C come Cesare Il Cesare in questione è proprio Caio Giulio Cesare, il noto imperatore romano

Contenuto del floppy disk


Contenuto del floppy disk Il floppy disk allegato costituisce una raccolta di
immagine di categoria

Scarica gratis La command line

La command line

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__()

_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'.

WILDARGS.OBJ

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. 

PSP e command line

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.

Una gestione Unix-like

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.

La 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:


La stringa optionS non deve contenere spazi


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.

Le funzioni per la libreria

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'

 L'environment non c'entra nulla con la command line, ma già che ci siamo

 Nelle librerie del C Microsoft troviamo setenvp() e setargv(): la sostanza non cambia.

 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.

 Priva, però, del nome del programma e degli eventuali simboli di piping (' ') e redirezione ('< >

 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).

 Per spazio si intende uno o più blank o tabulazioni.

 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 nel caso di opzioni errate!

 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 

In realtà, storeopt() deriva dalla funzione getopt(), che nei sistemi Unix rappresenta l'algoritmo standard (fornita come sorgente con molti compilatori proprio per consentire agli sviluppatori di scrivere applicazioni con interfaccia coerente) per la scansione della command line.

 Dal momento che gopError() è una funzione di servizio per storeopt(), entrambi i moduli .OBJ verrebbero comunque inclusi dal linker nell'eseguibile.

Scarica gratis La command line
Appunti su:



Scarica 100% gratis e , tesine, riassunti



Registrati ora

Password dimenticata?
  • Appunti superiori
  • In questa sezione troverai sunti esame, dispense, appunti universitari, esercitazioni e tesi, suddivisi per le principali facoltà.
  • Università
  • Appunti, dispense, esercitazioni, riassunti direttamente dalla tua aula Universitaria
  • all'Informatica
  • Introduzione all'Informatica, Information and Comunication Tecnology, componenti del computer, software, hardware ...