17.11.2012 Views

n - Index of

n - Index of

n - Index of

SHOW MORE
SHOW LESS

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

Appunti di informatica libera 2003.06.29<br />

Volume VIII<br />

Argomenti avanzati e accessori<br />

1


Appunti Linux<br />

Copyright © 1997-2000 Daniele Giacomini<br />

Appunti di informatica libera<br />

Copyright © 2000-2003 Daniele Giacomini<br />

Via Morganella Est, 21 -- I-31050 Ponzano Veneto (TV) -- daniele @ swlibero.org<br />

Le informazioni contenute in questa opera possono essere diffuse e riutilizzate in base alle condizioni<br />

poste dalla licenza GNU General Public License, come pubblicato dalla Free S<strong>of</strong>tware<br />

Foundation.<br />

In caso di modifica dell’opera e/o di riutilizzo parziale della stessa, secondo i termini della licenza,<br />

le annotazioni riferite a queste modifiche e i riferimenti all’origine di questa opera, devono<br />

risultare evidenti e apportate secondo modalità appropriate alle caratteristiche dell’opera stessa.<br />

In nessun caso è consentita la modifica di quanto, in modo evidente, esprime il pensiero,<br />

l’opinione o i sentimenti del suo autore.<br />

L’opera è priva di garanzie di qualunque tipo, come spiegato nella stessa licenza GNU General<br />

Public License.<br />

Queste condizioni e questo copyright si applicano all’opera nel suo complesso, salvo ove indicato<br />

espressamente in modo diverso.<br />

The informations contained inside this work can be spread and reused under the terms <strong>of</strong> the<br />

GNU General Public License as published by the Free S<strong>of</strong>tware Foundation.<br />

If you modify this work and/or reuse it partially, under the terms <strong>of</strong> the license, the notices about<br />

these changes and the references about the original work, must be evidenced conforming to the<br />

work characteristics. IN NO EVENT IS ALLOWED TO MODIFY WHAT ARE CLEARLY<br />

THE THOUGHTS, THE OPINIONS AND/OR THE FEELINGS OF THE AUTHOR.<br />

This work is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without<br />

even the implied warranty <strong>of</strong> MERCHANTABILITY or FITNESS FOR A PARTICULAR<br />

PURPOSE. See the GNU General Public License for more details.<br />

These conditions and this copyright apply to the whole work, except where clearly stated in a<br />

different way.<br />

Una copia della licenza GNU General Public License, versione 2, si trova nell’appendice A.<br />

A copy <strong>of</strong> GNU General Public License, version 2, is available in appendix A.<br />

2


The main distribution for Appunti di informatica libera is described below. For every distribution<br />

channel the maintainer’s name and address is also reported.<br />

Ordering<br />

Prosa srl distribute Appunti di informatica libera and possibly oder related products through<br />

the commercial channel. Any request to buy something related to Appunti di informatica<br />

libera, can be addressed to Prosa srl:<br />

<br />

<br />

Internet mirrors with good bandwidth<br />

• direct reading: <br />

download: <br />

Fabrizio Rubino, fabrizio @ koalas<strong>of</strong>t.org<br />

• direct reading: <br />

download: <br />

download: <br />

mirror-service @ garr.it<br />

• direct reading: <br />

download: <br />

Fabrizio Rubino, fabrizio @ koalas<strong>of</strong>t.org<br />

• direct reading: <br />

download: <br />

Italian Linux Society, info @ linux.it<br />

Internet main distribution sites<br />

• direct reading: <br />

download: and <br />

Michele Dalla Silvestra, mds @ swlibero.org<br />

• direct reading: and <br />

download: and <br />

Fabrizio Rubino, fabrizio @ koalas<strong>of</strong>t.org<br />

• direct reading: <br />

download: <br />

Carlo Perassi, carlo @ linux.it<br />

• direct reading: <br />

download: <br />

Davide Barbieri, paci @ prosa.it<br />

• direct reading: <br />

download: <br />

Fabrizio Zeno Cornelli, zeno @ filibusta.crema.unimi.it<br />

3


• direct reading: <br />

download: <br />

Franco Lazzero, PCTIME, pctime @ pctime.net<br />

• direct reading: <br />

download: <br />

David Pisa, david @ iglu.cc.uniud.it<br />

• direct reading: <br />

download: <br />

Claudio Neri, Sincro Consulting, neri.c @ sincroconsulting.com<br />

GNU distributions<br />

• GNU/Linux Debian <br />

Massimo Dal Zotto, dz @ cs.unitn.it<br />

Italian magazine’s CD-ROM<br />

• inter-punto-net <br />

Michele Dalla Silvestra, mds @ swlibero.org<br />

• Internet News <br />

Francesco Facconi, francesc<strong>of</strong>acconi @ libero.it<br />

Fabio Ferrazzo, fabio.fr @ tiscalinet.it<br />

• Linux Magazine <br />

Emmanuele Somma, esomma @ ieee.org<br />

• Linux Pro <br />

Massimiliano Zagaglia, max @ linuxwaves.com<br />

• PC Upgrade <br />

info @ pcupgrade.it<br />

La diffusione di questa opera è incoraggiata in base ai termini della licenza.<br />

The spread <strong>of</strong> this work is encouraged under the terms <strong>of</strong> the license.<br />

4


Parte lxix Prevenzione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7<br />

351 Copie di sicurezza . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8<br />

352 Dischetti di emergenza con GNU/Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19<br />

Parte lxx Informatica gestionale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29<br />

353 Codici a barre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30<br />

354 Barcode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46<br />

355 Trasformazione in lettere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49<br />

Parte lxxi i86 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57<br />

356 Minix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58<br />

357 ELKS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80<br />

Parte lxxii Dos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83<br />

358 Dos: introduzione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86<br />

359 Dos: dischi, file system, directory e file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100<br />

360 Dos: configurazione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108<br />

361 Dos: script dell’interprete dei comandi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115<br />

362 Dos: gestione della memoria centrale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .121<br />

363 FreeDOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123<br />

364 Progetto GNUish . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .126<br />

365 The valuable DOS Freeware page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128<br />

366 Clean the Clipper 5.2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134<br />

367 nanoBase 1997 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160<br />

368 nanoBase 1997 user manual . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166<br />

Parte lxxiii Braille . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 315<br />

369 Introduzione al sistema braille . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316<br />

370 Sistemi di interazione per non vedenti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321<br />

Parte lxxiv Aspetti umani . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 325<br />

371 Manifesto GNU . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331<br />

372 Il progetto GNU . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339<br />

373 La rivoluzione del Mimete, ovvero s<strong>of</strong>tware e copyright . . . . . . . . . . . . . . . . . . . . . . . . . .355<br />

374 Il copyright/diritto d’autore del s<strong>of</strong>tware . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358<br />

Indice analitico del volume . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 361<br />

5


Parte lxix<br />

Prevenzione<br />

351 Copie di sicurezza . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8<br />

351.1 Scelta del sistema di copia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8<br />

351.2 Strategia nelle copie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10<br />

351.3 Supporti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11<br />

351.4 Compressione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13<br />

351.5 Archiviazione e recupero attraverso tar e gzip . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13<br />

351.6 Archiviazione di un file system . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17<br />

352 Dischetti di emergenza con GNU/Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19<br />

352.1 Dischetti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19<br />

352.2 Personalizzazione di dischetti di emergenza . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .22<br />

352.3 Dischetti Slackware . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22<br />

352.4 Kernel per dischetti di emergenza . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24<br />

352.5 Cavi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25<br />

352.6 Utenti e gruppi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .27<br />

352.7 Riferimenti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27<br />

7


Copie di sicurezza<br />

Capitolo 351<br />

L’amministrazione di un sistema Unix è da sempre un grosso problema sotto tutti i punti di vista.<br />

Il primo tra tutti è quello della salvaguardia dei dati, ma al rischio della loro perdita si pone<br />

rimedio solo attraverso una gestione corretta delle copie di sicurezza.<br />

351.1 Scelta del sistema di copia<br />

Gli strumenti a disposizione per eseguire copie di sicurezza sono molti e si possono distinguere<br />

due estremi possibili:<br />

• copiare i file e le directory;<br />

• copiare una partizione o un disco intero.<br />

La copia di una partizione o di un disco può avere il vantaggio di permettere l’archiviazione<br />

della situazione esatta in cui si trova, problemi inclusi. Inoltre, non avendo un processo di lettura<br />

sui file, la data di lettura di questi non viene modificata. Lo svantaggio fondamentale di tale<br />

tipo di copia è che questa è riferita a un disco particolare (o a una partizione) di una macchina<br />

particolare: è molto poco probabile che si possano recuperare dati archiviati in questo modo in un<br />

disco fisso diverso. Questa tecnica, più che per eseguire delle copie di sicurezza, viene utilizzata<br />

per archiviare dischetti nel loro stato originale.<br />

La copia di file e directory non tiene conto del supporto fisico in cui si trovano e nemmeno del<br />

tipo di file system utilizzato. Questo comporta una serie di conseguenze:<br />

• i file e le directory vengono scanditi in lettura, alterando quindi le date di lettura;<br />

• i collegamenti simbolici vanno copiati come tali e non devono essere copiati gli oggetti a<br />

cui questi puntano;<br />

• i collegamenti fisici potrebbero non essere distinti.<br />

In generale, dal momento che una copia di file e directory è portabile, mentre una copia di un<br />

dispositivo intero non lo è (quando non si tratta di un dispositivo standard come i dischetti),<br />

dovrebbe essere preferibile la prima di queste due soluzioni.<br />

351.1.1 Archiviazione<br />

È intuitiva la ragione per la quale le copie di sicurezza non vanno fatte archiviando un dispositivo<br />

intero come se fosse un file unico. La copia pura e semplice dei file e delle directory è una tecnica<br />

possibile, ma richiede condizioni particolari:<br />

• l’unità di destinazione deve essere in grado di accogliere i dati come sono all’origine, in<br />

pratica dovrebbe trattarsi di un disco;<br />

• deve trattarsi di unità rimovibili;<br />

• la capacità di queste unità deve essere maggiore di quella del file più grande che si deve<br />

archiviare.<br />

8


Copie di sicurezza 9<br />

Di solito si preferisce la tecnica dell’archiviazione dei dati in un file unico (che rappresenta<br />

l’archivio), assieme a tutte le informazioni necessarie per riprodurre i file e le directory originali.<br />

In questo modo si possono utilizzare unità di memorizzazione di qualunque tipo, eventualmente<br />

suddividendo l’archivio in pezzi più piccoli contenibili al loro interno.<br />

351.1.2 Archiviazione di file speciali<br />

Gli oggetti contenibili in un file system possono essere di vario tipo (file puri e semplici, directory,<br />

file di dispositivo, collegamenti, ecc.) e così pure i loro attributi (permessi, date, ecc.). Il sistema<br />

di archiviazione che si utilizza deve essere in grado riprodurre correttamente tutti i dati del tipo<br />

di file system che si utilizza.<br />

Per esempio, non sarebbe possibile archiviare i dati di un file system Unix in un archivio ‘.zip’<br />

che è nato per gli ambienti Dos.<br />

351.1.3 Utenti e gruppi proprietari<br />

Tra gli attributi dei file, è molto importante l’indicazione degli utenti e dei gruppi proprietari.<br />

I programmi di archiviazione potrebbero non essere in grado di memorizzare il numero UID e<br />

GID, limitandosi ad annotare solo i nomi degli utenti e dei gruppi. In tal modo, nel momento del<br />

recupero, i numeri UID e GID verrebbero riprodotti in base alle caratteristiche del sistema, cioè<br />

in base alla particolare configurazione dei file ‘/etc/passwd’ e ‘/etc/group’.<br />

Il fatto che il programma di archiviazione memorizzi i numeri UID e GID, oppure che memorizzi<br />

i nomi di utenti e gruppi, ha delle implicazioni che si traducono, a seconda delle circostanze, in<br />

vantaggi o svantaggi.<br />

Se i dati archiviati devono essere riprodotti in un sistema diverso da quello di origine, in cui ci<br />

sono gli stessi nomi di utenti e di gruppi, che però potrebbero corrispondere a numeri UID e GID<br />

differenti, diventa conveniente un metodo di archiviazione che ignori i numeri degli utenti e dei<br />

gruppi. Tuttavia, se alcuni nomi di utenti o gruppi non sono presenti nel sistema di destinazione,<br />

la proprietà di questi file verrebbe assegnata automaticamente all’utente ‘root’.<br />

Quando si esegue una copia di sicurezza di un intero sistema e poi lo si vuole riprodurre altrove,<br />

si agisce per mezzo di un sistema operativo minimo, avviato probabilmente attraverso dischetti o<br />

CD-ROM. In queste condizioni, lo scopo è quello di riprodurre esattamente il sistema originale,<br />

per cui, i numeri UID e GID andrebbero rispettati fedelmente, nell’attesa che sia ripristinato tutto,<br />

compresi i file ‘/etc/passwd’ e ‘/etc/group’ originali.<br />

Quando il programma di archiviazione memorizza entrambe le informazioni, sia UID/GID che<br />

i nomi, nel momento del recupero si pone il problema di come comportarsi quando questi non<br />

corrispondono. Si presentano queste alternative:<br />

• se i numeri corrispondono ai nomi, non si pongono problemi nell’estrazione;<br />

• se i numeri e i nomi non sono utilizzati nel sistema di destinazione, si possono estrarre i<br />

dati utilizzando i numeri originali, anche se non sono abbinati ad alcun nome;<br />

• se i numeri non sono utilizzati nel sistema di destinazione, mentre i nomi sì, si possono<br />

cambiare i numeri in modo che corrispondano ai nomi;<br />

• se i nomi non sono utilizzati nel sistema di destinazione, mentre i numeri corrispondono<br />

a nomi differenti, non si può fare altro che recuperare i numeri come solo, assegnando in<br />

pratica la proprietà a utenti e gruppi con nomi differenti;


10 volume VIII Argomenti avanzati e accessori<br />

• se sono presenti sia i nomi che i numeri, ma questi non hanno lo stesso abbinamento (cioè<br />

i numeri corrispondono a nomi diversi), dovrebbe essere preferibile cambiare i numeri in<br />

modo che corrispondano ai nomi.<br />

351.1.4 Percorsi assoluti o relativi<br />

Quando si intende archiviare una porzione di file system e quindi solo ciò che si trova a partire da<br />

una certa directory in poi, è importante sapere come si comporta il programma di archiviazione<br />

al riguardo della registrazione dei percorsi (path). Se si vuole archiviare la directory ‘/home/<br />

tizio/esempi/’, il programma di archiviazione potrebbe registrare il suo contenuto in uno dei<br />

tre modi seguenti:<br />

1. ‘/home/tizio/esempi/ * ’<br />

2. ‘home/tizio/esempi/ * ’<br />

3. ‘./ * ’<br />

Naturalmente, ciò dipende anche dal modo in cui vengono date le istruzioni al programma stesso.<br />

Nel primo caso, quando dovesse rendersi necessario il recupero dei dati, questi verrebbero collocati<br />

esattamente nella directory indicata, in modo assoluto. Nel secondo, verrebbero collocati<br />

in modo relativo a partire dalla directory corrente, ottenendo così la directory ‘./home/<br />

tizio/esempi/ * ’. Nel terzo caso si avrebbe il recupero del contenuto di quella directory senza<br />

informazioni sul percorso precedente.<br />

351.2 Strategia nelle copie<br />

Le copie di sicurezza permettono di conservare la situazione dei dati in un istante determinato,<br />

ma i dati sono soggetti a continui aggiornamenti. Per questo occorre una procedura attraverso la<br />

quale si possa avere una gestione ordinata e ragionevolmente sicura delle copie.<br />

A parte i rischi connessi con il tipo di supporto utilizzato per le copie e il luogo in cui queste<br />

vengono conservate, vanno almeno considerate le modalità sequenziali con cui queste possono<br />

essere eseguite. È importante rispettare un paio di regole elementari:<br />

1. non si riutilizzano i supporti contenenti la copia effettuata la volta precedente;<br />

2. non si utilizzano supporti in cattive condizioni.<br />

351.2.1 Generazioni delle copie<br />

Per distinguere una copia effettuata in un momento rispetto a quella fatta in un altro, si parla<br />

di generazione. In pratica, l’ultima copia di sicurezza effettuata è l’ultima generazione, mentre<br />

le altre sono tutte generazioni precedenti. Questo termine si riferisce naturalmente a copie fatte<br />

sullo stesso insieme di dati.<br />

Il buon senso suggerisce di utilizzare almeno tre generazioni di copie: l’ultima, quella precedente<br />

e quella ancora precedente.


Copie di sicurezza 11<br />

351.2.2 Livelli delle copie<br />

La copia di un file system intero comporta solitamente un impegno consistente, sia in termini di<br />

tempo che di supporti impiegati. Il programma che si utilizza per le copie, oppure un gruppetto<br />

opportuno di script di shell, potrebbe permettere di effettuare la copia successiva dei soli file che<br />

sono stati modificati nel frattempo.<br />

Quando si esegue una copia dei soli file che risultano diversi rispetto all’ultima copia completa,<br />

si parla di copia di primo livello; quando se ne esegue un’altra in un momento successivo per le<br />

variazioni avvenute dopo quella di primo livello, si parla di secondo livello e così di seguito.<br />

A parte le difficoltà legate alla conservazione dell’informazione sullo stato dei file (di solito<br />

si tratta della data di modifica e di creazione), si pongono poi dei problemi nel momento in<br />

cui dovesse essere necessario un ripristino dalle copie. Si dovrebbe ripristinare l’ultima copia<br />

completa, seguita da tutte quelle aggiuntive dei soli file modificati, nello stesso ordine in cui<br />

sono state fatte: dalla più vecchia alla più recente.<br />

Sotto questo aspetto, quando non si vuole ripetere una copia completa troppo frequentemente, si<br />

cerca almeno di eseguire copie successive sempre di primo livello (si hanno quindi più generazioni<br />

di copie di primo livello). In tal modo, un eventuale recupero richiederebbe solo il ripristino<br />

dell’ultima generazione di copia completa e dell’ultima generazione di copia di primo livello.<br />

Questo sistema potrebbe non tenere conto dei file cancellati dopo l’ultima generazione di copia<br />

completa: in tal modo, un eventuale recupero dalle copie di sicurezza potrebbe comportare il<br />

ripristino di file che non servono più.<br />

351.2.3 Distribuire le responsabilità<br />

In un sistema monoutente, l’unico utilizzatore è anche l’amministratore del proprio sistema e di<br />

conseguenza anche l’unico responsabile. Sarà quindi lui (o lei) a sapere esattamente cosa ha fatto<br />

e cosa è necessario copiare per sicurezza.<br />

In un sistema multiutente o comunque quando si condividono dati in gruppo, anche se a prima<br />

vista potrebbe sembrare conveniente la gestione delle copie in modo centralizzato, è comunque<br />

utile affidarla in parte anche alla responsabilità dei singoli:<br />

• periodicamente, un amministratore potrebbe occuparsi di eseguire la copia complessiva di<br />

tutto il sistema;<br />

• ogni giorno, gli utenti dovrebbero preoccuparsi di eseguire le copie dei dati di loro<br />

competenza.<br />

Nel momento in cui dovesse essere necessario, si dovrebbero recuperare i dati dalle copie generali<br />

fatte dall’amministratore e successivamente da quelle particolari dei singoli utenti.<br />

Se determinate attività vengono svolte in gruppo, si potrebbe eleggere ugualmente un<br />

responsabile all’interno di questo che si occupi delle copie di quell’attività.<br />

Il vantaggio di questo metodo sta nell’alleggerimento delle responsabilità dell’amministratore e<br />

nella soluzione più facile di piccoli problemi locali:<br />

• se un gruppo di lavoro ha alterato i dati a causa di un’operazione errata, è sufficiente<br />

recuperare i dati di quel gruppo senza disturbare l’intero sistema;<br />

• la distribuzione della responsabilità aumenta la consapevolezza da parte degli utenti.


12 volume VIII Argomenti avanzati e accessori<br />

351.3 Supporti<br />

La scelta del supporto di conservazione della copia è importante e comporta alcune conseguenze:<br />

• il costo;<br />

• la disponibilità di unità in grado di utilizzarli;<br />

• la facilità o difficoltà nel recupero di porzioni dei dati archiviati.<br />

Il supporto tradizionalmente più economico e più diffuso nel passato è il nastro magnetico. Questo<br />

ha però lo svantaggio fondamentale di essere un mezzo di memorizzazione sequenziale: non<br />

è possibile estrarre un file se prima non si scorre tutto il nastro (o tutti i nastri) che c’è prima di<br />

quel dato. Un altro svantaggio importante sta nella necessità di rileggere il suo contenuto, dopo<br />

la copia, per verificare che i dati siano stati registrati correttamente.<br />

Da alcuni anni si possono trovare dischi rimovibili di grandi capacità a prezzi ragionevolmente<br />

bassi, oppure dischi fissi esterni (collegati attraverso la porta USB). Questi hanno il vantaggio di<br />

poter essere utilizzati come dischi normali, pertanto, il recupero di dati parziali diventa molto più<br />

facile, anche quando la copia di sicurezza avviene per mezzo di un’archiviazione tradizionale.<br />

Anche i CD-R sono diventati un ottimo mezzo di archiviazione, data l’economicità dei supporti:<br />

benché sia possibile una sola registrazione, il prezzo di un CD vergine è molto contenuto. La<br />

preparazione di un CD-ROM richiede una certa quantità di spazio libero su disco per la preparazione<br />

dell’immagine prima dell’operazione di «incisione» (burn) e richiede anche l’investimento<br />

del masterizzatore. Si tratta di soldi ben spesi: una copia di sicurezza fatta su CD-ROM può essere<br />

letta ovunque ci sia un lettore, ma questo è ormai un accessorio standard degli elaboratori;<br />

inoltre, il CD-ROM ha una vita media molto lunga, garantendo la durata delle copie di sicurezza.<br />

Esiste tuttavia un problema nuovo: si deve essere prudenti con le copie obsolete. Infatti, quando<br />

le copie di sicurezza sono molto vecchie e non servono più, si può essere tentati di conservare i<br />

CD o di donarli a qualcuno, magari per gioco, o perché li usi come un addobbo. È evidente che<br />

si tratta di un’idea sbagliata: dal momento che questi CD-ROM sono stati usati per delle copie di<br />

sicurezza, contengono potenzialmente informazioni delicate e riservate.<br />

I CD-ROM contenenti copie di sicurezza obsolete vanno distrutti prima di essere gettati! Per<br />

distruggere un CD, basta tagliarlo a metà con una forbice normale.<br />

A fianco dei CD-R ci sarebbero anche i CD-RW, ovvero i CD riscrivibili; tuttavia, il CD-RW<br />

non è altrettanto affidabile di un CD-R, la velocità di scrittura è relativamente più bassa rispetto a<br />

un CD-R e ci possono essere lettori che non sono in grado, successivamente, di accedere al loro<br />

contenuto.<br />

Quando i dati da archiviare sono pochi, può convenire l’utilizzo dei soliti dischetti: sono sicuramente<br />

una scelta economica e le unità a dischetti sono disponibili ovunque. Per quanto riguarda la<br />

facilità di estrazione dei dati, ciò dipende dal modo con cui questi vengono usati: se si registrano<br />

i dati al loro interno senza fare uso di alcun file system, si ottiene un comportamento equivalente<br />

ai nastri; se si utilizzano con un file system, è necessario che l’archivio sia contenibile all’interno<br />

di un solo dischetto.


Copie di sicurezza 13<br />

351.4 Compressione<br />

Il problema della dimensione dei dati da archiviare può essere ridotto parzialmente con l’aiuto<br />

della compressione. La tecnica della compressione può essere applicata all’archiviazione in due<br />

modi possibili:<br />

• prima della costruzione dell’archivio, ottenendo così un’archivio di file compressi;<br />

• dopo la costruzione dell’archivio, ottenendo così un archivio compresso.<br />

La differenza è enorme. La compressione introduce un elemento di rischio maggiore nella perdita<br />

di dati: se una copia di sicurezza viene danneggiata parzialmente, l’effetto di questo danno si<br />

riflette in una quantità di dati maggiore (spesso è compromesso tutto l’archivio).<br />

351.4.1 Compressione prima dell’archiviazione<br />

I programmi di archiviazione compressa maggiormente diffusi negli ambienti Dos utilizzano<br />

la tecnica della compressione prima dell’archiviazione. È questo il caso degli archivi ‘.zip’,<br />

‘.arj’, ‘.lzh’ e di altri ancora. Tale sistema ha il vantaggio di permettere una facile scansione<br />

dell’archivio alla ricerca di file da estrarre (e decomprimere) o un ampliamento dell’archivio in<br />

un momento successivo alla sua creazione. Un altro vantaggio è la minore sensibilità alla perdita<br />

dei dati: se una parte dell’archivio è danneggiato, dovrebbe essere possibile ripristinare almeno il<br />

resto. Lo svantaggio principale è che la compressione fatta in questo modo, a piccoli pezzi, non<br />

è molto efficiente.<br />

351.4.2 Compressione dopo l’archiviazione<br />

La compressione fatta dopo l’archiviazione elimina ogni possibilità di accedere ai dati contenuti<br />

nell’archivio e di poterlo ampliare, se non dopo averlo decompresso. Questo significa anche che<br />

un danneggiamento parziale dell’archivio implica la perdita di tutti i dati da quel punto in poi. 1<br />

Un altro tipo di problema deriva dalla difficoltà di distribuire un archivio compresso suddividendolo<br />

su più unità di memorizzazione. In questo caso però, l’efficienza della compressione è<br />

massima. Negli ambienti Unix, di fatto, è questa la scelta preferita.<br />

351.5 Archiviazione e recupero attraverso tar e gzip<br />

La coppia Tar e Gzip rappresenta lo standard nell’archiviazione dei dati: Tar genera un archivio<br />

non compresso che può comprendere anche collegamenti simbolici e file speciali; Gzip lo<br />

comprime generando un archivio più piccolo.<br />

La coppia funziona così bene che Tar è in grado di utilizzare Gzip direttamente senza dover far<br />

uso di pipeline, purché il risultato dell’archiviazione non debba essere suddiviso su più supporti.<br />

L’origine del nome Tar è Tape archive, ma questo programma permette ugualmente di gestire<br />

qualunque altro tipo di sistema di memorizzazione.<br />

La versione GNU di Tar (quella utilizzata normalmente nelle distribuzioni GNU/Linux), non<br />

memorizza percorsi assoluti.<br />

I programmi Tar e Gzip sono descritti rispettivamente nelle sezioni 74.1.2 e 74.2.1.<br />

1 Ci sono programmi di archiviazione che si comportano così anche se non subiscono compressioni successive.


14 volume VIII Argomenti avanzati e accessori<br />

Negli esempi seguenti si immagina di dover archiviare il contenuto della directory ‘~/<br />

lettere/’, equivalente a ‘/home/tizio/lettere/’, comprese eventuali sottodirectory<br />

discendenti.<br />

Negli esempi si cerca di utilizzare la forma tradizionale per l’indicazione delle opzioni standard<br />

di Tar. Alcune di queste possono fare a meno del trattino iniziale, come nel caso di ‘c’ e ‘x’. Altre<br />

opzioni hanno quel trattino, ma possono essere aggregate in un’unica serie di lettere, come nel<br />

caso di ‘czvf’, dove si ha l’unione di: ‘c’, ‘-z’, ‘-v’ e ‘-f’.<br />

351.5.1 Archiviazione diretta su dispositivi di memorizzazione<br />

L’archiviazione attraverso la registrazione diretta sui dispositivi utilizza completamente il supporto<br />

di memorizzazione destinatario, anche se la quantità di dati da archiviare è molto<br />

piccola.<br />

Quello sotto indicato è un esempio di archiviazione in un nastro magnetico singolo: l’opzione<br />

‘c’ sta per Create; ‘-f’ sta per File e permette di definire la destinazione dell’archiviazione;<br />

‘-z’ attiva la compressione attraverso ‘gzip’. Dal momento che si utilizza la compressione,<br />

l’archiviazione multivolume non è ammissibile.<br />

# tar czf /dev/ftape ~/lettere<br />

I dischetti possono essere utilizzati come i nastri, in modo sequenziale, ma questo lo si fa soltanto<br />

quando l’archivio generato non è contenibile in un solo dischetto: si ha quindi una copia<br />

multivolume e in tal caso non è ammissibile l’uso della compressione.<br />

# tar cf /dev/fd0u1440 -M ~/lettere<br />

In questo caso, l’opzione ‘-M’ sta proprio per Multivolume indicando quindi la possibilità che il<br />

supporto di destinazione non sia in grado di contenere l’intero archivio. In tal modo, Tar si prende<br />

cura di sospendere l’archiviazione ogni volta che viene raggiunta la capienza massima. Tar non<br />

è in grado di determinare da solo questa capacità, per cui, nell’esempio, il file di dispositivo<br />

dell’unità a dischetti è stato indicato in modo da riconoscerne la geometria, ma in alternativa si<br />

poteva utilizzare l’opzione ‘-L’ seguita dalla dimensione:<br />

# tar cf /dev/fd0 -M -L 1440 ~/lettere<br />

Quando si utilizzano in questo modo, i dischetti non contengono un file system e di conseguenza<br />

non possono essere montati. La lettura del loro contenuto avviene nello stesso modo della<br />

scrittura, attraverso il nome del dispositivo.<br />

L’archiviazione su dischetti, attraverso l’indicazione del file di dispositivo, richiede comunque<br />

che questi siano già stati inizializzati (a basso livello) secondo il formato che viene indicato.<br />

Non conta che siano vuoti: è importante che ci siano le tracce e i settori come previsto.


Copie di sicurezza 15<br />

351.5.2 Archiviazione normale su file<br />

Quando l’archiviazione può essere fatta su dischi (con file system ) di dimensione sufficiente<br />

a contenere l’archivio intero, invece di utilizzare l’opzione ‘-f’ per specificare un file di dispositivo,<br />

si può indicare direttamente un normalissimo file al loro interno, come nell’esempio<br />

seguente:<br />

$ tar cf /mnt/mo1/lettere.tar ~/lettere<br />

In pratica, nel caso appena visto, si utilizza un disco montato nella directory ‘/mnt/mo1/’ e si<br />

crea il file ‘lettere.tar’ al suo interno.<br />

L’archiviazione compressa, con l’utilizzo di ‘gzip’, può essere ottenuta semplicemente con<br />

l’opzione ‘-z’, come nell’esempio seguente:<br />

$ tar czf /mnt/mo1/lettere.tar.gz ~/lettere<br />

In tal caso l’estensione standard utilizzata (ma non obbligatoria) è ‘.tar.gz’ che rende esplicito<br />

il fatto che la compressione è stata fatta dopo l’archiviazione. In alternativa si può usare anche<br />

‘.tgz’, diffusa nei sistemi Dos.<br />

351.5.3 Archiviazione e percorsi<br />

Gli esempi seguenti, pur archiviando gli stessi dati, mostrano un modo diverso di registrare i<br />

percorsi all’interno dell’archivio. La directory di lavoro nel momento in cui si avvia il comando,<br />

è ‘/home/tizio/’, corrispondente alla directory personale dell’utente.<br />

/home/tizio$ tar czf /mnt/mo1/lettere.tar.gz ~/lettere<br />

/home/tizio$ tar czf /mnt/mo1/lettere.tar.gz /home/tizio/lettere<br />

/home/tizio$ tar czf /mnt/mo1/lettere.tar.gz lettere<br />

/home/tizio$ tar czf /mnt/mo1/lettere.tar.gz ./lettere<br />

Nei primi due esempi, viene archiviata l’indicazione del percorso precedente, ma pur essendo<br />

stato dato in modo assoluto (‘/home/tizio/lettere’), questo viene reso relativo da Tar, eliminando<br />

la prima barra obliqua che si riferisce alla directory radice. Questo comportamento<br />

riguarda almeno la realizzazione GNU di Tar.<br />

Negli ultimi due esempi, viene archiviata l’indicazione della sola directory ‘lettere/’, sempre<br />

in modo relativo.<br />

351.5.4 Archiviazione di periodi<br />

I file sono forniti di informazioni orarie. In base a queste è possibile eseguire delle copie di sicurezza<br />

riferite a dei periodi. Le copie di sicurezza a più livelli possono essere ottenute in modo<br />

semplificato attraverso l’uso dell’opzione ‘-N’ seguita da una data di partenza: si ottiene l’archiviazione<br />

di quanto variato a partire da una certa data; di solito si utilizza quella dell’ultima archiviazione<br />

completa. Il concetto di variazione, in questo caso, si deve intendere come variazione<br />

del contenuto o degli attributi. Quindi si tratta della data di modifica o della data di «creazione».<br />

$ tar czf /mnt/mo1/lettere.tar.gz -N 20030801 ~/lettere


16 volume VIII Argomenti avanzati e accessori<br />

In questo caso, la data che segue l’opzione ‘-N’ rappresenta la mezzanotte del primo agosto 2003.<br />

$ tar czf /mnt/mo1/lettere.tar.gz -N "20030801 15:30" ~/lettere<br />

Questo ultimo esempio aggiunge alla data l’indicazione di un’ora particolare, 15:30; per evitare<br />

che sia interpretato in maniera errata, il gruppo data-orario viene racchiuso tra virgolette.<br />

351.5.5 Archiviazione limitata a un’unità<br />

Quando si eseguono delle copie di sicurezza, è probabile che si voglia archiviare solo la situazione<br />

di una certa unità di memorizzazione (partizione o directory condivisa in rete). In tal caso si<br />

deve indicare precisamente questo limite con l’opzione ‘-l’ (Limit).<br />

351.5.6 Estrazione dei percorsi<br />

Quando si accede all’archivio per estrarne il contenuto o per compararlo con i dati originali, entra<br />

in gioco il problema dei percorsi. I dati vengono estratti normalmente nella directory corrente,<br />

oppure vengono comparati utilizzando come punto di partenza la directory corrente. Quindi, se<br />

l’archivio contiene la directory degli esempi precedenti, registrata a partire dalla radice (ma come<br />

già spiegato, senza l’indicazione della radice stessa), questi verranno estratti in ‘./home/tizio/<br />

lettere/’, oppure comparati con i dati contenuti a partire da questo percorso.<br />

Se in fase di estrazione o comparazione si vuole fare riferimento a percorsi assoluti, si può utilizzare<br />

l’opzione ‘-P’. In questo modo si afferma esplicitamente che i percorsi indicati nell’archivio<br />

vanno considerati come discendenti dalla directory radice.<br />

Questo particolare della gestione dei percorsi è molto importante quando si fanno le copie di<br />

sicurezza: spesso si hanno dischi montati su punti di innesto provvisori, pertanto non è molto<br />

conveniente memorizzare anche il percorso su cui sono montati.<br />

351.5.7 Recupero<br />

Per poter effettuare un recupero di dati da un archivio è necessario conoscere in particolare il<br />

modo in cui questo era stato creato: normale, compresso, multivolume.<br />

In generale, per recuperare dati da un archivio si utilizza l’opzione ‘x’ (Extract) al posto di ‘c’ e<br />

a essa si devono eventualmente aggiungere ‘-z’ nel caso di estrazione da un archivio compresso<br />

con ‘gzip’ o ‘-M’ nel caso di un archivio multivolume.<br />

Durante il recupero di una copia di sicurezza è importante fare in modo che i dati riprodotti mantengano<br />

gli stessi attributi originali (permessi e proprietà). Per questo si aggiungono le opzioni<br />

‘-p’ (riproduce i permessi) e ‘--same-owner’ (riproduce le proprietà: UID e GID).<br />

L’esempio seguente mostra un recupero da un archivio multivolume su dischetti:<br />

~$ tar x -M -p --same-owner -f /dev/fd0u1440<br />

L’esempio seguente mostra un recupero con percorso assoluto: i percorsi indicati all’interno<br />

dell’archivio vengono aggiunti alla directory radice:<br />

$ tar xz -P -p --same-owner -f /mnt/mo1/lettere.tar.gz


Copie di sicurezza 17<br />

351.5.8 Recupero parziale<br />

Il recupero parziale del contenuto di un archivio Tar può essere fatto per file singoli o per directory,<br />

oppure attraverso l’uso di caratteri jolly. In questo ultimo caso però, occorre fare attenzione<br />

a evitare che la shell esegua l’espansione: è compito di Tar determinare a cosa corrispondano<br />

all’interno dei suoi archivi. Questo è un po’ quello che accade a Find con l’opzione ‘-name’: è<br />

Find stesso ad analizzare i caratteri jolly.<br />

Valgono le regole solite: l’asterisco rappresenta un insieme di caratteri qualunque; il punto interrogativo<br />

rappresenta un carattere qualsiasi; le parentesi quadre rappresentano un carattere a<br />

scelta tra un insieme o tra un intervallo determinato.<br />

Quando si indicano nomi di file o directory, o quando si utilizzano i caratteri jolly, occorre tenere<br />

presente che si sta facendo riferimento ai dati contenuti nell’archivio, con i percorsi memorizzati<br />

originariamente. Inoltre, se con i caratteri jolly si determina la corrispondenza con una directory,<br />

si ottiene l’estrazione del contenuto complessivo di quella.<br />

L’esempio seguente mostra in che modo potrebbero essere recuperate le lettere contenute nella<br />

directory ‘home/tizio/lettere/nuove/’ (l’esempio appare diviso su due righe, a causa della<br />

sua lunghezza):<br />

$ tar xz -P -p --same-owner -f /mnt/mo1/lettere.tar.gz ←↪<br />

↩→home/tizio/lettere/nuove<br />

L’esempio seguente mostra l’estrazione di tutti i file e delle directory corrispondenti a ‘home/<br />

tizio/lettere/ve * ’. Gli apici sono necessari per evitare che intervenga la shell a espandere<br />

l’asterisco, che invece deve essere interpretato da Tar.<br />

$ tar xz -P -p --same-owner -f /mnt/mo1/lettere.tar.gz ←↪<br />

↩→’home/tizio/lettere/ve*’<br />

351.5.9 Elenco e controllo<br />

Per ottenere un elenco del contenuto di un archivio e per compararne il contenuto con i dati<br />

originali, valgono le stesse regole del recupero dei dati. In particolare, al posto dell’opzione ‘x’<br />

si deve utilizzare ‘t’ (List) per gli elenchi e ‘d’ (Diff ) per la comparazione.<br />

351.6 Archiviazione di un file system<br />

L’archiviazione di un file system intero, va fatta considerando le caratteristiche di questo, in particolare<br />

della sua struttura fisica: partizioni e condivisione attraverso la rete. In generale dovrebbe<br />

essere conveniente l’archiviazione separata per ogni partizione e per ogni file system condiviso<br />

in rete.<br />

Oltre a questo occorre evitare di archiviare anche l’archivio che si sta creando: quando la destinazione<br />

dell’archiviazione è un file su disco, questo deve essere montato da qualche parte e per<br />

questo si potrebbe creare un circolo vizioso.<br />

Ci sono directory che, per la loro natura, non conviene o non devono essere archiviate: per<br />

‘/tmp/’ non conviene; con ‘/proc/’ dei sistemi GNU/Linux non si deve. In questi casi si deve<br />

solo ricordare di ricreare queste directory, nel momento in cui fosse necessario il recupero.<br />

Naturalmente non bisogna dimenticare i permessi: ‘/tmp/’ 17778 e ‘/proc/’ 05558.


18 volume VIII Argomenti avanzati e accessori<br />

351.6.1 Strumenti per il recupero<br />

L’archiviazione di copie di sicurezza non è sufficiente a garantirsi contro gli incidenti: in che<br />

modo si può avviare un elaboratore in cui è appena stato sostituito il disco fisso? Evidentemente,<br />

occorre essere più previdenti e predisporre in anticipo gli strumenti necessari per preparare le<br />

partizioni di un nuovo disco fisso e per recuperare i dati archiviati precedentemente.<br />

Questo argomento viene trattato nei prossimi capitoli.<br />

Appunti di informatica libera 2003.06.29 --- Copyright © 2000-2003 Daniele Giacomini -- daniele @ swlibero.org


Dischetti di emergenza con GNU/Linux<br />

Capitolo 352<br />

Nel momento dell’imprevisto si può agire solo se si è stati previdenti, pensando ai tipi di situazioni<br />

che si possono presentare e preparando gli strumenti necessari in anticipo. Le copie di<br />

sicurezza sono la prima cosa da fare per prepararsi ai guai, ma da sole non bastano: occorrono<br />

altri strumenti per rimettere in sesto un sistema prima di poter effettuare un eventuale recupero<br />

dalle copie.<br />

352.1 Dischetti<br />

Di fronte a un problema qualunque di una gravità tale da non permettere l’avvio di un sistema<br />

locale, l’unica possibilità di intervenire è data da strumenti su dischetti (oppure su CD-ROM, ma<br />

non è questo l’argomento del capitolo). Esistono diversi tipi di dischetti che possono essere stati<br />

preparati in precedenza:<br />

• dischetti di avvio;<br />

• dischetti di installazione della distribuzione GNU/Linux;<br />

• dischetti preparati appositamente.<br />

352.1.1 Dischetti di avvio<br />

Un dischetto di avvio può essere utile quando, per qualche motivo, il metodo normale di caricamento<br />

del sistema operativo non funziona più. Esistono almeno tre tipi di dischetti di questo<br />

tipo:<br />

• dischetti con un settore di avvio, ma senza kernel;<br />

• dischetti con un settore di avvio e con il kernel;<br />

• dischetti contenenti solo l’immagine del kernel.<br />

352.1.2 Settore di avvio senza il kernel<br />

La soluzione del dischetto senza kernel non è adatta per avviare un sistema in difficoltà: è solo<br />

un modo per verificare una configurazione di un sistema di avvio come LILO quando non si<br />

vuole interferire con l’MBR del disco fisso. In pratica, nel caso di LILO si ottiene semplicemente<br />

indicando nel file ‘/etc/lilo.conf’ la riga ‘boot=/dev/fd0’, come nell’esempio seguente:<br />

boot=/dev/fd0<br />

prompt<br />

timeout=50<br />

image=/boot/vmlinuz<br />

label=linux<br />

root=/dev/hda2<br />

read-only<br />

Quando viene avviato l’eseguibile ‘lilo’ con questa configurazione, si ottiene la scrittura del<br />

primo settore del primo dischetto (il dischetto deve essere stato inizializzato in precedenza e può<br />

19


20 volume VIII Argomenti avanzati e accessori<br />

anche non contenere alcun file system). Ma in questo modo si intende che i file per il caricamento<br />

del sistema si devono trovare nella directory ‘/boot/’ del momento in cui si esegue ‘lilo’,<br />

ovvero nel file system in funzione in quel momento e non nel dischetto. Inoltre, anche il kernel<br />

‘/boot/vmlinuz’ si intende contenuto in quel file system e non nel dischetto.<br />

Se si utilizza GRUB, il file ‘/boot/grub/menu.lst’ rimane lo stesso, ma si danno dei comandi<br />

differenti per inserire il settore di avvio nel dischetto. Si osservi che il dischetto deve essere<br />

inserito nell’unità prima di avviare l’eseguibile ‘grub’:<br />

# grub [ Invio ]<br />

grub> root (hd0,1) [ Invio ]<br />

Filesystem type is ext2fs, partition type 0x83<br />

grub> setup (fd0) [ Invio ]<br />

Checking if "/boot/grub/stage1" exists... yes<br />

Checking if "/boot/grub/stage2" exists... yes<br />

Checking if "/boot/grub/e2fs_stage1_5" exists... yes<br />

Running "embed /boot/grub/e2fs_stage1_5 (fd0)"... failed (this is not fatal)<br />

Running "embed /boot/grub/e2fs_stage1_5 (hd0,1)"... failed (this is not fatal)<br />

Running "install /boot/grub/stage1 d (fd0) /boot/grub/stage2 p ←↪<br />

↩→/boot/grub/menu.lst"... succeeded<br />

Done.<br />

grub> quit [ Invio ]<br />

Quando si avvia con un dischetto fatto in questo modo, il programma contenuto nel primo settore<br />

va alla ricerca del kernel e degli altri file, necessari per il caricamento del sistema, nel disco fisso<br />

nel momento dell’utilizzo di LILO o di GRUB. Se il sistema non si avvia perché questi file o il<br />

kernel sono stati spostati, a nulla serve un dischetto fatto in questo modo.<br />

352.1.3 Settore di avvio e kernel<br />

Per fare in modo che il dischetto avvii un kernel contenuto al suo interno, è necessario che questo<br />

dischetto contenga un file system e un sistema di avvio.<br />

Nel caso di LILO deve contenere una copia della directory ‘/boot/’ con il suo contenuto, la<br />

directory ‘/etc/’ con il file ‘lilo.conf’ e deve essere riprodotta anche directory ‘/dev/’ con<br />

il file di dispositivo ‘fd0’ (assieme agli altri file di dispositivo necessari a individuare i dischi o<br />

le partizioni a cui si vuole fare riferimento). Quindi è sufficiente eseguire ‘lilo’ con l’opzione<br />

‘-r’, come descritto nella sezione 18.4.<br />

Nel caso di GRUB, il dischetto deve contenere la directory ‘/boot/grub/’ con i file ‘stage1’,<br />

‘stage2’ e ‘menu.lst’. Il file del kernel può trovarsi in qualunque posizione (con qualunque<br />

nome), purché il file ‘menu.lst’ sia stato predisposto di conseguenza. Fatto questo, si rende<br />

avviabile il dischetto nel modo consueto di GRUB:<br />

# grub [ Invio ]<br />

grub> root (fd0) [ Invio ]<br />

grub> setup (fd0) [ Invio ]<br />

grub> quit [ Invio ]


Dischetti di emergenza con GNU/Linux 21<br />

È bene ricordare che esiste anche la possibilità di usare SYSLINUX, che permette di realizzare un<br />

dischetto con le stesse caratteristiche e con meno difficoltà. SYSLINUX è descritto nel capitolo<br />

16.<br />

Rispetto alla prossima tecnica, un dischetto contenente il sistema di avvio e il kernel, come appena<br />

descritto, è uno strumento di avvio più completo perché permette di specificare, sia attraverso<br />

la configurazione del sistema di avvio, sia per mezzo dei comandi che, a seconda del tipo sistema<br />

di avvio, possono essere impartiti prima del caricamento del kernel, alcuni parametri di avvio<br />

particolari del quale il proprio sistema potrebbe avere bisogno.<br />

352.1.4 Immagine del kernel<br />

L’ultima possibilità è la più semplice e, sotto questo aspetto, anche la più sicura: il file del kernel<br />

viene copiato sul dispositivo del dischetto, senza fare uso di alcun file system. Si può utilizzare<br />

uno dei due modi seguenti.<br />

# cp vmlinuz /dev/fd0<br />

# dd if=vmlinuz <strong>of</strong>=/dev/fd0<br />

Evidentemente, il file del kernel è speciale perché riesce ad avviare se stesso. Il kernel da solo,<br />

però, potrebbe non sapere quale dispositivo contiene il file system principale da montare al momento<br />

dell’avvio. È necessario utilizzare il programma ‘rdev’ per inserire questa e altre notizie<br />

nel kernel.<br />

Supponendo che si debba avviare la partizione ‘/dev/hda2’, inizialmente in sola lettura, si<br />

procede come segue per fare queste annotazioni in un kernel copiato in un dispositivo ‘/dev/<br />

fd0’:<br />

# rdev /dev/fd0 /dev/hda2<br />

# rdev -R /dev/fd0 1<br />

352.1.5 Dischetti di una distribuzione<br />

La maggior parte delle distribuzioni GNU/Linux predispone dei dischetti di emergenza che<br />

consentono generalmente di accedere al disco fisso e di fare delle piccole riparazioni.<br />

Tra tutti, i dischetti più rudimentali sono quelli della distribuzione Slackware. La loro semplicità<br />

è da considerare un pregio, dal momento che utilizzandoli ci si trova di fronte un sistema<br />

GNU/Linux più o meno tradizionale, senza ottimizzazioni particolari.<br />

352.1.6 Dischetti realizzati appositamente<br />

Ogni sistema ha le proprie caratteristiche ed esigenze. I dischetti di emergenza preparati da altri,<br />

oppure ottenuti da una distribuzione GNU/Linux, possono adattarsi a un certo insieme di<br />

situazioni, ma non a tutte.<br />

Quando si vuole essere sicuri di avere gli strumenti giusti al momento giusto, occorre che questi<br />

siano stati preparati e collaudati bene, in modo da non sprecare tempo inutilmente. In sostanza,<br />

la realizzazione o la personalizzazione di dischetti di emergenza è una tappa importante per chi<br />

vuole amministrare seriamente il proprio sistema.


22 volume VIII Argomenti avanzati e accessori<br />

352.2 Personalizzazione di dischetti di emergenza<br />

L’utilizzo di dischetti di emergenza preparati da altri è un buon punto di partenza, ma le<br />

particolarità che ogni sistema può avere consigliano almeno una personalizzazione del kernel.<br />

352.2.1 Loopback block device<br />

Per poter costruire o almeno personalizzare dei dischetti di emergenza è particolarmente utile<br />

attivare nel kernel la gestione diretta delle immagini di questi (sezione 29.2.7).<br />

In questo modo, un’immagine non compressa di un dischetto può essere montata con un comando<br />

simile a quello seguente:<br />

mount -o loop -t tipo_di_file_system file_immagine punto_di_innesto<br />

352.2.2 disco RAM<br />

Il file system principale può essere caricato in memoria centrale (RAM) e montato da lì. Si ottiene<br />

un cosiddetto disco RAM. A parte ogni considerazione sui vantaggi che questo può avere nelle<br />

prestazioni del sistema, si tratta di una modalità quasi obbligata per l’utilizzo di dischetti di<br />

emergenza. Infatti, un disco RAM può essere ottenuto a partire da un’immagine compressa: è il<br />

kernel stesso che la espande in memoria all’atto del caricamento. Quindi, si può fare stare in un<br />

dischetto un’immagine di dimensioni superiori alla sua capacità.<br />

Oltre a questo vantaggio, che però richiede la presenza di molta memoria RAM, un dischetto<br />

contenente un file system che è stato trasferito nella RAM, può essere rimosso subito dopo il<br />

suo caricamento, permettendo il riutilizzo dell’unità a dischetti, magari per accedere ad altri<br />

programmi di servizio non inclusi nel disco RAM.<br />

Per la gestione di un disco RAM occorre che il kernel sia configurato appositamente (29.2.7).<br />

352.2.3 Scostamento (<strong>of</strong>fset)<br />

Quando il kernel carica un disco RAM da un’immagine contenuta in un dischetto, deve conoscere<br />

la posizione di inizio di questa immagine. Ciò è importante quando sia il kernel che l’immagine<br />

da caricare risiedono nello stesso dischetto. Quando l’immagine da caricare nel disco RAM è<br />

contenuta in un dischetto separato, questa si troverà normalmente a partire dall’inizio di questo,<br />

cioè da uno scostamento pari a zero.<br />

352.3 Dischetti Slackware<br />

I dischetti della distribuzione Slackware sono i più semplici ed efficaci in situazioni di emergenza.<br />

Per essere avviati necessitano di un dischetto di avvio (boot) contenente il kernel, ma questo può<br />

essere eventualmente predisposto localmente in modo da avere a disposizione la configurazione<br />

più adatta al proprio sistema. Questi dischetti sono reperibili normalmente presso gli indirizzi<br />

seguenti:<br />

<br />

<br />

Il dischetto migliore per la soluzione di problemi è rappresentato dall’immagine compressa<br />

‘rescue.gz’. Se si intende utilizzare anche un dischetto di avvio ottenuto dalla distribuzione,


Dischetti di emergenza con GNU/Linux 23<br />

occorre sceglierlo in base alle indicazioni che si trovano nei file di testo inclusi nelle directory<br />

indicate.<br />

L’immagine ‘rootdsks/rescue.gz’ è compressa e contiene in pratica un disco in formato<br />

Ext2 di qualche mebibyte (simbolo: Mibyte). Questo implica che per poterne fare uso occorre<br />

molta memoria RAM.<br />

Nelle prime versioni della distribuzione Slackware era distribuita un’immagine ‘rescue.gz’<br />

molto più piccola, che poteva essere espansa e collocata comodamente su un dischetto da<br />

1440 Kibyte. Questa immagine è ancora disponibile e si trova nel percorso ‘rootdsks/<br />

obsolete/rescue.gz’. Il fatto di poterla decomprimere su un dischetto da 1440 Kibyte permette<br />

di evitare il caricamento nella RAM. Per elaboratori aventi fino a 8 Mibyte di memoria<br />

RAM, questo è l’unico dischetto di emergenza che possa essere utilizzato ragionevolmente.<br />

Se si intende utilizzare l’immagine ‘rootdsks/obsolete/rescue.gz’, è necessario<br />

avviare con un kernel in grado di gestire i vecchi binari a.out.<br />

352.3.1 Organizzazione dei dischetti Slackware<br />

Come già accennato, la distribuzione Slackware mette a disposizione immagini di dischetti di avvio,<br />

contenenti essenzialmente il kernel, assieme a immagini di dischetti contenenti il file system<br />

principale (root).<br />

Le immagini per l’avvio rappresentano dischetti con un file system normale, contenente un settore<br />

di avvio, la directory ‘/boot/’ e il kernel. Si tratta in pratica di dischetti realizzati in modo<br />

analogo a quanto descritto in precedenza nella sezione 352.1.1, quando si faceva riferimento a<br />

dischetti contenenti questi elementi. Le immagini dei dischetti di avvio, anche se non sono di<br />

dimensioni pari a quelle di un dischetto normale, non dovrebbero essere compresse e si possono<br />

copiare semplicemente sul dispositivo del dischetto di destinazione.<br />

# dd if=net.i <strong>of</strong>=/dev/fd0<br />

Le immagini dei dischetti contenenti il sistema minimo (root), sono invece dischetti di qualche<br />

mebibyte, compressi in modo da poter essere collocati all’interno di un dischetto da 1440 Kibyte,<br />

costringendo però all’uso di un disco RAM.<br />

352.3.2 Utilizzare un kernel personalizzato<br />

Per abbinare un kernel personalizzato a un dischetto contenente il sistema minimo della distribuzione<br />

Slackware, si potrebbe ricostruire un dischetto di avvio seguendo le stesse modalità usate<br />

dalla distribuzione stessa, oppure in maniera più semplice, copiando il kernel in un dischetto<br />

direttamente attraverso il suo dispositivo e poi intervenendo con il programma ‘rdev’. Viene<br />

descritta l’ultima di queste modalità.<br />

# dd if=bzImage <strong>of</strong>=/dev/fd0<br />

La copia di un kernel in un dischetto, attraverso il suo dispositivo, genera il solito dischetto<br />

di avviamento già descritto tante volte. Questo kernel su dischetto deve però essere informato<br />

di dove e come fare il caricamento del sistema. Il file system principale viene caricato da un<br />

dischetto, quindi si scrive questo messaggio nel kernel attraverso ‘rdev’.


24 volume VIII Argomenti avanzati e accessori<br />

# rdev /dev/fd0 /dev/fd0<br />

I dischetti contenenti il sistema minimo della distribuzione Slackware non prevedono il controllo<br />

del file system e il successivo montaggio in lettura e scrittura. In pratica, il file system principale<br />

deve essere montato inizialmente in lettura-scrittura.<br />

# rdev -R /dev/fd0 0<br />

Infine si deve specificare che:<br />

• l’immagine del dischetto contenente il sistema (compressa o meno che sia) si trova in un<br />

dischetto separato e parte dalla posizione iniziale: lo scostamento è pari a zero;<br />

• si vuole, oppure non si vuole, che tale dischetto sia caricato in un disco RAM;<br />

• si vuole un preavviso per sapere quando si può togliere il dischetto del kernel per inserire<br />

il dischetto contenente il sistema.<br />

Per fare questo si agisce su una serie di bit configurabili attraverso ‘rdev’ con l’opzione ‘-r’:<br />

• i primi 11 (dal bit 0 al bit 10) permettono di definire l’indirizzo dello scostamento (in<br />

blocchi di 1024 byte);<br />

• il bit 14 indica che si vuole caricare un disco RAM;<br />

• il bit 15 indica che si vuole avere una pausa per lo scambio dei dischetti.<br />

Se si vuole caricare il file system principale in un disco RAM si deve utilizzare ‘rdev’ nel modo<br />

seguente:<br />

# rdev -r /dev/fd0 49152<br />

infatti, 2 15 + 2 14 + 0 = 49152.<br />

Se invece non si vuole il disco RAM si deve utilizzare ‘rdev’ nel modo seguente:<br />

# rdev -r /dev/fd0 32768<br />

infatti, 2 15 + 0 + 0 = 32768.<br />

352.4 Kernel per dischetti di emergenza<br />

Quando si configura un kernel da utilizzare assieme a dischetti di emergenza, occorre tenere presente<br />

che non è ragionevolmente possibile utilizzare i moduli ed è importante attivare determinate<br />

caratteristiche che di solito non vengono considerate per i sistemi normali.<br />

• Gestione dei binari a.out.<br />

I dischetti di emergenza obsoleti sono nel vecchio formato a.out. Il kernel deve essere stato<br />

predisposto per gestirli:<br />

Kernel support for a.out binaries


Dischetti di emergenza con GNU/Linux 25<br />

• File system Minix.<br />

Il file system più utilizzato in passato per i dischetti di emergenza, specialmente quando<br />

questi dovevano essere di dimensioni minime, è Minix. Anche se adesso i dischetti di<br />

emergenza che si trovano in circolazione sono prevalentemente in formato Ext2, conviene<br />

includere la gestione di questo vecchio tipo di formato:<br />

Minix fs support<br />

• dischi RAM.<br />

Anche se non si intendono utilizzare dischetti caricati in memoria RAM, vale la pena di<br />

preparare un kernel che ne permetta comunque l’utilizzo:<br />

RAM disk support<br />

• Porta parallela PLIP.<br />

Un kernel per un dischetto di emergenza deve permettere la gestione della rete (TCP/IP)<br />

e, in particolare, invece di attivare la gestione della porta parallela per la stampa, conviene<br />

attivare la gestione della connessione PLIP:<br />

PLIP (parallel port) support<br />

352.4.1 Tastiera<br />

Quando si usano dei dischetti di emergenza si hanno già molte limitazioni e a queste si aggiunge<br />

anche la scomodità di una tastiera che non combacia con quella USA.<br />

Si può risolvere il problema direttamente nel kernel senza dover tentare di inserire il programma<br />

‘loadkeys’ in dischetti già troppo piccoli. È sufficiente trovare il file della mappa della tastiera<br />

italiana (di solito si tratta del file ‘it.kmap’ collocato nella directory ‘/usr/share/keymaps/<br />

piattaforma/qwerty/’) e quindi generare il sorgente ‘defkeymap.c’. Si procede come segue,<br />

nel caso si utilizzi la piattaforma i386.<br />

# cd /usr/src/linux/drivers/char<br />

# loadkeys --mktable /usr/share/keymaps/i386/qwerty/it.kmap ←↪<br />

↩→> defkeymap.c<br />

La compilazione successiva di un nuovo kernel utilizzerà la mappa italiana come predefinita e<br />

non ci sarà bisogno di utilizzare ‘loadkeys’.<br />

352.5 Cavi<br />

Quando si ha la disponibilità di più elaboratori, è probabile che il danno presentatosi su uno di<br />

questi non si sia riprodotto in tutti. Una piccola rete locale potrebbe essere di aiuto in situazioni di<br />

emergenza e in sua mancanza potrebbero andare bene anche dei cavi paralleli PLIP. Questo tipo<br />

di cavo viene descritto nella parte lxxxvii. La sua realizzazione non è difficile: basta un piccolo<br />

saldatore, un po’ di stagno, due connettori maschi DB-25 e una piattina multipolare con almeno<br />

13 fili. La schermatura non è necessaria.<br />

Con i dischetti della distribuzione Slackware, preferibilmente con l’immagine ‘resque.gz’, è<br />

possibile stabilire una semplice connessione con un servente NFS.


26 volume VIII Argomenti avanzati e accessori<br />

352.5.1 Ethernet<br />

Attraverso una connessione Ethernet, con un’interfaccia riconosciuta come ‘eth0’, si può agire<br />

come nell’esempio seguente. Si suppone in particolare che l’indirizzo di rete sia 192.168.1.0,<br />

che la maschera di rete sia 255.255.255.0 e di poter utilizzare l’indirizzo IP 192.168.1.17 per<br />

l’elaboratore avviato con i dischetti di emergenza.<br />

# ifconfig eth0 192.168.1.17 netmask 255.255.255.0<br />

# route add -net 192.168.1.0 netmask 255.255.255.0 dev eth0<br />

Per verificare la connessione si può fare un ‘ping’ verso l’elaboratore da raggiungere: potrebbe<br />

trattarsi dell’indirizzo 192.168.1.1.<br />

# ping 192.168.1.1<br />

Se tutto è andato bene si può procedere. Si suppone che l’elaboratore 192.168.1.1 metta a<br />

disposizione il suo file system a partire dalla directory radice.<br />

# mount -t nfs 192.168.1.1:/ /mnt<br />

352.5.2 PLIP<br />

Nel caso di una connessione PLIP, la procedura è un po’ differente. In particolare bisogna ricordare<br />

che l’elaboratore dal quale si vogliono attingere i dati attraverso il protocollo NFS, deve<br />

avere un kernel compilato in modo da gestire questo tipo di connessione.<br />

Si fa riferimento allo stesso esempio riportato nella sezione precedente. L’unica differenza sta<br />

nell’interfaccia usata per la comunicazione: si suppone che sia stata riconosciuta la ‘plip1’ da<br />

entrambi i lati.<br />

Il procedimento di connessione va fatto da entrambi i capi, infatti, raramente un elaboratore<br />

ha una connessione PLIP stabile, per cui non si trova ad avere un indirizzo e una tabella di<br />

instradamento già pronti.<br />

Dal lato dell’elaboratore avviato con i dischetti si procede come segue:<br />

rescue# ifconfig plip1 192.168.1.17 pointopoint 192.168.1.1<br />

rescue# route add -host 192.168.1.17 dev plip1<br />

rescue# route add -host 192.168.1.1 dev plip1<br />

Dal lato dell’elaboratore servente si effettua l’operazione inversa:<br />

server# ifconfig plip1 192.168.1.1 pointopoint 192.168.1.17<br />

server# route add -host 192.168.1.1 dev plip1<br />

server# route add -host 192.168.1.17 dev plip1<br />

Per verificare la connessione si può provare con una richiesta di eco:<br />

rescue# ping 192.168.1.1<br />

Se tutto è andato bene si può procedere montando il file system di rete:


Dischetti di emergenza con GNU/Linux 27<br />

rescue# mount -t nfs 192.168.1.1:/ /mnt<br />

352.5.3 Considerazioni accessorie<br />

Il dischetto di emergenza ha bisogno di un altro punto di innesto per accedere a un disco fisso<br />

locale. È sufficiente creare un’altra directory.<br />

Quando si accede a un servente NFS e non è possibile farlo mantenendo i privilegi dell’utente<br />

‘root’, una semplice copia attraverso ‘cp -dpR’ non dà un risultato garantito: alcuni file potrebbero<br />

risultare inaccessibili in lettura. La cosa si risolve facilmente impacchettando quello che<br />

serve nell’elaboratore di origine e dando a questo archivio tutti i permessi necessari.<br />

352.6 Utenti e gruppi<br />

Quando si utilizza un sistema operativo minimo, avviato attraverso dischetti di emergenza, per<br />

recuperare i dati da uno o più archivi, occorre fare mente locale al problema dell’abbinamento<br />

utenti/gruppi, UID/GID.<br />

Trattandosi di un sistema minimo, conterrà alcuni nomi di utenti e di gruppi, presumibilmente<br />

non «umani», ma comunque esistenti. Solitamente, questi nomi di utenti e di gruppi sono standardizzati,<br />

tuttavia il loro abbinamento con numeri UID/GID non è sempre uniforme. A questo<br />

punto, se si recuperano i dati di un sistema in cui questi nomi non corrispondono esattamente,<br />

si rischia di riprodurre una copia differente, che non sarà valida quando il sistema normale sarà<br />

ripristinato.<br />

Se non ci sono alternative, si può accettare l’inconveniente, riavviare il sistema rigenerato e<br />

ripetere il recupero. In questo modo, i file verranno sovrascritti e le proprietà saranno abbinate in<br />

base ai nuovi file ‘/etc/passwd’ e ‘/etc/group’.<br />

In generale, proprio per questo problema, sarebbe opportuno che il dischetto di emergenza<br />

contenesse esclusivamente l’indicazione dell’utente e del gruppo ‘root’, eliminando<br />

qualunque altro tipo di utente di sistema.<br />

352.7 Riferimenti<br />

• Small Linux<br />

<br />

• LOAF: Linux on a floppy<br />

<br />

• Tomsrtbt: the most GNU/Linux on one floppy<br />

<br />

• Trinux<br />

<br />

Appunti di informatica libera 2003.06.29 --- Copyright © 2000-2003 Daniele Giacomini -- daniele @ swlibero.org


28 volume VIII Argomenti avanzati e accessori


Parte lxx<br />

Informatica gestionale<br />

353 Codici a barre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30<br />

353.1 Codice di controllo «modulo n» . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30<br />

353.2 UPC-E e UPC-A . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31<br />

353.3 EAN-8 e EAN-13 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31<br />

353.4 Code 39 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37<br />

353.5 Code 128 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .39<br />

353.6 ITF, ovvero i25 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43<br />

353.7 Sistemi bidimensionali . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44<br />

353.8 Riferimenti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44<br />

354 Barcode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46<br />

354.1 Utilizzo del programma . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .46<br />

354.2 Codifica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .47<br />

355 Trasformazione in lettere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49<br />

355.1 Da numero a sequenza alfabetica pura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49<br />

355.2 Da numero a numero romano . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .50<br />

355.3 Da numero a lettere, nel senso verbale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .53<br />

29


Codici a barre<br />

Capitolo 353<br />

I codici a barre sono dati memorizzati in forma ottica, attraverso l’uso di barre verticali che<br />

possono essere lette e interpretate facilmente con strumenti non troppo complessi. La tecnica dei<br />

codici a barre nasce negli anni 1960 e si diffonde negli anni 1970, con lo scopo di identificare<br />

rapidamente imballaggi e merci.<br />

Gli standard sui codici a barre sono molti e il problema più importante da risolvere quando<br />

si vogliono usare è il decidere quale sia quello più conveniente per i propri fini. Si parla di<br />

simbologia per fare riferimento al tipo di codice a barre, ovvero allo standard di rappresentazione<br />

dei dati; la simbologia definisce implicitamente il tipo di dati che possono essere memorizzati.<br />

Per fare un esempio abbastanza comune, i codici ISBN dei libri sono scritti usando codici a<br />

barre di tipo EAN-13, che si compongono di 13 cifre numeriche; in questo caso, la simbologia è<br />

EAN-13, con la quale si vanno a rappresentare in pratica i codici ISBN.<br />

Le rappresentazioni normali di un codice a barre sono a una sola dimensione, ovvero, è prevista<br />

una lettura orizzontale unica. Nella lettura vengono attraversate barre verticali nere e spazi bianchi;<br />

queste barre di larghezze differenti e gli spazi di ampiezze diverse sono gli elementi della<br />

simbologia; ogni simbologia usa un proprio numero di elementi differenti. Nel capitolo vengono<br />

descritte brevemente alcune simbologie standard.<br />

353.1 Codice di controllo «modulo n»<br />

Alcuni tipi di codici numerici utilizzano un numero di controllo (o codice di controllo) per permettere<br />

una verifica facile dell’integrità del dato rappresentato. Esiste un genere comune di algoritmi<br />

per il calcolo di tali numeri di controllo, a cui si fa spesso riferimento con la definizione<br />

modulo n, dove n è un numero intero maggiore di uno.<br />

In questo modo si fa riferimento a una somma di valori, ottenuta moltiplicando ogni cifra del<br />

numero di partenza, compreso il numero di controllo, per un certo peso (un altro numero intero<br />

positivo), che deve essere un multiplo del numero n stabilito. Si osservi l’esempio seguente, in<br />

cui il numero di controllo finale è calcolato con i pesi dati, dividendo per il modulo 5:<br />

5 + 12 + 15 + 14 + 4 = 50<br />

Valore 1 3 5 7 4<br />

Peso 5 4 3 2 1<br />

Peso*Valore 5 12 15 14 4<br />

50 è divisibile perfettamente per cinque, senza lasciare resti, garantendo l’integrità del valore.<br />

In pratica, il peso che si dà al valore da usare come numero di controllo è solitamente uno, per<br />

cui diventa facile il calcolo di questa cifra:<br />

5 + 12 + 15 + 14 = 46;<br />

46 / 5 = 9, con il resto di 1;<br />

5 - 1 = 4, ovvero il valore cercato come numero di controllo.<br />

30


Codici a barre 31<br />

353.2 UPC-E e UPC-A<br />

UPC è un insieme di simbologie standard utilizzato in particolare negli Stati Uniti e nel Canada.<br />

Con UPC-E e UPC-A si possono rappresentare solo cifre numeriche: UPC-A consente di rappresentare<br />

11 cifre più una di controllo; UPC-E consente di rappresentare sei cifre, senza codice di<br />

controllo (il controllo di integrità avviene in forma differente).<br />

La figura 353.1 mostra rispettivamente l’esempio di 123456 con UPC-E e di 12345678901 con<br />

UPC-A.<br />

Figura 353.1. Esempi di codici a barre con simbologia UPC-E e UPC-A.<br />

0 123456 5 1 23456 78901 2<br />

Gli elementi utilizzati nella simbologia UPC sono otto, composti da quattro tipi di barre nere e<br />

quattro tipi di spazi.<br />

Il codice di controllo di UPC-A si calcola moltiplicando in modo alterno le cifre che compongono<br />

il numero, per tre, o per uno, sommando alla fine i risultati. Ciò che si ottiene si divide per 10 e<br />

si tiene il resto; infine, 10 meno il resto ottenuto dà il codice di controllo cercato (modulo 10).<br />

Per esempio, nel caso di 12345678901 si ottiene il codice di controllo due, in base al calcolo<br />

seguente:<br />

Valore 1 2 3 4 5 6 7 8 9 0 1<br />

Peso 3 1 3 1 3 1 3 1 3 1 3<br />

Peso*Valore 3 2 9 4 15 6 21 8 27 0 3<br />

Il totale che si ottiene è 98; pertanto: 98/10 = 9 con un resto di 8; 10-8 = 2.<br />

353.3 EAN-8 e EAN-13<br />

EAN (European article number) è un insieme di simbologie standard di origine europea,<br />

utilizzato anche in altri paesi, nato come estensione delle simbologie UPC.<br />

EAN-8 e EAN-13 permettono di rappresentare solo cifre numeriche: EAN-8 consente di rappresentare<br />

sette cifre più una di controllo, mentre EAN-13 consente di rappresentare 12 cifre<br />

numeriche più una di controllo.<br />

La figura 353.2 mostra rispettivamente l’esempio di 1234567 con EAN-8 e di 123456789012<br />

con EAN-13.<br />

Figura 353.2. Esempi di codici a barre con simbologia EAN-8 e EAN-13.<br />

1234 5670 1 234567 890128


32 volume VIII Argomenti avanzati e accessori<br />

Il codice di controllo di EAN-8 e di EAN-13 si calcola nello stesso modo di UPC-A. Per esempio,<br />

nel caso di 1234567 (EAN-8) si ottiene il codice di controllo zero, mentre nel caso di<br />

123456789012 (EAN-13) si ottiene otto.<br />

Valore 1 2 3 4 5 6 7<br />

Peso 3 1 3 1 3 1 3<br />

Peso*Valore 3 2 9 4 15 6 21<br />

Il totale che si ottiene è 60; pertanto: 60/10 = 6 con un resto di 0; 10-0 = 10, da cui si prende<br />

solo l’ultima cifra (lo zero).<br />

Valore 1 2 3 4 5 6 7 8 9 0 1 2<br />

Peso 1 3 1 3 1 3 1 3 1 3 1 3<br />

Peso*Valore 1 6 3 12 5 18 7 24 9 0 1 6<br />

Il totale che si ottiene è 92; pertanto: 92/10 = 9 con un resto di 2; 10-2 = 8.<br />

La rappresentazione di ogni cifra numerica può avere tre forme differenti, a cui si attribuisce<br />

convenzionalmente una lettera alfabetica maiuscola: A, B e C. Nella simbologia EAN-8, le prime<br />

quattro cifre sono rappresentate secondo la forma A, mentre le ultime quattro secondo la forma C.<br />

Invece, la rappresentazione delle cifre nella simbologia EAN-13 è più complessa, per l’esigenza<br />

di essere compatibile con UPC-A.<br />

Con EAN-13 si rappresentano effettivamente solo le ultime 12 cifre (incluso il codice di controllo),<br />

mentre la prima viene determinata in base al modo in cui vengono rappresentate le sei cifre<br />

successive. In pratica, a seconda del valore della prima cifra, si determina la combinazione delle<br />

forme A e B, per le sei cifre che seguono la prima, mentre le ultime sei sono rappresentate con la<br />

forma C.<br />

Valore iniziale<br />

0 A A A A A A<br />

1 A A B A B B<br />

2 A A B B A B<br />

3 A A B B B A<br />

4 A B A A B B<br />

5 A B B A A B<br />

6 A B B B A A<br />

7 A B A B A B<br />

8 A B A B B A<br />

9 A B B A B A<br />

Dallo specchietto si può notare che con una prima cifra pari a zero, le sei cifre successive si<br />

rappresentano secondo la forma A, esattamente come avviene nella simbologia UPC-A. In pratica,<br />

a parte qualche differenza estetica minima, un codice a barre realizzato con la simbologia<br />

EAN-13 è uguale a un altro realizzato con la simbologia UPC-A, quando la prima cifra è pari a<br />

zero, tenendo conto che lo zero iniziale non può cambiare nulla anche nel calcolo del codice di<br />

controllo finale.


Codici a barre 33<br />

353.3.1 Codici UCC ed EAN<br />

In generale, la simbologia EAN-13 viene utilizzata per identificare degli «articoli», intesi principalmente<br />

come prodotti in vendita. Per poter applicare un codice a barre del genere occorre<br />

naturalmente avere ottenuto il numero per uno scopo preciso (l’ente italiano competente per<br />

l’attribuzione dei codici EAN è Indicod); tuttavia esiste un prefisso iniziale che fa parte dello<br />

standard e serve a stabilire l’origine del codice. l’origine è un concetto riferito precisamente a<br />

chi ha rilasciato il numero, che nella maggior parte dei casi si riferisce a un’organizzazione con<br />

competenza nazionale.<br />

Questo prefisso iniziale dipende principalmente da due organizzazioni: UCC (Uniform code<br />

council) ed EAN (European article number). La prima organizzazione, definisce codici in cui<br />

la prima cifra identifica l’origine nell’area nell’ambito di Stati Uniti e Canada, mentre la seconda<br />

copre tutto il mondo, includendo la codifica UCC a cui si aggiunge la cifra iniziale zero.<br />

La tabella 353.6 mostra in pratica un elenco abbastanza completo dei prefissi EAN, che solitamente<br />

si applicano ai codici a barre con simbologia EAN-13. Si può osservare che ormai i codici<br />

assegnati all’area Stati Uniti e Canada non hanno più solo lo zero iniziale; resta comunque l’abbinamento<br />

tra codice UCC e codice EAN, per cui il primo si trasforma nel secondo aggiungendo<br />

uno zero iniziale.<br />

Tabella 353.6. Attribuzione dei prefissi secondo la codifica EAN. Prima parte.<br />

Prefisso Contesto<br />

ISO<br />

3166<br />

Prefisso<br />

Contesto<br />

ISO 3166<br />

00-13 USA, Canada US, CA 14-19<br />

2 uso interno 30-37 Francia FR<br />

380<br />

384<br />

Bulgaria BG 383<br />

385<br />

Slovenia<br />

Croazia<br />

Bosnia<br />

SI<br />

HR<br />

386 387<br />

Erzego- BA<br />

vina<br />

388-<br />

389<br />

400-440<br />

GermaniaFede-<br />

DE<br />

45 Giappone JP 460-469 razione RU<br />

russa<br />

470 471 Taiwan TW<br />

472-<br />

473<br />

475 Lettonia LV<br />

474<br />

476<br />

Estonia<br />

Azerbaijan<br />

EE<br />

AZ<br />

477<br />

479<br />

481<br />

Lituania<br />

Sri Lanka<br />

Belarus<br />

LT<br />

LK<br />

BY<br />

478<br />

480<br />

482<br />

UzbekistanFilippine<br />

Ucraina<br />

UZ<br />

PH<br />

UA<br />

483<br />

485 Armenia AM<br />

484<br />

486<br />

Moldavia<br />

Georgia<br />

MD<br />

GE<br />

487 Kazakistan KZ 488<br />

489 Hong Kong HK 49<br />

Giappone<br />

JP<br />

50 Gran Bretagna UK 51<br />

520 Grecia GR 521-527<br />

528 Libano LB 529 Cipro CY<br />

530 531<br />

Macedonia<br />

MK


34 volume VIII Argomenti avanzati e accessori<br />

Prefisso Contesto<br />

ISO<br />

3166<br />

Prefisso<br />

Contesto<br />

ISO 3166<br />

532-<br />

534<br />

536-<br />

538<br />

54<br />

Belgio,<br />

Lussemburgo<br />

BE, LU<br />

535<br />

539<br />

55<br />

Malta<br />

Irlanda<br />

MT<br />

IE<br />

560 Portogallo PT 561-568<br />

569<br />

58<br />

Islanda IS 57<br />

590<br />

Danimarca<br />

Polonia<br />

DK<br />

PL<br />

591-<br />

593<br />

595-<br />

598<br />

600-<br />

601<br />

Sud Africa ZA<br />

594<br />

599<br />

602-608<br />

RomaniaUngheria<br />

RO<br />

HU<br />

609 Mauritius MU 610<br />

611 Marocco MA 612<br />

613 Algeria DZ 614-615<br />

616 Kenia KE 617-618<br />

619 Tunisia TN 620<br />

621 Siria SY 622 Egitto EG<br />

623 624 Libia LY<br />

625 Giordania JO 626 Iran IR<br />

627 Kuwait KW 628<br />

Arabia<br />

Saudita<br />

SA<br />

629 Emirati AE 63<br />

64 Finlandia FI 65-68<br />

690-<br />

693<br />

70<br />

Cina<br />

Norwegia<br />

CN<br />

NO<br />

694-699<br />

710-728<br />

729 Israele IL 73 Svezia SE<br />

740<br />

742<br />

Guatemala<br />

Honduras<br />

GT<br />

HN<br />

741<br />

743<br />

El SalvadorNicaragua<br />

SV<br />

NI<br />

744 Costa Rica CR 745 Panama PA<br />

746<br />

Repubblica Dominicana<br />

DO 747-749<br />

750 Messico MX 751-758<br />

759 Venezuela VE 76<br />

Svizzera<br />

CH<br />

770 Colombia CO 771-772<br />

773 Uruguay UY 774<br />

775 Perù PE 776<br />

777 Bolivia BO 778<br />

779 Argentina AR 780 Cile CL<br />

781-<br />

783<br />

784<br />

Paraguay<br />

PY<br />

785<br />

787-<br />

788<br />

80-83 Italia IT<br />

786<br />

789<br />

84<br />

Ecuador<br />

Brasile<br />

Spagna<br />

EC<br />

BR<br />

ES<br />

850 Cuba CU 851-857<br />

858 Slovacchia SK 859 Czech CZ<br />

860 Jugoslavia YU 861-866


Codici a barre 35<br />

Prefisso Contesto<br />

ISO<br />

3166<br />

Prefisso<br />

Contesto<br />

ISO 3166<br />

867 Nord Korea KP 868<br />

869 Turchia TR 87 Olanda NL<br />

880 Sud Korea KR 881-884<br />

885 Thailandia TH 886-887<br />

888 Singapore SG 889<br />

890 India IN 891-892<br />

893 Vietnam VN 894-898<br />

899 Indonesia ID 90-91 Austria AT<br />

92 93<br />

94 Nuova Zelanda NZ 950-954<br />

955 Malaysia MY 956-957<br />

958 Macau MO 959-976<br />

977<br />

980<br />

983-<br />

989<br />

periodici<br />

(ISSN)<br />

ricevute di rimborso<br />

(refund<br />

receipts)<br />

978-979<br />

981-982<br />

99<br />

Australia<br />

libri<br />

(ISBN)<br />

tagliandi<br />

di<br />

valuta<br />

(commoncurrencycoupons)tagliandi(coupons)<br />

Si può osservare in particolare che il prefissi 978 e 979 sono riservati per i libri, mentre il prefisso<br />

977 è riservato per i periodici (riviste). Dal momento che il prefisso EAN definiva originariamente<br />

un’area nazionale, era stato attribuito al codice 978 la denominazione bookland (la terra dei<br />

libri). Molta documentazione sui codici a barre e sulla numerazione ISBN fa ancora riferimento<br />

a questo nome per indicare tale prefisso.<br />

Un altro prefisso interessante è il due, che rimane libero. Molti negozi usano codici a barre EAN-<br />

13, con prefisso due per annotare il prezzo di qualcosa che non è identificabile diversamente,<br />

come gli alimentari ottenuti da un banco di salumi e formaggi. La figura 353.3 mostra un esempio<br />

reale in cui, tra le altre cose, si annota un prezzo di 11,24 e.<br />

Figura 353.3. Esempio di un codice a barre con simbologia EAN-13, usato per<br />

annotare un prezzo.<br />

2 000123 011240<br />

AU


36 volume VIII Argomenti avanzati e accessori<br />

353.3.2 ISBN<br />

La simbologia EAN-13 viene usata in diverse situazioni comuni, in particolare per ciò che<br />

riguarda le pubblicazioni.<br />

La codifica ISBN (International standard book number) permette di identificare un libro in una<br />

sua edizione precisa, attraverso un numero composto da 10 cifre. Il numero in questione contiene<br />

quattro parti: l’origine (ovvero l’ente che lo ha rilasciato), l’editore, il titolo (il libro) e un numero<br />

di controllo finale (una sola cifra).<br />

Per facilitare la lettura di queste informazioni, le quattro parti vengono mostrate solitamente<br />

come separate, attraverso un trattino. Per esempio, ‘88-8331-223-6’ indica l’origine 88 (Italia),<br />

l’editore 8331, il titolo 233 e il numero di controllo 6.<br />

Le varie parti in cui si suddivide l’informazione contenuta in un numero ISBN non sono di<br />

lunghezza fissa e la dimensione dell’una limita quella delle altre. In generale sono disponibili i<br />

raggruppamenti seguenti.<br />

1. La prima parte identifica l’area (nazionale, linguistica e geografica) e viene attribuita<br />

dall’agenzia internazionale dell’ISBN. Il numero assegnato all’Italia è 88.<br />

• 0-7<br />

• 80-94<br />

• 950-994<br />

• 9950-9989<br />

• 99900-99999<br />

2. La seconda parte identifica l’editore e viene attribuito dall’agenzia ISBN competente<br />

nell’area di riferimento.<br />

• 00-19<br />

• 200-699<br />

• 7000-8499<br />

• 85000-89999<br />

• 900000-999999<br />

3. La terza parte identifica il titolo, relativamente all’editore a cui si fa riferimento, utilizzando<br />

lo spazio rimanente, escludendo il numero di controllo finale.<br />

4. Il numero di controllo viene calcolato moltiplicando le cifre per un numero che va da 10 a<br />

uno, sommando ciò che si ottiene e dividendo per il «modulo 11». In questo modo, il valore<br />

che si ottiene può andare da 0 a 10, dove il 10 si rappresenta con una lettera «X».<br />

Per comprendere il meccanismo del calcolo del numero di controllo, conviene vedere un esempio<br />

reale, il numero ISBN ‘88-8331-223-6’. In pratica, si tratta di verificare che il numero sei sia<br />

effettivamente il numero di controllo corretto.<br />

Valore 8 8 8 3 3 1 2 2 3 6<br />

Peso 10 9 8 7 6 5 4 3 2 1<br />

Peso*Valore 80 72 64 21 18 5 8 6 6 6


Codici a barre 37<br />

Il totale che si ottiene è 286, che diviso per 11 dà esattamente 26, senza resto, a conferma della<br />

validità del numero di controllo. Non conoscendo il numero di controllo lo si potrebbe calcolare:<br />

la somma che si otterrebbe sarebbe solo 280, che divisa per 11 dà 25 con il resto di 5, per cui, 11<br />

- 5 dà 6, il numero cercato.<br />

Il numero ISBN si trasforma in EAN-13 mettendo anteriormente il valore 978 e togliendo il<br />

numero di controllo finale che va sostituito con quello calcolato secondo lo standard EAN-13;<br />

naturalmente, i trattini di divisione sono perduti. La figura 353.4 mostra l’esempio del codice<br />

ISBN 88-481-0113-5 rappresentato secondo la simbologia EAN-13, che si trasforma nel numero<br />

9788848101134.<br />

Figura 353.4. ISBN 88-481-0113-5.<br />

ISBN 88-481-0113-5<br />

9 788848 101134<br />

A seconda del paese in cui si utilizza il numero ISBN, può essere obbligatorio o meno un codice<br />

a barre aggiuntivo contenente l’informazione sul prezzo.<br />

353.3.3 ISSN<br />

La codifica ISSN permette di identificare una pubblicazione periodica attraverso una tecnica<br />

simile a quella del numero ISBN per i libri. In questo caso, il numero è composto da otto cifre<br />

numeriche (compreso il numero di controllo), che vanno inserite in una simbologia EAN-13<br />

aggiungendo all’inizio il prefisso 977, togliendo il numero di controllo ISSN, aggiungendo due<br />

cifre che rappresentano il codice del prezzo e aggiungendo il codice di controllo EAN-13. Inoltre,<br />

va aggiunto un codice a barre ulteriore per rappresentare due o cinque cifre in cui si indica il<br />

numero del fascicolo.<br />

Per esempio, il numero ISSN ‘1129-1346’ di una rivista potrebbe tradursi nel codice a barre<br />

9771129134006, con l’aggiunta di 10110, dove il codice del prezzo è assente e si fa riferimento<br />

al fascicolo numero 110.<br />

La figura 353.5 mostra l’esempio del codice di due riviste, in cui appare anche un’estensione<br />

contenente il numero della pubblicazione.<br />

Figura 353.5. Esempio di due codici ISSN, riferiti a riviste, con un’estensione<br />

contenente il numero della pubblicazione (rispettivamente 106 e 23).<br />

353.4 Code 39<br />

La simbologia denominata Code 39, ovvero 3 <strong>of</strong> 9, consente di rappresentare 42 simboli, composti<br />

da lettere maiuscole, cifre numeriche e pochi altri segni. La tabella 353.8 riepiloga l’insieme<br />

di caratteri. Un codice a barre Code 39 è composto da un carattere di inizio, dai dati che deve<br />

contenere, da un codice di controllo (che non appare tradotto per la lettura umana) e dal carattere<br />

di fine. Il carattere usato per iniziare e per concludere la sequenza è l’asterisco, che quindi non<br />

può essere utilizzato nei dati contenuti.


38 volume VIII Argomenti avanzati e accessori<br />

Tabella 353.8. Insieme di caratteri per la simbologia Code 39.<br />

Valore Carattere Valore Carattere<br />

0 0 22 M<br />

1 1 23 N<br />

2 2 24 O<br />

3 3 25 P<br />

4 4 26 Q<br />

5 5 27 R<br />

6 6 28 S<br />

7 7 29 T<br />

8 8 30 U<br />

9 9 31 V<br />

10 A 32 W<br />

11 B 33 X<br />

12 C 34 Y<br />

13 D 35 Z<br />

14 E 36 -<br />

15 F 37 .<br />

16 G 38 space<br />

17 H 39 $<br />

18 I 40 /<br />

19 J 41 +<br />

20 K 42 %<br />

21 L inizio/fine *<br />

Figura 353.6. Esempio di codice a barre Code 39.<br />

C I A O<br />

Gli elementi utilizzati nella simbologia Code 39 sono quattro, composti da due tipi di barre nere<br />

e due tipi di spazi. Ogni carattere o segno che può essere rappresentato con questa simbologia, si<br />

avvale di cinque barre e di quattro spazi, per un totale di nove elementi. Il nome della simbologia,<br />

che si può tradurre come «tre su nove», indica il fatto che tre elementi su nove sono sempre di<br />

tipo largo (rispetto agli altri che invece sono stretti).<br />

Il codice di controllo si ottiene sommando assieme i valori abbinati ai segni che si possono<br />

rappresentare, secondo la tabella di conversione 353.8, dividendo per 43. Il resto della divisione<br />

è il codice di controllo. Nel caso dell’esempio che appare in figura 353.6, si tratta della lettera<br />

‘L’:<br />

«CIAO» = 12 + 18 + 10 + 24 = 64<br />

64 / 43 = 1 lasciando un resto di 21 = ‘L’


Codici a barre 39<br />

353.4.1 Code 39 esteso<br />

Esiste anche un’estensione della simbologia Code 39, allo scopo di consentire la rappresentazione<br />

dell’ASCII standard completo. Si ottiene questo risultato scrivendo due simboli normali<br />

al posto di uno. Naturalmente, dal momento che non esiste un modo per distinguere la codifica<br />

standard da quella estesa, è necessario che il lettore di codice a barre sia impostato nel modo più<br />

conveniente. La tabella 353.9 mostra l’insieme di caratteri esteso e la corrispondenza con i segni<br />

della simbologia Code 39 normale.<br />

Tabella 353.9. Insieme di caratteri esteso per la simbologia Code 39.<br />

ASCII Code 39 ASCII Code 39 ASCII Code 39 ASCII Code 39<br />

%U = @ %V ‘ %W<br />

$A ! /A A A a +A<br />

$B " /B B B b +B<br />

$C # /C C C c +C<br />

$D $ /D D D d +D<br />

$E % /E E E e +E<br />

$F & /F F F f +F<br />

$G ’ /G G G g +G<br />

$H ( /H H H h +H<br />

$I ) /I I I i +I<br />

$J * /J J J j +J<br />

$K + /K K K k +K<br />

$L , /L L L l +L<br />

$M - /M M M m +M<br />

$N . /N N N n +N<br />

$O / /O O O o +O<br />

$P 0 0 P P p +P<br />

$Q 1 1 Q Q q +Q<br />

$R 2 2 R R r +R<br />

$S 3 3 S S s +S<br />

$T 4 4 T T t +T<br />

$U 5 5 U U u +U<br />

$V 6 6 V V v +V<br />

$W 7 7 W W w +W<br />

$X 8 8 X X x +X<br />

$Y 9 9 Y Y y +Y<br />

$Z : /Z Z Z z +Z<br />

%A ; %F [ %K { %P<br />

%B < %G \ %L | %Q<br />

%C = %H ] %M } %R<br />

%D > %I ^ %N ~ %S<br />

%E ? %J _ %O %T<br />

353.5 Code 128<br />

La simbologia denominata Code 128, ovvero USS Code 128, consente di rappresentare 106 simboli,<br />

che possono essere interpretati secondo tre insiemi di caratteri, distinti in base a una lettera:<br />

A, B e C. Per questa ragione, un codice a barre realizzato secondo la simbologia Code 128 inizia<br />

sempre con la dichiarazione dell’insieme di caratteri. L’informazione non ha una lunghezza<br />

predeterminata e può essere modificato l’insieme di caratteri quando serve. La simbologia Code<br />

128 prevede l’inserimento di un codice di controllo, calcolato automaticamente per consentire<br />

la verifica della lettura meccanica, ma questa cifra non viene mostrata per la lettura umana.


40 volume VIII Argomenti avanzati e accessori<br />

Tabella 353.10. Insiemi di caratteri per la simbologia Code 128. L’ultima colonna ripor-<br />

ta anche la descrizione della rappresentazione a barre, dove un numero da uno a<br />

tre indica la larghezza della barra, ‘b’, o dello spazio, ‘s’, dalla grandezza più piccola<br />

alla più grande. Si osservi che lo stop ha una barra in più alla fine.<br />

Valore Code A Code B Code C Barre e spazi:<br />

b s b s b s<br />

0 00 2 1 2 2 2 2<br />

1 ! ! 01 2 2 2 1 2 2<br />

2 " " 02 2 2 2 2 2 1<br />

3 # # 03 1 2 1 2 2 3<br />

4 $ $ 04 1 2 1 3 2 2<br />

5 % % 05 1 3 1 2 2 2<br />

6 & & 06 1 2 2 2 1 3<br />

7 ’ ’ 07 1 2 2 3 1 2<br />

8 ( ( 08 1 3 2 2 1 2<br />

9 ) ) 09 2 2 1 2 1 3<br />

10 * * 10 2 2 1 3 1 2<br />

11 + + 11 2 3 1 2 1 2<br />

12 ’ ’ 12 1 1 2 2 3 2<br />

13 - - 13 1 2 2 1 3 2<br />

14 . . 14 1 2 2 2 3 1<br />

15 / / 15 1 1 3 2 2 2<br />

16 0 0 16 1 2 3 1 2 2<br />

17 1 1 17 1 2 3 2 2 1<br />

18 2 2 18 2 2 3 2 1 1<br />

19 3 3 19 2 2 1 1 3 2<br />

20 4 4 20 2 2 1 2 3 1<br />

21 5 5 21 2 1 3 2 1 2<br />

22 6 6 22 2 2 3 1 1 2<br />

23 7 7 23 3 1 2 1 3 1<br />

24 8 8 24 3 1 1 2 2 2<br />

25 9 9 25 3 2 1 1 2 2<br />

26 : : 26 3 2 1 2 2 1<br />

27 ; ; 27 3 1 2 2 1 2<br />

28 < < 28 3 2 2 1 1 2<br />

29 = = 29 3 2 2 2 1 1<br />

30 > > 30 2 1 2 1 2 3<br />

31 ? ? 31 2 1 2 3 2 1<br />

32 @ @ 32 2 3 2 1 2 1<br />

33 A A 33 1 1 1 3 2 3<br />

34 B B 34 1 3 1 1 2 3<br />

35 C C 35 1 3 1 3 2 1<br />

36 D D 36 1 1 2 3 1 3<br />

37 E E 37 1 3 2 1 1 3<br />

38 F F 38 1 3 2 3 1 1<br />

39 G G 39 2 1 1 3 1 3<br />

40 H H 40 2 3 1 1 1 3<br />

41 I I 41 2 3 1 3 1 1<br />

42 J J 42 1 1 2 1 3 3<br />

43 K K 43 1 1 2 3 3 1<br />

44 L L 44 1 3 2 1 3 1<br />

45 M M 45 1 1 3 1 2 3<br />

46 N N 46 1 1 3 3 2 1<br />

47 O O 47 1 3 3 1 2 1<br />

48 P P 48 3 1 3 1 2 1<br />

49 Q Q 49 2 1 1 3 3 1


Codici a barre 41<br />

Valore Code A Code B Code C Barre e spazi:<br />

b s b s b s<br />

50 R R 50 2 3 1 1 3 1<br />

51 S S 51 2 1 3 1 1 3<br />

52 T T 52 2 1 3 3 1 1<br />

53 U U 53 2 1 3 1 3 1<br />

54 V V 54 3 1 1 1 2 3<br />

55 W W 55 3 1 1 3 2 1<br />

56 X X 56 3 3 1 1 2 1<br />

57 Y Y 57 3 1 2 1 1 3<br />

58 Z Z 58 3 1 2 3 1 1<br />

59 [ [ 59 3 3 2 1 1 1<br />

60 \ \ 60 3 1 4 1 1 1<br />

61 ] ] 61 2 2 1 4 1 1<br />

62 ^ ^ 62 4 3 1 1 1 1<br />

63 _ _ 63 1 1 1 2 2 4<br />

64 ‘ 64 1 1 1 4 2 2<br />

65 a 65 1 2 1 1 2 4<br />

66 b 66 1 2 1 4 2 1<br />

67 c 67 1 4 1 1 2 2<br />

68 d 68 1 4 1 2 2 1<br />

69 e 69 1 1 2 2 1 4<br />

70 f 70 1 1 2 4 1 2<br />

71 g 71 1 2 2 1 1 4<br />

72 h 72 1 2 2 4 1 1<br />

73 i 73 1 4 2 1 1 2<br />

74 j 74 1 4 2 2 1 1<br />

75 k 75 2 4 1 2 1 1<br />

76 l 76 2 2 1 1 1 4<br />

77 m 77 4 1 3 1 1 1<br />

78 n 78 2 4 1 1 1 2<br />

79 o 79 1 3 4 1 1 1<br />

80 p 80 1 1 1 2 4 2<br />

81 q 81 1 2 1 1 4 2<br />

82 r 82 1 2 1 2 4 1<br />

83 s 83 1 1 4 2 1 2<br />

84 t 84 1 2 4 1 1 2<br />

85 u 85 1 2 4 2 1 1<br />

86 v 86 4 1 1 2 1 2<br />

87 w 87 4 2 1 1 1 2<br />

88 x 88 4 2 1 2 1 1<br />

89 y 89 2 1 2 1 4 1<br />

90 z 90 2 1 4 1 2 1<br />

91 { 91 4 1 2 1 2 1<br />

92 | 92 1 1 1 1 4 3<br />

93 } 93 1 1 1 3 4 1<br />

94 ~ 94 1 3 1 1 4 1<br />

95 95 1 1 4 1 1 3<br />

96 FNC 3 FNC 3 96 1 1 4 3 1 1<br />

97 FNC 2 FNC 2 97 4 1 1 1 1 3<br />

98 Shift Shift 98 4 1 1 3 1 1<br />

99 Code C Code C 99 1 1 3 1 4 1<br />

100 Code B FNC 4 Code B 1 1 4 1 3 1<br />

101 FNC 4 Code A Code A 3 1 1 1 4 1<br />

102 FNC 1 FNC 1 FNC 1 4 1 1 1 3 1<br />

103 Start A Start A Start A 2 1 1 4 1 2<br />

104 Start B Start B Start B 2 1 1 2 1 4


42 volume VIII Argomenti avanzati e accessori<br />

Valore Code A Code B Code C Barre e spazi:<br />

b s b s b s<br />

105 Start C Start C Start C 2 1 1 2 3 2<br />

Stop Stop Stop 2 3 3 1 1 1 2<br />

Gli insiemi di caratteri A, B e C sono elencati nella tabella 353.10. Come si può vedere, l’insieme<br />

A consente di rappresentare l’alfabeto maiuscolo, le cifre numeriche, vari simboli di punteggiatura<br />

e caratteri di controllo; in pratica, ciò che si può rappresentare con la prima parte della codifica<br />

ASCII, tenendo conto che le posizioni non corrispondono. L’insieme B consente di rappresentare<br />

praticamente tutto l’alfabeto ASCII, compresa la distinzione tra lettere maiuscole e minuscole,<br />

a esclusione dei caratteri di controllo. L’insieme C consente di rappresentare valori numerici a<br />

coppie, per cui, un valore composto da un numero dispari di cifre acquisisce uno zero iniziale.<br />

Figura 353.7. Esempio di due codici Code 128, realizzati usando l’insieme B e l’insieme<br />

C, per rappresentare due dati equivalenti: «Ciao» e 35736579.<br />

Ci ao 35736579<br />

La figura 353.7 mostra la comparazione tra un codice a barre realizzato usando l’insieme B<br />

e un altro con l’insieme C. L’informazione contenuta è la stessa dal punto di vista dei valori<br />

rappresentabili (eventualmente si osservino le tabelle degli insiemi di caratteri). Se fosse possibile<br />

sovrapporre i due codici, si noterebbe che cambia solo la parte iniziale, quella in cui si dichiara<br />

l’insieme di caratteri, e la parte finale, dove si inserisce il codice di controllo. Si può anche<br />

osservare che gli ultimi elementi rimangono uguali e sono quelli che contengono il codice di<br />

conclusione.<br />

Volendo entrare nel dettaglio, l’esempio di figura 353.7 si compone di: ‘Start B’, 35, 73,<br />

65, 79, codice_di_controllo, ‘Stop’; ovvero, nel secondo caso, ‘Start C’, 35, 73, 65, 79,<br />

codice_di_controllo, ‘Stop’.<br />

Quando si vuole cambiare l’insieme di caratteri, si inserisce un simbolo ‘Code A’, ‘Code B’ o<br />

‘Code C’, per introdurre l’insieme a cui si vuole fare riferimento da quel punto in poi.<br />

Il codice di controllo finale si calcola sommando il valore corrispondente alla dichiarazione iniziale<br />

dell’insieme di caratteri alla somma dei valori successivi, moltiplicati per la loro posizione.<br />

La somma complessiva va divisa per 103 e il resto che si ottiene è il codice di controllo finale. Si<br />

osservi l’esempio:<br />

Pertanto:<br />

104 + 35 + 146 + 195 + 316 = 796<br />

Start B C i a o Stop<br />

Peso 1 1 2 3 4<br />

Valore 104 35 73 65 79<br />

Peso*Valore 104 35 146 195 316<br />

796 / 103 = 7 con resto di 75. Il codice di controllo è il simbolo corrispondente al valore 75.<br />

Usando l’insieme di caratteri C la cosa cambia, perché il simbolo iniziale ha il valore 105, per<br />

cui il codice di controllo finale è 76:


Codici a barre 43<br />

Start C 35 73 65 79 Stop<br />

Peso 1 1 2 3 4<br />

Valore 105 35 73 65 79<br />

Peso*Valore 105 35 146 195 316<br />

Dalla simbologia Code 128 derivano diverse applicazioni speciali che si distinguono per avere<br />

una struttura particolare. Esiste anche una variante che consente la rappresentazione di più<br />

caratteri rispetto all’ASCII standard.<br />

353.5.1 UCC/EAN 128<br />

La simbologia UCC/EAN 128 è un’applicazione di Code 128, in cui si inizia dichiarando<br />

l’insieme di caratteri e si inserisce subito dopo il simbolo ‘FNC 1’ (Function code one). 1<br />

Dopo il simbolo ‘FNC 1’ viene indicato un codice che indica il tipo di applicazione. Si fa riferimento<br />

a questo con la sigla AI (Application identifier). L’informazione in questione viene<br />

rappresentata per la lettura umana tra parentesi tonde, proprio per facilitare l’interpretazione, a<br />

sottolineare il fatto che si tratta di un prefisso. Naturalmente, l’informazione può essere anche<br />

più complessa e altre parti dei dati successivi possono essere separate ed evidenziate nello stesso<br />

modo o in modi differenti.<br />

353.6 ITF, ovvero i25<br />

La simbologia ITF, o i25, nota come Interleaved two <strong>of</strong> five, ovvero «interfogliata due su cinque»,<br />

consente la rappresentazione di soli valori numerici, di lunghezza indefinita, purché in numero di<br />

cifre pari. La simbologia prevede una cifra di controllo finale, opzionale.<br />

La simbologia si compone di due tipi di barre e due tipi di spazi. Ogni simbolo si rappresenta<br />

con cinque barre e cinque spazi, dove due barre su cinque e due spazi su cinque sono più larghi.<br />

La simbologia è detta «interfogliata», perché una cifra è codificata nelle barre e la successiva<br />

lo è negli spazi che separano gli elementi del carattere precedente. Prima dei dati è previsto un<br />

simbolo iniziale, composto da una barra sottile, uno spazio breve, una barra sottile e un altro<br />

spazio breve; alla fine c’è un simbolo finale, composto da una barra larga, uno spazio sottile e<br />

una barra sottile.<br />

Figura 353.8. Esempio di codice a barre con simbologia interfogliata due su cinque.<br />

0 1 2 3 4 5 6 7 8<br />

1 L’uso comune è dell’insieme di caratteri C, ma ciò non è imposto dallo standard.


44 volume VIII Argomenti avanzati e accessori<br />

353.7 Sistemi bidimensionali<br />

Il codice a barre comune è a una sola dimensione, nel senso che contiene una sola riga di informazioni,<br />

eventualmente anche molto breve. Semplificando le cose, si può dire che quando si<br />

sovrappongono più righe in un codice a barre, questo diventa a due dimensioni.<br />

Un sistema di codice a barre a due dimensioni, con lo scopo di memorizzare dati, deve essere<br />

organizzato in modo da permettere al lettore meccanico di riconoscere e seguire le righe; inoltre<br />

deve essere previsto un sistema di informazioni ridondanti, anche molto complesso, in modo da<br />

garantire la lettura in presenza di errori.<br />

Lo standard più comune per i codici a barre a due dimensioni è il PDF417, che comunque qui<br />

non viene descritto.<br />

353.8 Riferimenti<br />

• EAN international<br />

<br />

• The EAN.UCC system<br />

<br />

• Tino Hempel, Die Europäische Artikelnummer (EAN)<br />

<br />

• Barcoding for Beginners & Bar Code FAQ<br />

<br />

• Code 39 Barcode FAQ and Tutorial<br />

<br />

• Code 128 / USS Code-128 Barcode FAQ & Tutorial<br />

<br />

• PDF417 Barcode FAQ<br />

<br />

• UPC / EAN Barcode Font Data Sheet<br />

<br />

• PDF417<br />

<br />

• Russ Adams, Barcode 1<br />

<br />

• International ISBN Agency: Coordinator <strong>of</strong> the International Standard Book Number<br />

system<br />

<br />

• International ISBN Agency, The ISBN Users’ Manual<br />


Codici a barre 45<br />

• AIE - Agenzia ISBN<br />

<br />

Appunti di informatica libera 2003.06.29 --- Copyright © 2000-2003 Daniele Giacomini -- daniele @ swlibero.org


Barcode<br />

Capitolo 354<br />

Barcode 1 è una libreria e anche un programma molto semplice per la realizzazione di codici<br />

a barre standard a una sola dimensione. Il codice a barre viene realizzato in forma di file Post-<br />

Script, che successivamente può essere utilizzato direttamente o convertito in altri formati grafici,<br />

attraverso programmi standard.<br />

354.1 Utilizzo del programma<br />

Barcode, come programma, si compone in pratica dell’eseguibile ‘barcode’. Questo genera normalmente<br />

un file PostScript che, salvo l’utilizzo dell’opzione ‘-o’, viene emesso attraverso lo<br />

standard output.<br />

barcode [opzioni]<br />

Teoricamente si può usare Barcode anche senza l’indicazione di alcun argomento, per ottenere<br />

la conversione automatica di quanto inserito attraverso lo standard input, scegliendo la simbologia<br />

in modo automatico. Tuttavia, in genere è opportuno accertarsi di selezionare la simbologia<br />

attraverso l’opzione ‘-e’.<br />

Le opzioni che vengono mostrate qui sono poche; in particolare mancano quelle che servono a<br />

definire la stampa di codici a barre in modo organizzato sul foglio, allo scopo di centrare delle<br />

etichette adesive. Per appr<strong>of</strong>ondire basta leggere la documentazione originale: barcode.info.<br />

Tabella 354.1. Alcune opzioni.<br />

Opzione Descrizione<br />

Consente di specificare la simbologia dei codici a barre che si<br />

vogliono realizzare (encoding). Sono disponibili diverse pa-<br />

-e simbologia<br />

role chiave da usare come argomento dell’opzione, secondo<br />

la tabella 354.2.<br />

Stabilisce il nome di un file contenente un elenco di codici da<br />

trasformare in codici a barre. Se manca questa indicazione e<br />

-i file<br />

se manca anche l’opzione ‘-b’, viene letto lo standard input.<br />

La lettera dell’opzione richiama mnemonicamente la parola<br />

«input».<br />

Consente di indicare una stringa da convertire in un codice a<br />

barre singolo. La stringa deve essere compatibile con i tipi di<br />

dati che possono essere rappresentati con la simbologia pre-<br />

-b stringa<br />

scelta. La lettera dell’opzione richiama mnemonicamente la<br />

parola barcode, ovvero il codice a barre che si vuole ottenere<br />

dalla stringa.<br />

-E Genera un file PostScript incapsulato (EPS).<br />

Segue la descrizione di alcuni esempi.<br />

• $ barcode -E -e i25 -b "12345678" > prova.ps<br />

Converte il numero 12345678 in un codice a barre secondo la simbologia interfogliata due<br />

su cinque, generando il file ‘prova.ps’, di tipo EPS.<br />

• $ barcode -E -e i25 -b "12345678" -o prova.ps<br />

1 Barcode GNU GPL<br />

46


Barcode 47<br />

Esattamente come nell’esempio precedente.<br />

• $ barcode -E -e isbn -b "88-386-4177-3" -o prova.ps<br />

Come nell’esempio precedente, usando la simbologia EAN-13 per rappresentare un codice<br />

ISBN, come indicato con l’opzione ‘-b’.<br />

354.2 Codifica<br />

Generalmente, Barcode viene utilizzato indicando il tipo di simbologia, attraverso una parola<br />

chiave secondo l’elenco della tabella 354.2. In generale, si tratta di nomi che fanno riferimento<br />

al tipo di codice a barre; in alcuni casi, il nome indica anche altre caratteristiche. Per esempio,<br />

la parola chiave ‘code128b’ indica la simbologia Code 128, nella quale si utilizza l’insieme di<br />

caratteri B; inoltre, la parola chiave ‘isbn’ fa riferimento al codice ISBN, che viene rappresentato<br />

correttamente secondo la simbologia EAN-13.<br />

Tabella 354.2. Simbologie disponibili con Barcode.<br />

Simbologia Parola chiave<br />

UPC-A<br />

upc-a<br />

UPC-E<br />

upc-e<br />

UPC-A o UPC-E automaticamente<br />

upc<br />

EAN-8 ean8, ean-8<br />

EAN-13 ean13, ean-13<br />

EAN-8 o EAN-13 automaticamente<br />

ean<br />

Code 39 code39, 39<br />

Code 128 insieme di caratteri B code128b, 128b<br />

Code 128 insieme di caratteri C code128c, 128c<br />

Code 128 insieme di caratteri A, B o C, automaticamente code128, 128<br />

Code 128 libero 128raw<br />

interfogliata due su cinque i25, interleaved 2 <strong>of</strong> 5<br />

Codabar cdr, codabar<br />

Plessey pls, plessey<br />

ISBN attraverso EAN-13 isbn<br />

Nel caso dell’uso della simbologia Code 128, si pone il problema di rappresentare i codici da<br />

a , il codice e le funzioni speciali previste dalla simbologia. Usando l’eseguibile<br />

‘barcode’ diventa difficile indicare questi simboli; con la libreria non ci sono problemi<br />

a indicare i codici ASCII da a e il codice , mentre negli altri casi viene fatta<br />

una trasformazione, come rappresentato nella tabella 354.3.<br />

Tabella 354.3. Rappresentazione dei caratteri speciali per la simbologia Code 128,<br />

quando si usa la libreria Barcode.<br />

Codice o simbolo Ottale Decimale Esadecimale<br />

2008 128 8016<br />

FNC 1 3018 193 C116<br />

FNC 2 3028 194 C216<br />

FNC 3 3038 195 C316<br />

FNC 4 3048 196 C416<br />

Sempre a proposito della simbologia Code 128, è possibile utilizzando la parola chiave ‘128raw’<br />

per indicare un codice attraverso una stringa espressa nella forma:


48 volume VIII Argomenti avanzati e accessori<br />

n_1 n_2... n_n<br />

In pratica, si indicano una serie di numeri separati tra loro da uno spazio. I numeri in questione<br />

rappresentano il valore indicato nella tabella 353.10; inoltre occorre tenere in considerazione che<br />

va fornito anche il valore iniziale, con il quale si definisce solitamente l’insieme di caratteri A, B<br />

o C, mentre il codice di controllo finale e lo stop sono aggiunti automaticamente.<br />

Appunti di informatica libera 2003.06.29 --- Copyright © 2000-2003 Daniele Giacomini -- daniele @ swlibero.org


Trasformazione in lettere<br />

Capitolo 355<br />

Nell’ambito della realizzazione di applicativi gestionali, capitano frequentemente problemi di<br />

conversione di numeri interi in una qualche forma alfabetica. In questo capitolo vengono mostrati<br />

degli algoritmi molto semplici per risolvere questo tipo di problemi.<br />

355.1 Da numero a sequenza alfabetica pura<br />

Esiste un tipo di numerazione in cui si utilizzano solo le lettere dell’alfabeto, dalla «a» alla «z»,<br />

senza accenti o altri simboli speciali, senza distinguere tra maiuscole e minuscole. In generale,<br />

i simboli da «a» a «z» consentono di rappresentare valori da 1 a 26, dove lo zero è escluso.<br />

Per rappresentare valori superiori, si possono accoppiare più lettere, ma il calcolo non è banale,<br />

proprio perché manca lo zero.<br />

Attraverso la pseudocodifica introdotta nel capitolo 286, si può descrivere una funzione che calcoli<br />

la stringa corrispondente a un numero intero positivo, maggiore di zero. Per prima cosa,<br />

occorre definire una sotto funzione che sia in grado di trasformare un numero intero, compreso<br />

tra 1 e 26 nella lettera alfabetica corrispondente; ciò si ottiene abbastanza facilmente, attraverso<br />

la verifica di più condizioni in cascata. Il vero problema, purtroppo, sta nel costruire una stringa<br />

composta da più lettere, quando si vuole rappresentare un valore superiore a 26. Non essendoci<br />

lo zero, diventa difficile fare i calcoli. Se si parte dal presupposto che il numero da convertire<br />

non possa essere superiore a 702, si sa con certezza che servono al massimo due lettere alfabetiche<br />

(perché la stringa «ZZ» corrisponderebbe proprio al numero 702); in tal caso, è sufficiente<br />

dividere il numero per 26, dove la parte intera rappresenta la prima lettera, mentre il resto rappresenta<br />

la seconda. Tuttavia, se la divisione non dà resto, la stringa corretta è quella precedente.<br />

Per esempio, il numero 53 corrisponde alla stringa «BA», perché 53/26 = 2 con un resto di 1.<br />

Nello stesso modo, però, 52 si traduce nella stringa «AZ», perché 53/26 = 2, ma non c’è resto,<br />

pertanto, «B_» diventa «AZ».<br />

La pseudocodifica seguente riepiloga il concetto in modo semplificato, dove, a seconda delle<br />

esigenze, la conversione è sempre limitata a un valore massimo. Le omissioni sono parti di codice<br />

facilmente intuibili.<br />

INTEGER_TO_ALPHABET (N)<br />

ALPHABET_DIGIT (DIGIT)<br />

IF DIGIT = 0<br />

THEN<br />

RETURN ""<br />

ELSE IF DIGIT = 1<br />

THEN<br />

RETURN "A"<br />

ELSE IF DIGIT = 2<br />

THEN<br />

RETURN "B"<br />

...<br />

...<br />

ELSE IF DIGIT = 26<br />

THEN<br />

RETURN "Z"<br />

ELSE<br />

RETURN "##ERROR##"<br />

FI<br />

END ALPHABET_DIGIT<br />

IF N


50 volume VIII Argomenti avanzati e accessori<br />

THEN<br />

RETURN "##ERROR##";<br />

ELSE IF N


Trasformazione in lettere 51<br />

Per risolvere il problema con un algoritmo relativamente semplice, si può scomporre il valore<br />

di partenza in fasce: unità, decine, centinaia e migliaia (la conversione di valori superiori<br />

genererebbe soltanto una serie lunghissima di «M» che risulta poi troppo difficile da leggere).<br />

INTEGER_TO_ROMAN (N)<br />

LOCAL DIGIT_1 INTEGER<br />

LOCAL DIGIT_2 INTEGER<br />

LOCAL DIGIT_3 INTEGER<br />

LOCAL DIGIT_4 INTEGER<br />

DIGIT_1 := 0<br />

DIGIT_2 := 0<br />

DIGIT_3 := 0<br />

DIGIT_4 := 0<br />

DIGIT_1_TO_ROMAN (DIGIT)<br />

IF DIGIT = 0<br />

THEN<br />

RETURN ""<br />

ELSE IF DIGIT = 1<br />

THEN<br />

RETURN "I"<br />

ELSE IF DIGIT = 2<br />

THEN<br />

RETURN "II"<br />

ELSE IF DIGIT = 3<br />

THEN<br />

RETURN "III"<br />

ELSE IF DIGIT = 4<br />

THEN<br />

RETURN "IV"<br />

ELSE IF DIGIT = 5<br />

THEN<br />

RETURN "V"<br />

ELSE IF DIGIT = 6<br />

THEN<br />

RETURN "VI"<br />

ELSE IF DIGIT = 7<br />

THEN<br />

RETURN "VII"<br />

ELSE IF DIGIT = 8<br />

THEN<br />

RETURN "VIII"<br />

ELSE IF DIGIT = 9<br />

THEN<br />

RETURN "IX"<br />

END IF<br />

END DIGIT_1_TO_ROMAN<br />

DIGIT_2_TO_ROMAN (DIGIT)<br />

IF DIGIT = 0<br />

THEN<br />

RETURN ""<br />

ELSE IF DIGIT = 1<br />

THEN<br />

RETURN "X"<br />

ELSE IF DIGIT = 2<br />

THEN<br />

RETURN "XX"<br />

ELSE IF DIGIT = 3<br />

THEN<br />

RETURN "XXX"<br />

ELSE IF DIGIT = 4<br />

THEN


52 volume VIII Argomenti avanzati e accessori<br />

RETURN "XL"<br />

ELSE IF DIGIT = 5<br />

THEN<br />

RETURN "L"<br />

ELSE IF DIGIT = 6<br />

THEN<br />

RETURN "LX"<br />

ELSE IF DIGIT = 6<br />

THEN<br />

RETURN "LXX"<br />

ELSE IF DIGIT = 8<br />

THEN<br />

RETURN "LXXX"<br />

ELSE IF DIGIT = 9<br />

THEN<br />

RETURN "XC"<br />

END IF<br />

END DIGIT_2_TO_ROMAN<br />

DIGIT_3_TO_ROMAN (DIGIT)<br />

IF DIGIT = 0<br />

THEN<br />

RETURN ""<br />

ELSE IF DIGIT = 1<br />

THEN<br />

RETURN "C"<br />

ELSE IF DIGIT = 2<br />

THEN<br />

RETURN "CC"<br />

ELSE IF DIGIT = 3<br />

THEN<br />

RETURN "CCC"<br />

ELSE IF DIGIT = 4<br />

THEN<br />

RETURN "CD"<br />

ELSE IF DIGIT = 5<br />

THEN<br />

RETURN "D"<br />

ELSE IF DIGIT = 6<br />

THEN<br />

RETURN "DC"<br />

ELSE IF DIGIT = 7<br />

THEN<br />

RETURN "DCC"<br />

ELSE IF DIGIT = 8<br />

THEN<br />

RETURN "DCCC"<br />

ELSE IF DIGIT = 9<br />

THEN<br />

RETURN "CM"<br />

END IF<br />

END DIGIT_3_TO_ROMAN<br />

DIGIT_4_TO_ROMAN (DIGIT)<br />

IF DIGIT = 0<br />

THEN<br />

RETURN ""<br />

ELSE IF DIGIT = 1<br />

THEN<br />

RETURN "M"<br />

ELSE IF DIGIT = 2<br />

THEN<br />

RETURN "MM"<br />

ELSE IF DIGIT = 3


Trasformazione in lettere 53<br />

THEN<br />

RETURN "MMM"<br />

ELSE IF DIGIT = 4<br />

THEN<br />

RETURN "MMMM"<br />

ELSE IF DIGIT = 5<br />

THEN<br />

RETURN "MMMMM"<br />

ELSE IF DIGIT = 6<br />

THEN<br />

RETURN "MMMMMM"<br />

ELSE IF DIGIT = 7<br />

THEN<br />

RETURN "MMMMMMM"<br />

ELSE IF DIGIT = 8<br />

THEN<br />

RETURN "MMMMMMMM"<br />

ELSE IF DIGIT = 9<br />

THEN<br />

RETURN "MMMMMMMMM"<br />

END IF<br />

END DIGIT_4_TO_ROMAN<br />

DIGIT_4 := int (N/1000)<br />

N := N - (DIGIT_4 * 1000)<br />

DIGIT_3 := int (N/100)<br />

N := N - (DIGIT_3 * 100)<br />

DIGIT_2 := int (N/10)<br />

N := N - (DIGIT_2 * 10)<br />

DIGIT_1 := N<br />

RETURN DIGIT_4_TO_ROMAN (DIGIT_4)<br />

DIGIT_3_TO_ROMAN (DIGIT_3)<br />

DIGIT_2_TO_ROMAN (DIGIT_2)<br />

DIGIT_1_TO_ROMAN (DIGIT_2)<br />

END INTEGER_TO_ROMAN<br />

Come si vede, dopo aver scomposto il valore in quattro fasce, si utilizzano quattro funzioni<br />

distinte per ottenere la porzione di stringa che traduce il valore relativo. L’istruzione ‘RETURN’<br />

finale intende concatenare tutte le stringhe risultanti.<br />

355.3 Da numero a lettere, nel senso verbale<br />

Quando si trasforma un numero in lettere, per esempio quando si vuole trasformare 123 in «centoventitre»,<br />

l’algoritmo di conversione deve tenere conto delle convenzioni linguistiche e non<br />

esiste una soluzione generale per tutte le lingue.<br />

Per quanto riguarda la lingua italiana, esistono nomi diversi fino al 19, poi ci sono delle particolarità<br />

per i plurali o i singolari. La pseudocodifica seguente risolve il problema in una sola funzione<br />

ricorsiva. Le omissioni dovrebbero essere sufficientemente intuitive.<br />

INTEGER_TO_ITALIAN (N)<br />

LOCAL X INTEGER<br />

LOCAL Y INTEGER


54 volume VIII Argomenti avanzati e accessori<br />

IF N = 0<br />

THEN<br />

RETURN ""<br />

ELSE IF N = 1<br />

THEN<br />

RETURN "UNO"<br />

ELSE IF N = 2<br />

THEN<br />

RETURN "DUE"<br />

ELSE IF N = 3<br />

THEN<br />

RETURN "TRE"<br />

ELSE IF N = 4<br />

THEN<br />

RETURN "QUATTRO"<br />

ELSE IF N = 5<br />

THEN<br />

RETURN "CINQUE"<br />

ELSE IF N = 6<br />

THEN<br />

RETURN "SEI"<br />

ELSE IF N = 7<br />

THEN<br />

RETURN "SETTE"<br />

ELSE IF N = 8<br />

THEN<br />

RETURN "OTTO"<br />

ELSE IF N = 9<br />

THEN<br />

RETURN "NOVE"<br />

ELSE IF N = 10<br />

THEN<br />

RETURN "DIECI"<br />

ELSE IF N = 11<br />

THEN<br />

RETURN "UNDICI"<br />

ELSE IF N = 12<br />

THEN<br />

RETURN "DODICI"<br />

ELSE IF N = 13<br />

THEN<br />

RETURN "TREDICI"<br />

ELSE IF N = 14<br />

THEN<br />

RETURN "QUATTORDICI"<br />

ELSE IF N = 15<br />

THEN<br />

RETURN "QUINDICI"<br />

ELSE IF N = 16<br />

THEN<br />

RETURN "SEDICI"<br />

ELSE IF N = 17<br />

THEN<br />

RETURN "DICIASSETTE"<br />

ELSE IF N = 18<br />

THEN<br />

RETURN "DICIOTTO"<br />

ELSE IF N = 19<br />

THEN<br />

RETURN "DICIANNOVE"<br />

ELSE IF N = 20<br />

THEN<br />

RETURN "VENTI"<br />

ELSE IF N = 21


Trasformazione in lettere 55<br />

THEN<br />

RETURN "VENTUNO"<br />

ELSE IF (N >= 22) AND (N = 32) AND (N = 92) AND (N = 100) AND (N = 200) AND (N = 1000) AND (N = 2000) AND (N = 1000000) AND (N = 2000000) AND (N = 1000000000) AND (N = 2000000000) AND (N


56 volume VIII Argomenti avanzati e accessori<br />

"MILIARDI"<br />

INTEGER_TO_ITALIAN (Y)<br />

ELSE<br />

"##ERROR##"<br />

END IF<br />

END INTEGER_TO_ITALIAN<br />

Appunti di informatica libera 2003.06.29 --- Copyright © 2000-2003 Daniele Giacomini -- daniele @ swlibero.org


Parte lxxi<br />

i86<br />

356 Minix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58<br />

356.1 Procurarsi il s<strong>of</strong>tware . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58<br />

356.2 Preparazione all’installazione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59<br />

356.3 Avvio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60<br />

356.4 Installazione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62<br />

356.5 Ricompilazione del kernel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67<br />

356.6 Parametri di avvio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .69<br />

356.7 Configurazione della rete . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70<br />

356.8 Personalizzazione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72<br />

356.9 Tastiera . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73<br />

356.10 Altri programmi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76<br />

356.11 Copie di sicurezza . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77<br />

356.12 Convivenza tra Minix e GNU/Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78<br />

356.13 Riferimenti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79<br />

357 ELKS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80<br />

357.1 Sperimentare ELKS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80<br />

357.2 Immagini di dischetti già pronti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .81<br />

357.3 Avvio di ELKS all’interno di DOSEMU . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81<br />

357.4 Riferimenti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82<br />

57


Minix<br />

Capitolo 356<br />

Minix 1 è nato originariamente come sistema didattico. Minix non ha avuto il successo e la<br />

diffusione che avrebbe potuto avere a causa delle limitazioni della sua licenza iniziale. In seguito<br />

le cose sono cambiate, fortunatamente, perché Minix resta probabilmente l’unica possibilità reale<br />

per chi vuole utilizzare elaboratori con architettura i286 o inferiore.<br />

Lo scopo di questo capitolo è introdurre all’uso di Minix per poter riutilizzare i vecchi elaboratori<br />

i286, in particolare, collegandoli a una rete locale TCP/IP. Le informazioni seguenti si riferiscono<br />

alla versione 2.0.0.<br />

356.1 Procurarsi il s<strong>of</strong>tware<br />

Minix è ottenibile dalla rete, precisamente a partire dalla sua pagina di presentazione ufficiale,<br />

quella del suo primo autore (La sigla «AST» rappresenta le iniziali di Andrew S. Tanenbaum),<br />

oltre che dai vari siti speculari del relativo FTP.<br />

<br />

Minix è nato assieme a un libro che tuttora dovrebbe essere accompagnato da un CD-ROM<br />

contenente il sistema operativo:<br />

• Andrew S. Tanenbaum, Alber S. Woodhull, Operating Systems: Design and Implementation,<br />

2/e, Prentice-Hall<br />

356.1.1 Pacchetti essenziali<br />

Minix è un sistema molto piccolo e composto da pochi pacchetti. Questi hanno alcune particolarità:<br />

i nomi sono composti con lettere maiuscole e gli archivi compressi utilizzano la combinazione<br />

‘tar’+‘compress’ e sono evidenziati dall’uso dell’estensione ‘.TAZ’.<br />

Per prima cosa è necessario riprodurre la coppia di dischetti ‘ROOT’ e ‘USR’, a partire dai file<br />

omonimi. Il primo è in grado di avviarsi e contiene un file system minimo che si installa in<br />

un disco RAM, il secondo contiene una piccola serie di programmi da montare nella directory<br />

‘/usr/’, che servono per poter installare Minix nel disco fisso. Se si ha poca memoria a disposizione<br />

(i classici 640 Kibyte sono il minimo in assoluto per poter fare funzionare Minix), si può<br />

evitare l’utilizzo del disco RAM fondendo i due file in un solo dischetto. Data l’intenzione di<br />

questo capitolo verrà descritta l’ultima di queste modalità di installazione.<br />

Il mini sistema che si ottiene attraverso i due file appena citati, permette di installare, più o meno<br />

automaticamente, un insieme minimo di programmi contenuto nell’archivio ‘USR.TAZ’<br />

Esistono due versioni di questi tre file: una per architettura i386 o superiore e l’altra per i microprocessori<br />

inferiori. Date le intenzioni, si dovranno utilizzare i file della versione denominata<br />

‘i86’.<br />

1 Minix licenza simile a BSD<br />

58


Minix 59<br />

356.2 Preparazione all’installazione<br />

Il procedimento che viene descritto è valido sia per dischetti da 1440 Kibyte che da 1200 Kibyte.<br />

I due file ‘ROOT’ e ‘USR’ vanno copiati uno di seguito all’altro. Utilizzando GNU/Linux, si può<br />

fare nel modo seguente:<br />

# cat ROOT USR > /dev/fd0<br />

Dal punto di vista di Minix, il dischetto inserito nella prima unità a dischetti corrisponde al dispositivo<br />

‘/dev/fd0’, come per GNU/Linux, ma risulta diviso in partizioni: l’immagine ‘ROOT’<br />

risulta essere ‘/dev/fd0a’ e l’immagine ‘USR’ è ‘/dev/fd0c’ (la partizione ‘b’ è vuota). 2<br />

L’archivio ‘USR.TAZ’ deve essere suddiviso in diversi dischetti, con un procedimento un po’ insolito:<br />

viene semplicemente tagliato a fettine della dimensione massima contenibile dal tipo di dischetti<br />

che si utilizza. Nel caso si tratti di dischetti da 1440 Kibyte, si può utilizzare GNU/Linux,<br />

o un altro sistema Unix, nel modo seguente:<br />

# dd if=/USR.TAZ <strong>of</strong>=/dev/fd0 bs=1440k count=1 skip=0<br />

# dd if=/USR.TAZ <strong>of</strong>=/dev/fd0 bs=1440k count=1 skip=1<br />

# dd if=/USR.TAZ <strong>of</strong>=/dev/fd0 bs=1440k count=1 skip=2<br />

Se si trattasse di dischetti da 1200 Kibyte, occorrerebbe modificare la dimensione del blocco,<br />

come nell’esempio seguente:<br />

# dd if=/USR.TAZ <strong>of</strong>=/dev/fd0 bs=1200k count=1 skip=0<br />

# dd if=/USR.TAZ <strong>of</strong>=/dev/fd0 bs=1200k count=1 skip=1<br />

# dd if=/USR.TAZ <strong>of</strong>=/dev/fd0 bs=1200k count=1 skip=2<br />

356.2.1 Nomi di dispositivo riferiti alle partizioni<br />

Minix viene installato normalmente all’interno di una partizione primaria suddivisa in almeno<br />

due partizioni secondarie. La prima partizione serve a contenere il file system principale ed è<br />

di piccole dimensioni: 1440 Kibyte. La seconda serve per tutto il resto e viene montata in corrispondenza<br />

della directory ‘/usr/’. Inizialmente le partizioni erano tre e ‘/usr/’ era la terza.<br />

Attualmente, ‘/usr/’ continua a essere la terza partizione e si finge che esista una seconda<br />

partizione senza alcuno spazio a disposizione.<br />

Il primo disco fisso viene identificato dal dispositivo ‘/dev/hd0’, il secondo da ‘/dev/hd5’.<br />

Le partizioni primarie del primo disco fisso vanno da ‘/dev/hd1’ a ‘/dev/hd4’; quelle del<br />

secondo disco fisso da ‘/dev/hd6’ a ‘/dev/hd9’. Le partizioni secondarie corrispondono al<br />

nome del dispositivo della partizione primaria con l’aggiunta di una lettera alfabetica che ne<br />

indica l’ordine.<br />

• /dev/hd0<br />

– /dev/hd1<br />

* /dev/hd1a<br />

2 Se si trattasse della seconda unità a dischetti, si parlerebbe di ‘/dev/fd1’, ‘/dev/fd1a’ e ‘/dev/fd1c’.


60 volume VIII Argomenti avanzati e accessori<br />

* /dev/hd1b<br />

* /dev/hd1c<br />

* ...<br />

– /dev/hd2<br />

– /dev/hd3<br />

– /dev/hd4<br />

• /dev/hd5<br />

– /dev/hd6<br />

– /dev/hd7<br />

– /dev/hd8<br />

– /dev/hd9<br />

Minix può essere installato in una partizione primaria qualunque, purché ci siano almeno 40 Mibyte<br />

a disposizione. Utilizzando la versione di Minix ‘i86’, non conviene tentare di superare i<br />

128 Mibyte.<br />

356.3 Avvio<br />

Minix utilizza un sistema di avvio piuttosto s<strong>of</strong>isticato; per fare un paragone con GNU/Linux, si<br />

tratta di qualcosa che compie le stesse funzioni di LILO, o di un cosiddetto bootloader.<br />

Il sistema che svolge questa funzione in Minix si chiama boot monitor ed è importante capire<br />

subito come si utilizza se non si ha molta memoria RAM a disposizione, quanta ne richiederebbe<br />

un disco RAM per l’immagine ‘ROOT’.<br />

Per cominciare, dopo aver preparato il dischetto ‘ROOT’+‘USR’, lo si inserisce senza la protezione<br />

contro la scrittura e si avvia l’elaboratore. Questo è ciò che appare.<br />

Minix boot monitor 2.5<br />

Press ESC to enter the monitor<br />

Hit a key as follows:<br />

= Start Minix<br />

Premendo il tasto [ Esc ] si attiva il boot monitor, mentre premendo [ = ] (si fa riferimento alla<br />

tastiera americana e questo simbolo si trova in corrispondenza della nostra lettera «ì») si avvia<br />

Minix con le impostazioni predefinite.<br />

Dal momento che si immagina di avere a disposizione poca memoria (solo 1 Mibyte), non<br />

si può avviare Minix così, perché il contenuto dell’immagine ‘ROOT’ verrebbe caricato come<br />

disco RAM. È necessario utilizzare subito il boot monitor.<br />

[ Esc ]<br />

[ESC]<br />

fd0><br />

Si ottiene un invito (prompt), attraverso il quale possono essere utilizzati alcuni comandi importanti<br />

per predisporre l’avvio del sistema Minix. Per ottenere aiuto si può utilizzare il comando<br />

‘help’.


Minix 61<br />

fd0> help [ Invio ]<br />

Si ottiene un riepilogo dei comandi e del modo con cui possono essere utilizzati. Le cose più importanti<br />

che si possono fare con il boot monitor sono: l’avvio a partire da una partizione differente<br />

da quella prestabilita; l’assegnamento o il ripristino al valore predefinito di una variabile.<br />

Queste variabili sono solo entità riferite al sistema di avvio e la loro modifica permette di cambiare<br />

il modo con cui si avvia il kernel. Il comando ‘set’ permette di elencare il contenuto di<br />

queste variabili.<br />

fd0> set [ Invio ]<br />

rootdev = (ram)<br />

ramimagedev = (bootdev)<br />

ramsize = (0)<br />

processor = (286)<br />

bus = (at)<br />

memsize = (640)<br />

emssize = (330)<br />

video = (vga)<br />

chrome = (mono)<br />

image = (minix)<br />

main() = (menu)<br />

I valori appaiono tutti tra parentesi tonde perché rappresentano le impostazioni predefinite.<br />

Quando si cambia qualche valore, questo appare senza le parentesi.<br />

La prima cosa da cambiare è il dispositivo di avvio, ‘rootdev’. Si deve assegnare il nome di<br />

dispositivo riferito all’immagine ‘ROOT’ su dischetto. Si tratta di ‘/dev/fd0a’, come dire, la<br />

prima partizione secondaria del dischetto. In questo caso, il nome del dispositivo può anche<br />

essere indicato senza la parte iniziale, limitandolo al solo ‘fd0a’.<br />

fd0> rootdev=fd0a [ Invio ]<br />

fd0> set [ Invio ]<br />

rootdev = fd0a<br />

ramimagedev = (bootdev)<br />

ramsize = (0)<br />

processor = (286)<br />

bus = (at)<br />

memsize = (640)<br />

emssize = (330)<br />

video = (vga)<br />

chrome = (mono)<br />

image = (minix)<br />

main() = (menu)<br />

Per avviare il sistema, basta utilizzare il comando ‘boot’ senza argomenti.<br />

fd0> boot [ Invio ]<br />

In questo modo si lascia il boot monitor e si avvia il kernel. Una volta avviato il sistema, viene<br />

richiesto immediatamente il montaggio della seconda immagine, ‘USR’, contenente gli strumenti<br />

necessari all’installazione. Avendo avviato senza disco RAM, il dischetto contenente l’immagine<br />

‘ROOT’ non può essere tolto e questo è il motivo per il quale deve essere contenuta nello stesso<br />

dischetto insieme a ‘USR’. 3<br />

3 Una cosa da sapere subito è che Minix non utilizza la sequenza [ Ctrl+C ] per interrompere un programma. Per questo<br />

si usa il tasto [ Canc ] da solo.


62 volume VIII Argomenti avanzati e accessori<br />

Minix 2.0.0 Copyright 1997 Prentice-Hall, Inc.<br />

Executing in 16-bit protected mode<br />

Memory size = 970K MINIX = 206K RAM disk = 0K Available = 765K<br />

Mon Nov 3 15:24:15 MET 1997<br />

Finish the name <strong>of</strong> device to mount as /usr: /dev/<br />

Date le premesse, occorre specificare il nome del dispositivo corrispondente all’immagine ‘USR’:<br />

si tratta di ‘fd0c’.<br />

fd0c [ Invio ]<br />

/dev/fd0c is read-write mounted on /usr<br />

Starting standard daemons: update.<br />

Login as root and run ’setup’ to install Minix.<br />

Minix Release 2.0 Version 0<br />

noname login:<br />

root [ Invio ]<br />

#<br />

356.4 Installazione<br />

L’installazione di Minix avviene in tre fasi:<br />

1. preparazione della partizione di destinazione;<br />

2. trasferimento del contenuto del dischetto, ovvero delle immagini ‘ROOT’ e ‘USR’;<br />

3. dopo il riavvio, trasferimento dell’archivio ‘USR.TAZ’ e possibilmente, se si dispone di una<br />

partizione di almeno 40 Mibyte, anche di ‘SYS.TAZ’ e ‘CMD.TAZ’.<br />

356.4.1 Setup<br />

Per iniziare l’installazione, dopo aver avviato il sistema Minix dal dischetto, si utilizza lo script<br />

‘setup’.<br />

# setup [ Invio ]


Minix 63<br />

This is the Minix installation script.<br />

Note 1: If the screen blanks suddenly then hit F3 to select "s<strong>of</strong>tware<br />

scrolling".<br />

Note 2: If things go wrong then hit DEL and start over.<br />

Note 3: The installation procedure is described in the manual page<br />

usage(8). It will be hard without it.<br />

Note 4: Some questions have default answers, like this: [y]<br />

Simply hit RETURN (or ENTER) if you want to choose that answer.<br />

Note 5: If you see a colon (:) then you should hit RETURN to continue.<br />

:<br />

[ Invio ]<br />

Dopo la breve spiegazione, avendo premuto il tasto [ Invio ] si passa all’indicazione del tipo di<br />

tastiera. La scelta è ovvia, ‘italian’, anche se non corrisponde esattamente: la barra verticale<br />

(quella per le pipeline) si trova al posto della lettera «ì» (i accentata). Durante questa fase di<br />

installazione conviene utilizzare la tastiera nazionale (‘italian’) per evitare spiacevoli incidenti<br />

quando si utilizza il programma di gestione delle partizioni.<br />

What type <strong>of</strong> keyboard do you have? You can choose one <strong>of</strong>:<br />

french italian latin-am scandinavn uk us-swap<br />

german japanese olivetti spanish us-std<br />

Keyboard type? [us-std]<br />

italian [ Invio ]<br />

Minix needs one primary partition <strong>of</strong> at least 30 Mb (it fits in 20 Mb, but<br />

it needs 30 Mb if fully recompiled. Add more space to taste.)<br />

If there is no free space on your disk then you have to back up one <strong>of</strong> the<br />

other partitions, shrink, and reinstall. See the appropriate manuals <strong>of</strong> the<br />

the operating systems currently installed. Restart your Minix installation<br />

after you have made space.<br />

To make this partition you will be put in the editor "part". Follow the<br />

advice under the ’!’ key to make a new partition <strong>of</strong> type MINIX. Do not<br />

touch an existing partition unless you know precisely what you are doing!<br />

Please note the name <strong>of</strong> the partition (hd1, hd2, ..., hd9, sd1, sd2, ...<br />

sd9) you make. (See the devices section in usage(8) on Minix device names.)<br />

:<br />

Il programma di Minix che permette di accedere alla tabella delle partizioni è ‘part’ ed è ciò che<br />

sta per essere avviato. Come sempre, l’uso di un programma di questo genere è molto delicato:<br />

un piccolo errore mette fuori uso tutti i dati eventualmente contenuti in altre partizioni.<br />

[ Invio ]


64 volume VIII Argomenti avanzati e accessori<br />

Select device ----first---- --geom/last-- ------sectors-----<br />

Device Cyl Head Sec Cyl Head Sec Base Size Kb<br />

/dev/hd0 ? ? ?<br />

? ? ? ? ? ? ? ? ?<br />

Num Sort Type<br />

? ? ? ? ? ? ? ? ? ? ? ? ?<br />

? ? ? ? ? ? ? ? ? ? ? ? ?<br />

? ? ? ? ? ? ? ? ? ? ? ? ?<br />

? ? ? ? ? ? ? ? ? ? ? ? ?<br />

Type ’+’ or ’-’ to change, ’r’ to read, ’?’ for more help, ’!’ for advice<br />

Prima di utilizzare questo programma conviene leggere la sua guida interna, ottenibile con la<br />

pressione del tasto [ ? ]. Il cursore si presenta inizialmente sull’indicazione del disco, ‘/dev/<br />

hd0’, e può essere cambiato semplicemente premendo i tasti [ + ] o [ - ]. Una volta raggiunto<br />

il disco desiderato (in questo caso il primo disco va bene), si deve leggere la sua tabella delle<br />

partizioni, in modo da rimpiazzare tutti i punti interrogativi che riempiono lo schermo.<br />

[ r ]<br />

Select device ----first---- --geom/last-- ------sectors-----<br />

Device Cyl Head Sec Cyl Head Sec Base Size Kb<br />

/dev/hd0 615 8 17<br />

0 0 0 614 7 16 0 83640 41820<br />

Num Sort Type<br />

1* hd1 86 DOS-BIG 0 1 0 613 7 16 17 83487 41743<br />

2 hd2 00 None 0 0 0 0 0 -1 0 0 0<br />

3 hd3 00 None 0 0 0 0 0 -1 0 0 0<br />

4 hd4 00 None 0 0 0 0 0 -1 0 0 0<br />

Con questo esempio si suppone di avere solo un vecchio disco fisso MFM di circa 40 Mibyte, nel<br />

quale la prima partizione primaria era stata dedicata in precedenza al Dos. Così, basta cambiare<br />

il numero che identifica il tipo di partizione. Per farlo, vi si posiziona sopra il cursore, spostandolo<br />

con i tasti freccia, quindi si usano i tasti [ + ] o [ - ] fino a fare apparire il numero 8116. Al<br />

primo intervento per cambiare un valore qualsiasi, viene richiesto esplicitamente se si intende<br />

modificare effettivamente i dati della tabella delle partizioni.<br />

Do you wish to modify existing partitions (y/n) [ y ]<br />

Una volta modificato il tipo, la prima partizione dovrebbe apparire così come segue:<br />

Num Sort Type<br />

1* hd1 81 MINIX 0 1 0 613 7 16 17 83487 41743<br />

Quindi si conclude.<br />

[ q ]<br />

Save partition table? (y/n) [ y ]<br />

Lo script di configurazione e installazione riprende richiedendo quale sia la partizione su cui<br />

installare Minix. In questo caso si tratta della prima, cioè ‘/dev/hd1’.<br />

Please finish the name <strong>of</strong> the primary partition you have created:<br />

(Just type RETURN if you want to rerun "part") /dev/


Minix 65<br />

hd1 [ Invio ]<br />

You have created a partition named: /dev/hd1<br />

The following subpartitions are about to be created on /dev/hd3:<br />

Root subpartition: /dev/hd1a 1440 kb<br />

/usr subpartition: /dev/hd1c rest <strong>of</strong> hd1<br />

Hit return if everything looks fine, or hit DEL to bail out if you want to<br />

think it over. The next step will destroy /dev/hd1.<br />

:<br />

Come accennato in precedenza, Minix viene installato in due partizioni secondarie: la prima<br />

serve a contenere il file system principale, la seconda per il resto. In seguito si possono montare<br />

anche partizioni successive.<br />

Migrating from floppy to disk...<br />

Scanning /dev/hd1c for bad blocks. (Hit DEL to stop the scan if are absolutely<br />

sure that there can not be any bad blocks. Otherwise just wait.)<br />

La scansione del disco fisso è necessaria se si utilizza un vecchio disco MFM, come si suppone<br />

di avere in questo esempio, mentre può essere inutile con un disco ATA. È importante fare attenzione:<br />

se ci sono settori inutilizzabili, vengono creati alcuni file ‘/usr/.Bad_ * ’ che non vanno<br />

cancellati! Alla fine, lo script procede a copiare il contenuto del dischetto nel disco fisso.<br />

What is the memory size <strong>of</strong> this system in kilobytes? [4096 or more]<br />

La dimensione di memoria RAM disponibile effettivamente è solo di 970 Kibyte, quindi si inserisce<br />

questo valore; se la memoria a disposizione fosse maggiore o uguale a 4 Mibyte, non<br />

occorrerebbe indicare alcunché, basterebbe solo confermare.<br />

970 [ Invio ]<br />

Second level file system block cache set to 0 kb.<br />

A questo punto, termina l’installazione del dischetto nel disco fisso e si può passare a riavviare il<br />

sistema da lì.<br />

Please insert the installation ROOT floppy and type ’halt’ to exit Minix.<br />

You can type ’boot hd1’ to try the newly installed Minix system. See<br />

"TESTING" in the usage manual.<br />

# halt [ Invio ]<br />

System Halted


66 volume VIII Argomenti avanzati e accessori<br />

356.4.2 Avvio del sistema copiato nel disco fisso<br />

Una volta conclusa l’esecuzione dello script di configurazione e installazione, si ritorna sotto il<br />

controllo del boot monitor, attraverso il quale è possibile avviare il sistema dalla prima partizione<br />

del disco fisso.<br />

fd0> boot /dev/hd1 [ Invio ]<br />

Minix 2.0.0 Copyright 1997 Prentice-Hall, Inc.<br />

Executing in 16-bit protected mode<br />

at-hd0: 615x8x17<br />

Memory size = 970K MINIX = 206K RAM disk = 0K Available = 765K<br />

Mon Nov 3 16:01:27 MET 1997<br />

Starting standard daemons: update.<br />

Login as root and run ’setup /usr’ to install floppy sets.<br />

Minix Release 2.0 Version 0<br />

noname login:<br />

# root [ Invio ]<br />

Il suggerimento dato all’avvio ricorda che è possibile installare altre serie di dischetti, a<br />

cominciare da ‘USR.TAZ’, utilizzando il comando ‘setup /usr’.<br />

356.4.3 Installazione delle serie di dischetti<br />

Tra i pacchetti di Minix, ‘USR.TAZ’ è essenziale e cambia a seconda del tipo di architettura (i86<br />

o i386). Però, dal momento che c’è spazio sufficiente nel disco fisso, conviene installare anche<br />

‘SYS.TAZ’ per poter ricompilare il kernel e ‘CMD.TAZ’ che contiene i sorgenti dei vari programmi<br />

di servizio.<br />

Tutti questi pacchetti devono essere suddivisi in dischetti nel modo visto in precedenza per il<br />

caso di ‘USR.TAZ’.<br />

# setup /usr [ Invio ]<br />

Lo script ‘setup’ chiede una serie di conferme.<br />

What is the size <strong>of</strong> the images on the diskettes? [all]<br />

Premendo semplicemente [ Invio ] si intende che i dischetti vadano letti nella loro interezza.<br />

[ Invio ]<br />

What floppy drive to use? [0]<br />

Premendo semplicemente [ Invio ] si fa riferimento alla prima unità, ‘/dev/fd0’.<br />

[ Invio ]<br />

Please insert input volume 1 and hit return


Minix 67<br />

Si inserisce il primo dischetto e si conferma<br />

[ Invio ]<br />

Inizia la fase di estrazione di quanto contenuto nel primo dischetto, a partire dalla directory<br />

‘/usr/’. Quando termina l’estrazione del primo dischetto, viene richiesto il successivo, fino alla<br />

conclusione.<br />

Conviene ripetere la procedura fino a quando sono stati installati anche gli archivi ‘SYS.TAZ’ e<br />

‘CMD.TAZ’.<br />

356.4.4 Dischetto di avvio<br />

Minix è molto semplice e non è necessario un dischetto di avvio realizzato appositamente. È sufficiente<br />

il dischetto utilizzato per iniziare l’installazione. Se si hanno difficoltà con l’avviamento<br />

di Minix dal disco fisso, si può avviare il boot monitor dal dischetto e con quello utilizzare il<br />

comando ‘boot /dev/hd1’.<br />

356.4.5 Conclusione<br />

Per chiudere l’attività di Minix, si può fare nel solito modo comune a quasi tutti i sistemi Unix.<br />

# shutdown -h now [ Invio ]<br />

356.5 Ricompilazione del kernel<br />

Anche Minix, nella sua semplicità, richiede una ricompilazione del kernel per la sua ottimizzazione.<br />

In particolare, per poter attivare la gestione del TCP/IP occorre passare per la configurazione<br />

e ricompilazione.<br />

Il file del kernel, secondo la tradizione di Minix, dovrebbe trovarsi nella directory radice e avere<br />

il nome ‘minix’. Se però, invece di trattarsi di un file, si tratta di una directory, nella fase di avvio<br />

viene eseguito il file più recente contenuto in tale directory. Il kernel normale, cioè quello che si<br />

trova dopo l’installazione, dovrebbe essere ‘/minix/2.0.0’.<br />

Per poter ricompilare il kernel occorre avere installato il pacchetto ‘SYS.TAZ’. Si procede come<br />

segue:<br />

1. si modifica il file ‘/usr/include/minix/config.h’;<br />

2. ci si posiziona nella directory ‘/usr/src/tools/’;<br />

3. si avvia la compilazione con il comando ‘make’.<br />

Al termine si ottiene il file del kernel (o immagine) corrispondente a ‘/usr/src/tools/image’<br />

che si può copiare e rinominare come si ritiene più opportuno.


68 volume VIII Argomenti avanzati e accessori<br />

356.5.1 /usr/include/minix/config.h<br />

La configurazione che viene proposta deriva dagli esempi precedenti, in cui si ha una particolare<br />

penuria di memoria. Seguono solo alcuni pezzi.<br />

/* If ROBUST is set to 1, writes <strong>of</strong> i-node, directory, and indirect blocks<br />

* from the cache happen as soon as the blocks are modified. This gives a more<br />

* robust, but slower, file system. If it is set to 0, these blocks are not<br />

* given any special treatment, which may cause problems if the system crashes.<br />

*/<br />

#define ROBUST 1 /* 0 for speed, 1 for robustness */<br />

La macro ‘ROBUST’ permette di sincronizzare le operazioni di accesso al disco. Nell’esempio<br />

mostrato si attiva questa opzione, in modo da poter utilizzare il sistema con tranquillità (e<br />

ovviamente con maggiore lentezza).<br />

/* Number <strong>of</strong> slots in the process table for user processes. */<br />

#define NR_PROCS 32<br />

Il numero massimo dei processi eseguibili può essere una seria limitazione all’uso simultaneo<br />

dell’elaboratore da parte di più utenti, ma la scarsa memoria a disposizione consiglia di mantenere<br />

basso questo valore.<br />

/* Enable or disable the second level file system cache on the RAM disk. */<br />

#define ENABLE_CACHE2 0<br />

Sempre a causa della carenza di memoria, è opportuno disabilitare la memoria cache.<br />

/* Include or exclude device drivers. Set to 1 to include, 0 to exclude. */<br />

#define ENABLE_NETWORKING 1 /* enable TCP/IP code */<br />

#define ENABLE_AT_WINI 1 /* enable AT winchester driver */<br />

#define ENABLE_BIOS_WINI 1 /* enable BIOS winchester driver */<br />

#define ENABLE_ESDI_WINI 1 /* enable ESDI winchester driver */<br />

#define ENABLE_XT_WINI 0 /* enable XT winchester driver */<br />

#define ENABLE_ADAPTEC_SCSI 0 /* enable ADAPTEC SCSI driver */<br />

#define ENABLE_MITSUMI_CDROM 0 /* enable Mitsumi CD-ROM driver */<br />

#define ENABLE_SB_AUDIO 0 /* enable Soundblaster audio driver */<br />

In questa sezione è importante abilitare ciò che serve ed eliminare il resto. In particolare, è qui<br />

che si attiva la connettività TCP/IP, che non risulta attivata in modo predefinito.<br />

/* NR_CONS, NR_RS_LINES, and NR_PTYS determine the number <strong>of</strong> terminals the<br />

* system can handle.<br />

*/<br />

#define NR_CONS 2 /* # system consoles (1 to 8) */<br />

#define NR_RS_LINES 1 /* # rs232 terminals (0, 1, or 2) */<br />

#define NR_PTYS 2 /* # pseudo terminals (0 to 64) */<br />

Il numero predefinito di console virtuali è due, ma può essere espanso, sempre che ciò possa<br />

avere senso date le limitazioni del sistema. Invece è importante attivare gli pseudoterminali, cioè<br />

il numero massimo di connessioni remote. Volendo gestire la rete, è il caso di indicare almeno<br />

uno pseduoterminale.<br />

Per modificare il file ‘/usr/include/minix/config.h’ si può utilizzare ‘vi’, che è un<br />

collegamento a ‘elvis’, oppure ‘elle’. 4<br />

Si procede con la compilazione.<br />

4 Il programma ‘elvis’ in particolare, è molto tradizionale: i tasti freccia generano proprio le lettere corrispondenti e<br />

quindi non possono essere usati durante la fase di inserimento. ‘elle’ è un Emacs ridotto al minimo.


Minix 69<br />

# cd /usr/src/tools [ Invio ]<br />

# make [ Invio ]<br />

Al termine della compilazione, se non sono occorsi incidenti, si ottiene il file ‘image’.<br />

# cp image /minix/rete.0.1 [ Invio ]<br />

Questo dovrebbe bastare, trattandosi del file più recente nella directory ‘/minix/’, è anche quello<br />

che verrà avviato la prossima volta.<br />

# shutdown -h [ Invio ]<br />

356.5.2 File di dispositivo<br />

Quando si ricompila il kernel è probabile che si renda necessaria la creazione di file di dispositivo<br />

che prima non erano necessari. Nel caso della gestione della rete, sono necessari i file seguenti:<br />

• ‘/dev/eth’;<br />

• ‘/dev/ip’;<br />

• ‘/dev/tcp’;<br />

• ‘/dev/udp’;<br />

• ‘/dev/ttyp0’ e successivi;<br />

• ‘/dev/ptyp0’ e successivi.<br />

Questo ragionamento vale anche per le console virtuali: se si vogliono molte console, forse è<br />

necessario aggiungere i file relativi.<br />

Probabilmente c’è già tutto ciò di cui si può avere bisogno, ma se manca si può creare con lo<br />

script ‘MAKEDEV’.<br />

MAKEDEV dispositivo<br />

Per esempio, trovandosi già nella directory ‘/dev/’, si può creare il dispositivo ‘/dev/tcp’ nel<br />

modo seguente:<br />

# MAKEDEV tcp<br />

356.6 Parametri di avvio<br />

Anche Minix richiede alcuni parametri di avvio in presenza di hardware particolare. La gestione<br />

di questi avviene in modo molto semplice attraverso il boot monitor: basta definire una nuova<br />

variabile, assegnandole il valore corretto.


70 volume VIII Argomenti avanzati e accessori<br />

356.6.1 Scheda di rete<br />

Per gestire una rete occorre una scheda di rete Ethernet. Nell’esempio seguente si immagina di<br />

disporre di una scheda compatibile con il modello NE2000 configurata con indirizzo di I/O 30016<br />

e IRQ 11.<br />

Il parametro di avvio per ottenere il riconoscimento della scheda Ethernet è ‘DPETHn’, dove n è<br />

il numero della scheda, a partire da zero.<br />

DPETHn=indirizzo_i/o:irq:indirizzo_di_memoria<br />

La scheda NE2000 non utilizza alcun indirizzo di memoria, quindi, per il nostro esempio occorre<br />

il parametro seguente:<br />

DPETH0=300:11<br />

Come si vede, l’indirizzo di I/O è espresso implicitamente in esadecimale e l’IRQ in decimale,<br />

mentre l’indirizzo di memoria viene omesso trattandosi di una NE2000. Per inserire tale<br />

parametro si utilizza il boot monitor nel modo seguente:<br />

hd0> DPETH0=300:11 [ Invio ]<br />

hd0> save [ Invio ]<br />

L’ultima istruzione, ‘save’, salva questo parametro che altrimenti dovrebbe essere indicato ogni<br />

volta che si avvia il sistema.<br />

Se la scheda di rete viene riconosciuta, all’avvio appare il messaggio seguente:<br />

Minix 2.0.0 Copyright 1997 Prentice-Hall, Inc.<br />

Executing in 16-bit protected mode<br />

ne2000: NE2000 at 300:11<br />

356.7 Configurazione della rete<br />

La configurazione della rete va fatta con cura, in modo da non avere bisogno di alcuni demoni che<br />

permettono una sorta di autoconfigurazione. Negli esempi seguenti si configura il nuovo sistema<br />

Minix tenendo conto di questa situazione:<br />

• dinkel.brot.dg, IP 192.168.1.1, servizio DNS e router predefinito;<br />

• minix.brot.dg, IP 192.168.1.25, elaboratore Minix.<br />

Per quanto possibile, si fa in modo di non avere bisogno del DNS.


Minix 71<br />

356.7.1 /etc/hosts<br />

Volendo attivare localmente la risoluzione dei nomi e degli indirizzi è necessario il file ‘/etc/<br />

hosts’, che va configurato come al solito, esattamente come si fa con GNU/Linux.<br />

127.0.0.1 localhost<br />

192.168.1.1 dinkel.brot.dg<br />

192.168.1.25 minix.brot.dg<br />

356.7.2 /etc/hostname.file<br />

Il file ‘/etc/hostname.file’ serve solo a definire il nome dell’elaboratore locale, in senso<br />

generale. Non ha niente a che vedere con le interfacce di rete.<br />

# echo "minix.brot.dg" > /etc/hostname.file [ Invio ]<br />

356.7.3 /etc/resolv.conf<br />

Il file ‘/etc/resolv.conf’ permette di indicare gli indirizzi dei nodi che forniscono un servizio<br />

DNS. Nell’esempio proposto si vuole fare in modo che il sistema di risoluzione dei nomi avvenga<br />

localmente, per mezzo di quanto contenuto nel file ‘/etc/hosts’. Per questo viene indicato<br />

come servente DNS anche l’indirizzo locale (loopback).<br />

nameserver 127.0.0.1<br />

nameserver 192.168.1.1<br />

356.7.4 /etc/rc.net<br />

Lo script ‘/etc/rc.net’ viene utilizzato da ‘/etc/rc’ per attivare la rete. Lo si può utilizzare<br />

per attivare l’interfaccia di rete e per definire l’instradamento verso il router (l’instradamento<br />

verso la rete connessa all’interfaccia è predefinito).<br />

# Attiva l’interfaccia e l’instradamento verso la sua rete.<br />

ifconfig -h 192.168.1.25<br />

# Definisce l’instradamento predefinito verso il router<br />

add_route -g 192.168.1.1<br />

356.7.5 /etc/rc<br />

Probabilmente, è utile ritoccare il file ‘/etc/rc’, per eliminare l’avvio automatico di alcuni<br />

demoni inutili dal momento che la rete è configurata. Quello che segue è il pezzo che attiva la<br />

gestione della rete.<br />

# Network initialization.<br />

(


72 volume VIII Argomenti avanzati e accessori<br />

fi<br />

ifconfig -h 192.9.200.1<br />

if [ "$net" ]<br />

then<br />

echo -n "Starting network daemons: "<br />

for daemon in rarpd nonamed irdpd talkd<br />

do<br />

if [ -f /usr/bin/$daemon ]<br />

then<br />

echo -n " $daemon"<br />

$daemon &<br />

fi<br />

done<br />

echo .<br />

fi<br />

# Get the nodename from the DNS and set it.<br />

hostaddr -a >/etc/hostname.file || echo noname >/etc/hostname.file<br />

echo -n "Starting network services:"<br />

for pair in ’shell in.rshd’ ’login in.rld’ \<br />

’telnet in.telnetd’ ’ftp in.ftpd’<br />

do<br />

set $pair<br />

if [ -f /usr/bin/$2 ]<br />

then<br />

echo -n " $1"<br />

tcpd $1 /usr/bin/$2 &<br />

fi<br />

done<br />

echo .<br />

Vale la pena di modificare quanto segue:<br />

if [ "$net" ]<br />

then<br />

echo -n "Starting network daemons: "<br />

for daemon in nonamed talkd ### rarpd nonamed irdpd talkd<br />

do<br />

...<br />

Nel pezzo precedente non vengono avviati i demoni ‘rarpd’ e ‘irdpd’, che sono necessari rispettivamente<br />

per ottenere l’indirizzo IP in base all’indirizzo hardware della scheda Ethernet<br />

e a definire gli instradamenti verso i router. Eventualmente, si potrebbe anche evitare di avviare<br />

‘talkd’ se non si intende utilizzare ‘talk’. Il demone ‘nonamed’ è necessario se non si vuole essere<br />

obbligati ad avere un servizio DNS esterno; in pratica è necessario perché venga interpretato<br />

il contenuto del file ‘/etc/hosts’.<br />

356.8 Personalizzazione<br />

Il sistema risulta configurato in maniera piuttosto disordinata, a cominciare dal fatto che la directory<br />

personale dell’utente ‘root’ corrisponde alla directory radice; così, al suo interno si trovano<br />

i file di configurazione dell’amministratore. Probabilmente, la prima cosa da fare è quella di<br />

creare una directory ‘/root/’, porvi al suo interno i file di configurazione (dovrebbe trattarsi di<br />

‘.ellepro.b1’, ‘.exrc’ e ‘.pr<strong>of</strong>ile’), modificando anche il file ‘/etc/passwd’ in modo da<br />

assegnare all’utente ‘root’ questa nuova directory.


Minix 73<br />

356.8.1 /etc/passwd, /etc/group e /etc/shadow<br />

Minix, nonostante la sua semplicità, utilizza le password shadow. Pertanto, se si tenta di inserire<br />

un utente manualmente, occorre intervenire anche su questo file, ‘/etc/shadow’, altrimenti<br />

l’utente non riuscirà ad accedere.<br />

Il file ‘/etc/group’, se non va bene com’è, deve essere modificato manualmente, mentre per gli<br />

utenti conviene affidarsi allo script ‘adduser’.<br />

adduser utente gruppo directory_home<br />

Dopo aver creato un utente, come al solito è opportuno utilizzare il programma ‘passwd’ per<br />

assegnare la parola d’ordine. 5<br />

356.9 Tastiera<br />

La mappa della tastiera viene definita attraverso il programma ‘loadkeys’ e il file contenente la<br />

mappa desiderata. Per cui,<br />

# loadkeys ./tastiera.map<br />

permette di caricare la mappa del file ‘tastiera.map’ contenuto nella directory corrente.<br />

La mappa della tastiera, secondo la scelta fatta durante l’installazione di Minix, avviene per<br />

mezzo del file ‘/etc/keymap’: se lo script ‘/etc/rc’ lo trova durante la fase di avvio, lo carica<br />

attraverso ‘loadkeys’.<br />

356.9.1 Modifica della mappa<br />

La configurazione della tastiera italiana, per quanto riguarda la versione 2.0 di Minix, non è<br />

perfetta. Per modificare la mappa occorre intervenire sul file ‘/usr/src/kernel/keymaps/<br />

italian.src’. Dopo la modifica si deve compilare il sorgente in modo da ottenere il file<br />

‘/usr/src/kernel/keymaps/italian.map’. Al termine, questo file va copiato e rinominato<br />

in modo da sostituire ‘/etc/keymap’.<br />

Il sorgente corretto potrebbe apparire come nell’esempio seguente, in particolare, per ottenere la<br />

tilde (‘~’) si deve usare la combinazione [ AltGr+ì ], mentre per ottenere l’apostr<strong>of</strong>o inverso (‘‘’)<br />

si deve usare la combinazione [ AltGr+’ ]. I caratteri che si trovano oltre il settimo bit, vengono<br />

rappresentati in ottale.<br />

/* Modified by Daniele Giacomini daniele @ swlibero.org 1998.12.22 */<br />

/* Keymap for Italian standard keyboard, similar to Linux layout. */<br />

u16_t keymap[NR_SCAN_CODES * MAP_COLS] = {<br />

/* scan-code !Shift Shift Alt AltGr Alt+Sh Ctrl */<br />

/* ==================================================================== */<br />

/* 00 - none */ 0, 0, 0, 0, 0, 0,<br />

/* 01 - ESC */ C(’[’), C(’[’), CA(’[’),C(’[’), C(’[’), C(’[’),<br />

/* 02 - ’1’ */ ’1’, ’!’, A(’1’), ’1’, ’!’, C(’A’),<br />

/* 03 - ’2’ */ ’2’, ’"’, A(’2’), ’2’, ’@’, C(’@’),<br />

/* 04 - ’3’ */ ’3’, 0234, A(’3’), ’3’, 0234, C(’C’),<br />

/* 05 - ’4’ */ ’4’, ’$’, A(’4’), ’4’, ’$’, C(’D’),<br />

5 Lo script ‘adduser’ si avvale della directory personale dell’utente ‘ast’ per inserire i file di configurazione iniziali.<br />

Questa directory, corrispondente a ‘/usr/ast/’, svolge il ruolo di scheletro delle directory personali da creare. Volendo<br />

si può realizzare un proprio script per rendere la cosa più elegante.


74 volume VIII Argomenti avanzati e accessori<br />

/* 06 - ’5’ */ ’5’, ’%’, A(’5’), ’5’, ’%’, C(’E’),<br />

/* 07 - ’6’ */ ’6’, ’&’, A(’6’), ’6’, ’&’, C(’F’),<br />

/* 08 - ’7’ */ ’7’, ’/’, A(’7’), ’{’, ’/’, C(’G’),<br />

/* 09 - ’8’ */ ’8’, ’(’, A(’8’), ’[’, ’(’, C(’H’),<br />

/* 10 - ’9’ */ ’9’, ’)’, A(’9’), ’]’, ’)’, C(’I’),<br />

/* 11 - ’0’ */ ’0’, ’=’, A(’0’), ’}’, ’=’, C(’@’),<br />

/* 12 - ’-’ */ ’\’’, ’?’, A(’\’’),’\‘’, ’?’, C(’@’),<br />

/* 13 - ’=’ */ 0215, ’^’, 0215, ’~’, ’^’, C(’^’),<br />

/* 14 - BS */ C(’H’), C(’H’), CA(’H’),C(’H’), C(’H’), 0177,<br />

/* 15 - TAB */ C(’I’), C(’I’), CA(’I’),C(’I’), C(’I’), C(’I’),<br />

/* 16 - ’q’ */ L(’q’), ’Q’, A(’q’), ’q’, ’Q’, C(’Q’),<br />

/* 17 - ’w’ */ L(’w’), ’W’, A(’w’), ’w’, ’W’, C(’W’),<br />

/* 18 - ’e’ */ L(’e’), ’E’, A(’e’), ’e’, ’E’, C(’E’),<br />

/* 19 - ’r’ */ L(’r’), ’R’, A(’r’), ’r’, ’R’, C(’R’),<br />

/* 20 - ’t’ */ L(’t’), ’T’, A(’t’), ’t’, ’T’, C(’T’),<br />

/* 21 - ’y’ */ L(’y’), ’Y’, A(’y’), ’y’, ’Y’, C(’Y’),<br />

/* 22 - ’u’ */ L(’u’), ’U’, A(’u’), ’u’, ’U’, C(’U’),<br />

/* 23 - ’i’ */ L(’i’), ’I’, A(’i’), ’i’, ’I’, C(’I’),<br />

/* 24 - ’o’ */ L(’o’), ’O’, A(’o’), ’o’, ’O’, C(’O’),<br />

/* 25 - ’p’ */ L(’p’), ’P’, A(’p’), ’p’, ’P’, C(’P’),<br />

/* 26 - ’[’ */ 0212, 0202, 0212, ’[’, ’{’, C(’[’),<br />

/* 27 - ’]’ */ ’+’, ’*’, A(’+’), ’]’, ’}’, C(’]’),<br />

/* 28 - CR/LF */ C(’M’), C(’M’), CA(’M’),C(’M’), C(’M’), C(’J’),<br />

/* 29 - Ctrl */ CTRL, CTRL, CTRL, CTRL, CTRL, CTRL,<br />

/* 30 - ’a’ */ L(’a’), ’A’, A(’a’), ’a’, ’A’, C(’A’),<br />

/* 31 - ’s’ */ L(’s’), ’S’, A(’s’), ’s’, ’S’, C(’S’),<br />

/* 32 - ’d’ */ L(’d’), ’D’, A(’d’), ’d’, ’D’, C(’D’),<br />

/* 33 - ’f’ */ L(’f’), ’F’, A(’f’), ’f’, ’F’, C(’F’),<br />

/* 34 - ’g’ */ L(’g’), ’G’, A(’g’), ’g’, ’G’, C(’G’),<br />

/* 35 - ’h’ */ L(’h’), ’H’, A(’h’), ’h’, ’H’, C(’H’),<br />

/* 36 - ’j’ */ L(’j’), ’J’, A(’j’), ’j’, ’J’, C(’J’),<br />

/* 37 - ’k’ */ L(’k’), ’K’, A(’k’), ’k’, ’K’, C(’K’),<br />

/* 38 - ’l’ */ L(’l’), ’L’, A(’l’), ’l’, ’L’, C(’L’),<br />

/* 39 - ’;’ */ 0225, 0207, 0225, ’@’, ’@’, C(’@’),<br />

/* 40 - ’\’’ */ 0205, 0370, 0205, ’#’, ’#’, C(’@’),<br />

/* 41 - ’‘’ */ ’\\’, ’|’, ’\\’, ’\\’, ’|’, C(’\\’),<br />

/* 42 - l. SHIFT*/ SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, SHIFT,<br />

/* 43 - ’\\’ */ 0227, ’|’, 0227, 0227, ’|’, C(’@’),<br />

/* 44 - ’z’ */ L(’z’), ’Z’, A(’z’), ’z’, ’Z’, C(’Z’),<br />

/* 45 - ’x’ */ L(’x’), ’X’, A(’x’), ’x’, ’X’, C(’X’),<br />

/* 46 - ’c’ */ L(’c’), ’C’, A(’c’), ’c’, ’C’, C(’C’),<br />

/* 47 - ’v’ */ L(’v’), ’V’, A(’v’), ’v’, ’V’, C(’V’),<br />

/* 48 - ’b’ */ L(’b’), ’B’, A(’b’), ’b’, ’B’, C(’B’),<br />

/* 49 - ’n’ */ L(’n’), ’N’, A(’n’), ’n’, ’N’, C(’N’),<br />

/* 50 - ’m’ */ L(’m’), ’M’, A(’m’), ’m’, ’M’, C(’M’),<br />

/* 51 - ’,’ */ ’,’, ’;’, A(’,’), ’,’, ’;’, C(’@’),<br />

/* 52 - ’.’ */ ’.’, ’:’, A(’.’), ’.’, ’:’, C(’@’),<br />

/* 53 - ’/’ */ ’-’, ’_’, A(’-’), ’-’, ’_’, C(’_’),<br />

/* 54 - r. SHIFT*/ SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, SHIFT,<br />

/* 55 - ’*’ */ ’*’, ’*’, A(’*’), ’*’, ’*’, C(’M’),<br />

/* 56 - ALT */ ALT, ALT, ALT, ALT, ALT, ALT,<br />

/* 57 - ’ ’ */ ’ ’, ’ ’, A(’ ’), ’ ’, ’ ’, C(’@’),<br />

/* 58 - CapsLck */ CALOCK, CALOCK, CALOCK, CALOCK, CALOCK, CALOCK,<br />

/* 59 - F1 */ F1, SF1, AF1, AF1, ASF1, CF1,<br />

/* 60 - F2 */ F2, SF2, AF2, AF2, ASF2, CF2,<br />

/* 61 - F3 */ F3, SF3, AF3, AF3, ASF3, CF3,<br />

/* 62 - F4 */ F4, SF4, AF4, AF4, ASF4, CF4,<br />

/* 63 - F5 */ F5, SF5, AF5, AF5, ASF5, CF5,<br />

/* 64 - F6 */ F6, SF6, AF6, AF6, ASF6, CF6,<br />

/* 65 - F7 */ F7, SF7, AF7, AF7, ASF7, CF7,<br />

/* 66 - F8 */ F8, SF8, AF8, AF8, ASF8, CF8,<br />

/* 67 - F9 */ F9, SF9, AF9, AF9, ASF9, CF9,<br />

/* 68 - F10 */ F10, SF10, AF10, AF10, ASF10, CF10,<br />

/* 69 - NumLock */ NLOCK, NLOCK, NLOCK, NLOCK, NLOCK, NLOCK,


Minix 75<br />

/* 70 - ScrLock */ SLOCK, SLOCK, SLOCK, SLOCK, SLOCK, SLOCK,<br />

/* 71 - Home */ HOME, ’7’, AHOME, AHOME, ’7’, CHOME,<br />

/* 72 - CurUp */ UP, ’8’, AUP, AUP, ’8’, CUP,<br />

/* 73 - PgUp */ PGUP, ’9’, APGUP, APGUP, ’9’, CPGUP,<br />

/* 74 - ’-’ */ NMIN, ’-’, ANMIN, ANMIN, ’-’, CNMIN,<br />

/* 75 - Left */ LEFT, ’4’, ALEFT, ALEFT, ’4’, CLEFT,<br />

/* 76 - MID */ MID, ’5’, AMID, AMID, ’5’, CMID,<br />

/* 77 - Right */ RIGHT, ’6’, ARIGHT, ARIGHT, ’6’, CRIGHT,<br />

/* 78 - ’+’ */ PLUS, ’+’, APLUS, APLUS, ’+’, CPLUS,<br />

/* 79 - End */ END, ’1’, AEND, AEND, ’1’, CEND,<br />

/* 80 - Down */ DOWN, ’2’, ADOWN, ADOWN, ’2’, CDOWN,<br />

/* 81 - PgDown */ PGDN, ’3’, APGDN, APGDN, ’3’, CPGDN,<br />

/* 82 - Insert */ INSRT, ’0’, AINSRT, AINSRT, ’0’, CINSRT,<br />

/* 83 - Delete */ 0177, ’.’, A(0177),0177, ’.’, 0177,<br />

/* 84 - Enter */ C(’M’), C(’M’), CA(’M’),C(’M’), C(’M’), C(’J’),<br />

/* 85 - ??? */ 0, 0, 0, 0, 0, 0,<br />

/* 86 - ??? */ ’’, A(’’, C(’@’),<br />

/* 87 - F11 */ F11, SF11, AF11, AF11, ASF11, CF11,<br />

/* 88 - F12 */ F12, SF12, AF12, AF12, ASF12, CF12,<br />

/* 89 - ??? */ 0, 0, 0, 0, 0, 0,<br />

/* 90 - ??? */ 0, 0, 0, 0, 0, 0,<br />

/* 91 - ??? */ 0, 0, 0, 0, 0, 0,<br />

/* 92 - ??? */ 0, 0, 0, 0, 0, 0,<br />

/* 93 - ??? */ 0, 0, 0, 0, 0, 0,<br />

/* 94 - ??? */ 0, 0, 0, 0, 0, 0,<br />

/* 95 - ??? */ 0, 0, 0, 0, 0, 0,<br />

/* 96 - EXT_KEY */ EXTKEY, EXTKEY, EXTKEY, EXTKEY, EXTKEY, EXTKEY,<br />

/* 97 - ??? */ 0, 0, 0, 0, 0, 0,<br />

/* 98 - ??? */ 0, 0, 0, 0, 0, 0,<br />

/* 99 - ??? */ 0, 0, 0, 0, 0, 0,<br />

/*100 - ??? */ 0, 0, 0, 0, 0, 0,<br />

/*101 - ??? */ 0, 0, 0, 0, 0, 0,<br />

/*102 - ??? */ 0, 0, 0, 0, 0, 0,<br />

/*103 - ??? */ 0, 0, 0, 0, 0, 0,<br />

/*104 - ??? */ 0, 0, 0, 0, 0, 0,<br />

/*105 - ??? */ 0, 0, 0, 0, 0, 0,<br />

/*106 - ??? */ 0, 0, 0, 0, 0, 0,<br />

/*107 - ??? */ 0, 0, 0, 0, 0, 0,<br />

/*108 - ??? */ 0, 0, 0, 0, 0, 0,<br />

/*109 - ??? */ 0, 0, 0, 0, 0, 0,<br />

/*110 - ??? */ 0, 0, 0, 0, 0, 0,<br />

/*111 - ??? */ 0, 0, 0, 0, 0, 0,<br />

/*112 - ??? */ 0, 0, 0, 0, 0, 0,<br />

/*113 - ??? */ 0, 0, 0, 0, 0, 0,<br />

/*114 - ??? */ 0, 0, 0, 0, 0, 0,<br />

/*115 - ??? */ 0, 0, 0, 0, 0, 0,<br />

/*116 - ??? */ 0, 0, 0, 0, 0, 0,<br />

/*117 - ??? */ 0, 0, 0, 0, 0, 0,<br />

/*118 - ??? */ 0, 0, 0, 0, 0, 0,<br />

/*119 - ??? */ 0, 0, 0, 0, 0, 0,<br />

/*120 - ??? */ 0, 0, 0, 0, 0, 0,<br />

/*121 - ??? */ 0, 0, 0, 0, 0, 0,<br />

/*122 - ??? */ 0, 0, 0, 0, 0, 0,<br />

/*123 - ??? */ 0, 0, 0, 0, 0, 0,<br />

/*124 - ??? */ 0, 0, 0, 0, 0, 0,<br />

/*125 - ??? */ 0, 0, 0, 0, 0, 0,<br />

/*126 - ??? */ 0, 0, 0, 0, 0, 0,<br />

/*127 - ??? */ 0, 0, 0, 0, 0, 0<br />

};<br />

Dopo la modifica, si avvia la compilazione.<br />

# cd /usr/src/kernel/keymaps/ [ Invio ]


76 volume VIII Argomenti avanzati e accessori<br />

# make [ Invio ]<br />

Vengono generati tutti i file di configurazione che non siano già presenti (se si vuole ripetere la<br />

compilazione occorre prima rimuovere il file ‘italian.map’).<br />

# cp italian.map /etc/keymap [ Invio ]<br />

Al prossimo riavvio sarà utilizzata la nuova mappa.<br />

356.10 Altri programmi<br />

Il s<strong>of</strong>tware a disposizione per Minix non è molto e può essere trovato negli stessi FTP da cui<br />

si accede ai file del sistema operativo citati qui. In particolare, tra i programmi riferiti alla rete,<br />

vale la pena di ricordare i pacchetti ‘HTTPD.TAZ’ e ‘FROG.TAZ’. Il primo è un servente HTTP<br />

molto semplice e il secondo è un programma per il tracciamento dell’instradamento (simile a<br />

Traceroute). Sono entrambi molto utili e compilabili facilmente.<br />

356.10.1 HTTPD.TAZ<br />

Minix dispone di un servente HTTP elementare e lo si trova distribuito nel pacchetto<br />

‘HTTPD.TAZ’. I pacchetti supplementari, come questo, vanno installati a partire dalla directory<br />

‘/usr/local/’ e i sorgenti vanno collocati in ‘/usr/local/src/’.<br />

# cd /usr/local/src [ Invio ]<br />

Supponendo che il file ‘HTTPD.TAZ’ si trovi nel dischetto, montato nella directory ‘/mnt/’, si<br />

può agire come segue:<br />

# cat /mnt/HTTPD.TAZ | compress -d | tar xvf - [ Invio ]<br />

Si ottiene la creazione della directory ‘httpd/’ a partire dalla posizione corrente, cioè ‘/usr/<br />

local/src/’.<br />

# cd httpd [ Invio ]<br />

Si procede con la compilazione.<br />

# make [ Invio ]<br />

Quindi si installa il programma.<br />

# make install [ Invio ]<br />

L’installazione non si occupa di copiare i file di configurazione: bisogna farlo manualmente.<br />

# cp httpd.conf /etc [ Invio ]<br />

# cp httpd.mtype /etc [ Invio ]<br />

Il demone ‘httpd’, installato in ‘/usr/local/bin/’, ha bisogno di un utente ‘www’, in base<br />

alla configurazione predefinita del file ‘/etc/httpd.conf’ che non serve modificare.<br />

# adduser www operator /usr/home/www [ Invio ]<br />

Naturalmente la scelta della directory personale di questo utente fittizio è solo un fatto di gusto<br />

personale. Sempre in base alla configurazione predefinita, occorre aggiungere alla directory<br />

personale la directory ‘exec/’ e all’interno di questa si devono collocare un paio di file.


Minix 77<br />

# mkdir /usr/home/www/exec [ Invio ]<br />

# cp dir2html /usr/home/www/exec [ Invio ]<br />

# cp dir2html.sh /usr/home/www/exec [ Invio ]<br />

Per ultimo, occorre avviare il demone. Per questo conviene ritoccare il file ‘/etc/rc.net’ in<br />

modo da aggiungere la riga seguente:<br />

tcpd http /usr/local/bin/httpd &<br />

356.10.2 FROG.TAZ<br />

‘frog’ è un programma per il tracciamento dell’instradamento. È semplice, ma anche molto<br />

importante. L’estrazione dell’archivio avviene nel modo solito, così come la compilazione e<br />

l’installazione.<br />

# cd /usr/local/src [ Invio ]<br />

# cat /mnt/FROG.TAZ | compress -d | tar xvf - [ Invio ]<br />

# cd frog [ Invio ]<br />

# make [ Invio ]<br />

# make install [ Invio ]<br />

Tutto qui.<br />

356.11 Copie di sicurezza<br />

Le copie di sicurezza possono essere fatte soltanto utilizzano ‘tar’ e ‘compress’. Dal momento<br />

che il sistema è organizzato in modo piuttosto rigido, con una partizione principale molto piccola<br />

e una partizione ‘/usr/’, normalmente conviene preoccuparsi solo di questa seconda partizione.<br />

Per la prima converrebbe realizzare un dischetto di avvio e installazione con gli stessi file di<br />

configurazione, compresi ‘/etc/passwd’, ‘/etc/group’ e ‘/etc/shadow’.<br />

356.11.1 Archiviazione<br />

Se si dispone di abbastanza spazio libero nella partizione ‘/usr/’, se ne può fare la copia di<br />

sicurezza in un file collocato all’interno della stessa partizione. Successivamente si può scaricare<br />

su dischetti. Si può procedere nel modo seguente:<br />

# cd /usr [ Invio ]<br />

# tar cf - . | compress -c > .BKP.TAZ [ Invio ]<br />

Si ottiene il file ‘/usr/.BKP.TAZ’ contenente la copia di quanto contenuto nella directory corrente<br />

‘/usr/’. Successivamente si può copiare il file ottenuto, a pezzi, su una serie di dischetti<br />

formattati in precedenza. Si comincia con la formattazione e si suppone di disporre di dischetti<br />

da 1440 Kibyte.<br />

# format /dev/fd0 1440 [ Invio ]


78 volume VIII Argomenti avanzati e accessori<br />

...<br />

Una volta preparati i dischetti formattati, si può scaricare il file nei dischetti.<br />

# dd if=/usr/.BKP.TAZ <strong>of</strong>=/dev/fd0 bs=1440k count=1 skip=0 [ Invio ]<br />

# dd if=/usr/.BKP.TAZ <strong>of</strong>=/dev/fd0 bs=1440k count=1 skip=2 [ Invio ]<br />

...<br />

356.11.2 Recupero<br />

Per recuperare un sistema archiviato nel modo mostrato nella sezione precedente, si deve cominciare<br />

dall’installazione con il dischetto iniziale. La cosa migliore sarebbe l’utilizzo di un dischetto<br />

modificato opportunamente in modo che i file di configurazione corrispondano a quanto utilizzato<br />

nel proprio sistema.<br />

Dopo l’installazione iniziale che consiste nel trasferimento di quanto contenuto nel dischetto<br />

iniziale nel disco fisso, si procede con l’installazione della copia (preparata in precedenza) della<br />

partizione collocata a partire da ‘/usr/’.<br />

# setup /usr [ Invio ]<br />

Uno dopo l’altro verranno richiesti tutti i dischetti.<br />

356.12 Convivenza tra Minix e GNU/Linux<br />

Se lo si desidera, si può fare convivere Minix assieme a GNU/Linux, nello stesso disco fisso, su<br />

partizioni distinte. Ma installare Minix in una partizione libera di un disco in cui GNU/Linux è<br />

già stato installato richiede prudenza e attenzione.<br />

• L’installazione di Minix provoca l’alterazione dell’MBR del disco fisso, di conseguenza, al<br />

termine si avvia solo Minix. Quindi, prima di installare Minix occorre preparare uno o più<br />

dischi di avvio di GNU/Linux, in modo da poter in seguito ripristinare il sistema di avvio<br />

attraverso LILO.<br />

• Quando si conclude il lavoro con Minix e si esegue un riavvio con un semplice<br />

[ Ctrl+Alt+Canc ], si ottiene un avvio a caldo (warm boot), ma se dopo si vuole avviare un<br />

kernel Linux, questo non potrà essere caricato. Pertanto, è necessario un riavvio a freddo,<br />

al limite attraverso lo spegnimento e la riaccensione dell’elaboratore.<br />

356.12.1 LILO<br />

Una volta che si è riusciti a fare riavviare il sistema GNU/Linux, con i dischetti di avvio a cui si<br />

faceva riferimento in precedenza, conviene modificare il file ‘/etc/lilo.conf’ in modo che si<br />

possa scegliere tra l’avvio di GNU/Linux, Minix ed eventualmente altro.<br />

Supponendo di avere installato Minix nella seconda partizione del primo disco fisso, le righe<br />

necessarie nel file ‘/etc/lilo.conf’ sono quelle seguenti:<br />

other=/dev/hda2<br />

label=minix<br />

table=/dev/hda


Minix 79<br />

Volendo supporre che Minix sia stato installato nel secondo disco fisso, sempre nella seconda<br />

partizione, le righe sarebbero quelle seguenti:<br />

other=/dev/hdb2<br />

label=minix<br />

table=/dev/hdb<br />

loader=/boot/chain.b<br />

In pratica, è esattamente ciò che si fa quando si vuole controllare l’avvio del Dos.<br />

356.13 Riferimenti<br />

Minix è un sistema operativo molto limitato rispetto a GNU/Linux. Resta comunque l’unica<br />

opportunità, almeno per ora, di fronte a vecchi elaboratori i286 o inferiori.<br />

Molti particolari importanti non sono stati descritti, ma le informazioni relative sono comunque<br />

accessibili dai siti FTP di distribuzione di Minix e dalla documentazione interna costituita delle<br />

pagine di manuale (‘man’).<br />

• Andrew S. Tanenbaum, Alber S. Woodhull, Operating Systems: Design and Implementation,<br />

2/e, Prentice-Hall<br />

• <br />

Appunti di informatica libera 2003.06.29 --- Copyright © 2000-2003 Daniele Giacomini -- daniele @ swlibero.org


ELKS<br />

Capitolo 357<br />

Il progetto ELKS (Embeddable linux kernel subset) vuole creare un sistema operativo per i<br />

microprocessori ix86 di fascia bassa, a partire da un sottoinsieme di funzionalità di GNU/Linux.<br />

È possibile avviare un mini sistema composto da un dischetto di avvio (boot) e un dischetto contenente<br />

un sistema minimo; con un po’ di pazienza è anche possibile installarlo in una partizione<br />

del disco fisso.<br />

È disponibile un compilatore da utilizzare con GNU/Linux, per produrre binari ELKS, cioè<br />

tutto il necessario per sviluppare questo nuovo sistema; è possibile eseguire i binari ELKS su<br />

GNU/Linux, attraverso una libreria di emulazione; è possibile avviare ELKS anche all’interno di<br />

DOSEMU.<br />

357.1 Sperimentare ELKS<br />

ELKS non è un sistema completo, quindi necessita di un pacchetto di sviluppo, composto essenzialmente<br />

da un compilatore, da utilizzare in una piattaforma GNU/Linux normale. Questo<br />

pacchetto è Dev86, distribuito normalmente in forma sorgente.<br />

Una volta scaricato il pacchetto di sviluppo, questo può essere espanso a partire dalla directory<br />

‘/usr/src/’, nell’elaboratore GNU/Linux, come mostrato dall’esempio seguente:<br />

# cd /usr/src [ Invio ]<br />

# tar xzvf Dev86src-0.13.4.tar.gz [ Invio ]<br />

Si otterrà la directory ‘/usr/src/linux-86’ che si articola ulteriormente. Terminata<br />

l’installazione occorre compilare questi sorgenti e installarli.<br />

# cd /usr/src/linux-86 [ Invio ]<br />

# make install [ Invio ]<br />

A questo punto si può pensare ai sorgenti del kernel di ELKS e dei vari programmi di sistema e<br />

di servizio. Anche questi vanno installati a partire da ‘/usr/src/’.<br />

# cd /usr/src [ Invio ]<br />

# tar xzvf elks-0.0.67.tar.gz [ Invio ]<br />

# tar xzvf elkscmd.tar.gz [ Invio ]<br />

Si ottengono le directory ‘/usr/src/elks/’ e ‘/usr/src/elkscmd/’. La prima contiene il<br />

kernel, la seconda i programmi di contorno. Per compilare il kernel basta eseguire i passi seguenti.<br />

# cd /usr/src/elks [ Invio ]<br />

# make config [ Invio ]<br />

# make dep ; make clean [ Invio ]<br />

# make [ Invio ]<br />

80


ELKS 81<br />

Si ottiene il file ‘/usr/src/elks/Image’ che può essere trasferito nel modo solito in un<br />

dischetto, attraverso ‘cp’ o ‘dd’.<br />

Per compilare gli altri programmi occorre passare le varie directory in cui si articola ‘/usr/src/<br />

elkscmd/’ e usare il comando ‘make’.<br />

357.2 Immagini di dischetti già pronti<br />

La realizzazione di un sistema ELKS è un po’ difficoltosa in questa fase iniziale del suo progetto<br />

di realizzazione. La cosa migliore è partire dalle immagini già pronte, contenute normalmente in<br />

un pacchetto unico. Per trasferirle nei dischetti ci si comporta nel modo solito, esattamente come<br />

si fa per le immagini di dischetti di GNU/Linux.<br />

Volendo, l’immagine ‘boot’ (quella di avvio) può essere sostituita semplicemente con un kernel<br />

compilato personalmente, mentre l’immagine ‘root’ può essere rielaborata aggiungendo o<br />

sostituendo altri programmi. L’immagine ‘root’ contiene un file system Minix.<br />

Per mettere in funzione il sistema ELKS è sufficiente avviare l’elaboratore con il dischetto ottenuto<br />

dall’immagine ‘boot’, sostituendolo con quello dell’immagine ‘root’, quando il kernel lo<br />

richiede.<br />

357.3 Avvio di ELKS all’interno di DOSEMU<br />

ELKS può essere avviato all’interno di DOSEMU, sia in una console che in una finestra di X.<br />

Per farlo basta avviare l’emulatore in modo che esegua il caricamento dal dischetto. Questo si<br />

ottiene di solito utilizzando l’opzione ‘-A’.<br />

# dos -A [ Invio ]<br />

La figura 357.1 mostra la fase finale dell’avvio di ELKS. Si nota in particolare l’invito della<br />

shell (il prompt), che è molto scarno: per ora non si possono usare i caratteri jolly e tutte le altre<br />

funzioni cui si è abituati con le shell normali.<br />

Figura 357.1. L’avvio di ELKS.


82 volume VIII Argomenti avanzati e accessori<br />

357.4 Riferimenti<br />

• ELKS: the embeddable Linux kernel system<br />

<br />

• Source Forge: ELKS<br />

<br />

Appunti di informatica libera 2003.06.29 --- Copyright © 2000-2003 Daniele Giacomini -- daniele @ swlibero.org


Parte lxxii<br />

Dos<br />

358 Dos: introduzione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86<br />

358.1 Avvio del sistema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86<br />

358.2 Dispositivi secondo il Dos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87<br />

358.3 Directory, file ed eseguibili . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87<br />

358.4 Comandi e ambiente di avvio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .89<br />

358.5 Caratteri jolly . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90<br />

358.6 Invito dell’interprete dei comandi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91<br />

358.7 Comandi interni principali . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92<br />

358.8 Flussi standard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .97<br />

358.9 Accesso diretto ai dispositivi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99<br />

358.10 Riferimenti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99<br />

359 Dos: dischi, file system, directory e file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100<br />

359.1 Suddivisione in partizioni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100<br />

359.2 Inizializzazione di un’unità di memorizzazione . . . . . . . . . . . . . . . . . . . . . . . . . . . 100<br />

359.3 Etichetta di un’unità di memorizzazione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101<br />

359.4 Analisi e correzione del file system . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101<br />

359.5 Copia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102<br />

359.6 Trasferimento del sistema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103<br />

359.7 Modifica delle unità . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103<br />

359.8 Altre particolarità . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105<br />

360 Dos: configurazione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108<br />

360.1 CONFIG.SYS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108<br />

360.2 AUTOEXEC.BAT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112<br />

360.3 Comandi ridondanti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112<br />

360.4 Localizzazione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113<br />

360.5 Orologio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114<br />

361 Dos: script dell’interprete dei comandi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115<br />

361.1 Parametri, variabili ed espansione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115<br />

361.2 Chiamate di altri script . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115<br />

361.3 Strutture di controllo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115<br />

361.4 Comandi utili negli script . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118<br />

362 Dos: gestione della memoria centrale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .121<br />

83


362.1 Gestione particolare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121<br />

362.2 Comandi appositi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121<br />

362.3 Verifica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121<br />

363 FreeDOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123<br />

363.1 Installazione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .123<br />

363.2 Impostazione e configurazione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124<br />

363.3 RxDOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125<br />

363.4 Riferimenti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125<br />

364 Progetto GNUish . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .126<br />

364.1 Programmi di servizio vari . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126<br />

364.2 Gnuplot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126<br />

364.3 Spreadsheet Calculator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127<br />

364.4 Ispell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127<br />

364.5 Perl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .127<br />

364.6 Riferimenti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127<br />

365 The valuable DOS Freeware page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128<br />

365.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128<br />

365.2 OS and GUI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129<br />

365.3 Utility . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129<br />

365.4 Network . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130<br />

365.5 Compilers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132<br />

365.6 Typesetting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132<br />

365.7 More Dos s<strong>of</strong>tware sources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133<br />

365.8 Search engines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133<br />

366 Clean the Clipper 5.2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134<br />

366.1 Step 1: try to compile with the /P parameter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135<br />

366.2 Step 2: understand well the use <strong>of</strong> code blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136<br />

366.3 Step 3: understand the object programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137<br />

366.4 Step 4: understand the get object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138<br />

366.5 Step 5: trying to stop using commands . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140<br />

366.6 Step 6: free yourself from STD.CH - /U . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153<br />

366.7 Step 7: take control over all include files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153<br />

367 nanoBase 1997 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160<br />

367.1 What is it . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160<br />

367.2 The dot command line . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160<br />

367.3 The menu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161<br />

367.4 The macro recording, compiling and execution . . . . . . . . . . . . . . . . . . . . . . . . . . . .161<br />

84


367.5 The report system . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162<br />

367.6 The integrated text editor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163<br />

367.7 The internal documentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164<br />

367.8 Download it . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164<br />

367.9 Bugs and known problems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165<br />

368 nanoBase 1997 user manual . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166<br />

368.1 Dos xBase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166<br />

368.2 Composition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171<br />

368.3 How to use nB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173<br />

368.4 Status line . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174<br />

368.5 The dot line . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175<br />

368.6 The menu system . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175<br />

368.7 The text editor DOC() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187<br />

368.8 The help text file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187<br />

368.9 Macro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188<br />

368.10 Data types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192<br />

368.11 Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200<br />

368.12 Delimiters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201<br />

368.13 Code blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202<br />

368.14 Standard functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202<br />

368.15 nB functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261<br />

368.16 Normal command substitution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299<br />

368.17 nB command substitution functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305<br />

368.18 RPT: the nB print function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 310<br />

368.19 How can I... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313<br />

368.20 The source files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314<br />

85


Dos: introduzione<br />

Capitolo 358<br />

DOS è acronimo di Disk Operating System e sta a indicare il nome di un sistema operativo per<br />

micro elaboratori basati su microprocessori i86, successore del vecchio CP/M. Probabilmente,<br />

data la sua estrema limitatezza, è un po’ azzardato voler parlare di «sistema operativo», tanto che<br />

qualcuno lo appella: «gestore di interruzioni» (interrupt).<br />

Questo sistema operativo nasce come s<strong>of</strong>tware proprietario; tuttavia, attualmente il progetto più<br />

attivo attorno a questo tipo di sistema è FreeDOS, il cui scopo è quello di realizzarne un’edizione<br />

libera e completa.<br />

358.1 Avvio del sistema<br />

Un sistema Dos è composto essenzialmente da un kernel, un interprete dei comandi e da una<br />

serie di programmi di servizio. Questo concetto è analogo ai sistemi Unix, con la differenza che<br />

il kernel <strong>of</strong>fre funzionalità molto scarse e solo per mezzo di interruzioni s<strong>of</strong>tware (IRQ).<br />

Nelle versioni proprietarie del Dos, il kernel era suddiviso in due file, che raccoglievano funzionalità<br />

distinte in base all’importanza relativa. I nomi usati sono stati differenti e nel caso di<br />

FreeDOS il kernel è contenuto tutto in un solo file (tabella 358.1).<br />

Tabella 358.1. Comparazione tra i nomi dei file che compongono il kernel di un<br />

sistema Dos.<br />

Micros<strong>of</strong>t IBM Novell, Caldera RxDOS FreeDOS<br />

IO.SYS<br />

MSDOS.SYS<br />

IBMBIO.COM<br />

IBMDOS.COM<br />

IBMBIO.COM<br />

IBMDOS.COM<br />

RXDO-<br />

SBIO.SYS<br />

RXDOS.SYS<br />

KERNEL.SYS<br />

--<br />

I file del kernel devono trovarsi nella directory radice della partizione o del dischetto per poter<br />

essere avviati. Per la precisione, l’avvio del kernel viene gestito direttamente dal codice inserito<br />

nel settore di avvio della partizione o del dischetto (512 Kibyte), che a sua volta viene avviato<br />

dal firmware (il BIOS, secondo la terminologia specifica dell’architettura i86 e successiva).<br />

Il kernel, dopo essere stato avviato, non attiva una procedura di avvio, ma si limita a interpretare<br />

uno script speciale, ‘CONFIG.SYS’, e subito dopo avvia l’interprete dei comandi, ovvero<br />

la shell. Tradizionalmente, il programma in questione è ‘COMMAND.COM’. Secondo la tradizione,<br />

l’interprete dei comandi che viene avviato dal kernel si occupa subito di eseguire lo script<br />

‘AUTOEXEC.BAT’. Gli script ‘CONFIG.SYS’ e ‘AUTOEXEC.BAT’ devono trovarsi nella directory<br />

radice del disco o della partizione da cui si avvia il sistema, ovvero quella in cui si trova già il<br />

kernel che viene avviato.<br />

L’interprete dei comandi, ‘COMMAND.COM’, è in grado di eseguire direttamente alcune funzionalità,<br />

attraverso comandi interni che non si traducono in programmi di servizio veri e propri.<br />

Tradizionalmente ‘COMMAND.COM’ si colloca nella directory radice del disco o della partizione<br />

in cui si trova il kernel stesso. Ciò non è propriamente indispensabile, ma conviene attenersi a<br />

questa linea per evitare fastidi inutili.<br />

86


Dos: introduzione 87<br />

358.2 Dispositivi secondo il Dos<br />

I dispositivi secondo il Dos hanno un nome, composto da lettere e cifre numeriche, terminato da<br />

due punti opzionali:<br />

nome_dispositivo[:]<br />

Il nome in questione può essere indicato utilizzando lettere maiuscole o minuscole, senza che la<br />

cosa faccia differenza. I nomi più comuni sono elencati nella tabella 358.2. È il caso di osservare<br />

che i due punti che concludono il nome, vanno usati necessariamente quando questo viene<br />

abbinato ad altre informazioni da cui non potrebbe essere distinto (per esempio un percorso).<br />

Tabella 358.2. Nomi dei dispositivi più comuni in Dos.<br />

Dispositivo Descrizione<br />

‘A:’ Disco nella prima unità a dischetti.<br />

‘B:’ Disco nella seconda unità a dischetti.<br />

‘C:’ Prima partizione Dos nel primo disco fisso.<br />

‘D:’, ‘E:’,... ‘Z:’ Partizione Dos o altro tipo di disco.<br />

‘CON:’ Console: tastiera e schermo.<br />

‘PRN:’ Porta stampante principale.<br />

‘LPT1:’, ‘LPT2:’,... Porte parallele.<br />

‘COM1:’, ‘COM2:’,... Porte seriali.<br />

Il Dos mantiene distinti i dischi e le partizioni, nel senso che questi non devono creare una<br />

struttura unica come avviene nei sistemi Unix. Pertanto, quando si fa riferimento a un percorso<br />

di un file o di una directory, si deve tenere in considerazione anche il disco o la partizione in cui<br />

si trova.<br />

Il modo utilizzato dal Dos per identificare i dischi e le partizioni, di fatto impedisce di accedere a<br />

questi dispositivi in modo indipendente dal file system sottostante. Per intenderci, l’«unità» ‘X:’<br />

può essere una partizione Dos di un disco non meglio identificato; mentre non esiste un modo<br />

univoco per poter raggiungere il dispositivo fisico in cui si trova questo disco.<br />

358.3 Directory, file ed eseguibili<br />

Il Dos è nato dopo Unix e da questo sistema ha ereditato alcuni concetti elementari (forse troppo<br />

pochi). I percorsi di file e directory si separano con una barra obliqua, che però è inversa rispetto<br />

allo Unix. Anche con il Dos c’è una directory radice; tuttavia si aggiunge l’indicazione dell’unità<br />

di memorizzazione (il disco o la partizione). Si può osservare a questo proposito la figura 358.1.<br />

Figura 358.1. Struttura di un percorso in un file system Dos.<br />

C:\PRIMO\SECONDO\TERZO\QUARTO<br />

^ ^ ^ ^ ^<br />

| | | | |<br />

| | | | file o directory finale<br />

| | | |<br />

| | | directory<br />

| | |<br />

| | separazione tra una directory e la successiva<br />

| |<br />

| directory radice<br />

|<br />

unità di memorizzazione


88 volume VIII Argomenti avanzati e accessori<br />

I nomi di file e directory possono essere indicati utilizzando lettere maiuscole o minuscole, senza<br />

che la cosa possa fare differenza. Questi nomi possono essere composti utilizzando anche cifre<br />

numeriche e altri simboli (che comunque è bene usare con parsimonia).<br />

Per la precisione, sono esclusi i simboli: ‘/’, ‘\’, ‘[’, ‘]’, ‘’, ‘+’, ‘=’, ‘;’, ‘:’, ‘,’, ‘?’, ‘*’,<br />

‘{’, ‘}’ e il punto che va usato esattamente come descritto nel seguito.<br />

Tradizionalmente, il Dos utilizza un tipo di file system elementare, denominato FAT (Dos-FAT),<br />

in cui i nomi dei file e delle directory possono essere composti utilizzando al massimo 11 caratteri,<br />

di cui otto compongono un prefisso e tre un suffisso. Il prefisso e il suffisso di questi nomi<br />

appaiono uniti attraverso un punto. Per esempio: ‘CIAO.COM’, ‘LETTERA.TXT’, ‘PIPPO.NB’,...<br />

Questa conformazione dei nomi è una caratteristica fondamentale del Dos, da cui derivano una<br />

serie di consuetudini e di limitazioni molto importanti.<br />

È importante osservare che non è opportuno che i nomi dei file coincidano con quelli dei dispositivi<br />

(senza i due punti finali). In pratica, non conviene creare file del tipo ‘CON:’, ‘PRN:’,<br />

ecc. Tutto dipende dal contesto, ma in generale è bene fare attenzione a questo particolare.<br />

Come nei sistemi Unix il Dos annovera il concetto di directory corrente, a cui si aggiunge il<br />

concetto di unità di memorizzazione corrente. Infatti, la directory va collocata in un disco o<br />

in una partizione. In base a questo principio, si possono indicare dei percorsi relativi, che fanno<br />

riferimento alla posizione corrente (nell’unità di memorizzazione corrente). Tuttavia, in più,<br />

ogni unità di memorizzazione ha una sua directory corrente. Per esempio, fare riferimento a un<br />

file in una certa unità di memorizzazione ‘x:’, senza specificare il percorso, significa indicare<br />

implicitamente la directory corrente di quella unità.<br />

Per esempio, supponendo che la directory corrente dell’unità ‘X:’ sia ‘X:\PRIMO\SECONDO\’,<br />

facendo riferimento al file ‘X:CIAO’, si intende indicare implicitamente il file ‘X:\PRIMO\<br />

SECONDO\CIAO’.<br />

In un percorso si possono usare anche i simboli ‘.’ e ‘..’, con lo stesso significato che hanno in<br />

un sistema Unix: la directory stessa e la directory genitrice.<br />

Il file system tradizionale del Dos consente di annotare solo poche informazioni per i file e le<br />

directory: la data di modifica e quattro indicatori booleani, rappresentati da altrettante lettere:<br />

• H file o directory nascosti;<br />

• S file o directory di sistema;<br />

• R file o directory in sola lettura e non cancellabile;<br />

• A file o directory da archiviare (i dati sono stati modificati).<br />

Si tratta di attributi completamente differenti da quelli di Unix. Si può osservare in particolare la<br />

mancanza di un attributo che specifichi la possibilità di eseguire un programma o di attraversare<br />

una directory. Secondo la tradizione Dos, gli attributi vanno considerati nel modo seguente:<br />

• A viene attivato ogni volta che il file viene scritto o modificato e serve per automatizzare<br />

i sistemi di copia periodica;<br />

• R se attivo, il Dos non consente la scrittura o la rimozione;


Dos: introduzione 89<br />

• S se attivo si tratta di un file di «sistema», ma in pratica si comporta come l’attributo H;<br />

• H se attivo si tratta di un file «nascosto», che così non dovrebbe apparire nelle liste di file<br />

e directory.<br />

In generale, file e directory nascosti o di sistema non dovrebbero essere spostati fisicamente,<br />

nemmeno nell’ambito della stessa unità di memorizzazione. Questa esigenza nasce in particolare<br />

per i file del kernel, che non possono essere spostati se si vuole poter riavviare il sistema<br />

operativo.<br />

Dal momento che il file system non permette di determinare se un file è un eseguibile, l’unico<br />

modo per permettere al sistema di conoscere questa caratteristica sta nell’uso di suffissi convenzionali<br />

nei nomi: i file che terminano con l’estensione ‘.COM’ e ‘.EXE’ sono programmi binari<br />

(la differenza tra i due tipi di estensione riguarda il formato del binario); quelli che terminano per<br />

‘.BAT’ sono script dell’interprete dei comandi (‘COMMAND.COM’).<br />

La prima stranezza che deriva da questa caratteristica del Dos sta nel fatto che per avviare un<br />

eseguibile di questi, è sufficiente indicare il nome del file senza l’estensione, che diventa così<br />

un componente opzionale agli occhi dell’utilizzatore.<br />

358.4 Comandi e ambiente di avvio<br />

L’interprete dei comandi tradizionale dei sistemi Dos è il programma ‘COMMAND.COM’, che viene<br />

avviato direttamente dal kernel. ‘COMMAND.COM’ può essere avviato più volte successive, anche<br />

se di solito ciò è di scarsa utilità, dal momento che il Dos non è un sistema operativo in multiprogrammazione.<br />

In ogni caso, quando viene avviato dal kernel, si occupa di interpretare ed eseguire<br />

lo script ‘AUTOEXEC.BAT’ che si trova nella directory radice dell’unità di avvio.<br />

‘COMMAND.COM’ mostra un invito simile idealmente a quello delle shell Unix, dopo il quale possono<br />

essere inseriti i comandi. A loro volta, questi possono essere riferiti a comandi interni<br />

corrispondenti a funzionalità <strong>of</strong>ferte direttamente dall’interprete, oppure possono rappresentare<br />

la richiesta di avvio di un programma esterno.<br />

Figura 358.2. Riga di comando.<br />

C:\>DIR A: /W<br />

^ ^ ^ ^<br />

| | | |<br />

| | | opzione<br />

| | |<br />

| | argomento<br />

| |<br />

| comando<br />

|<br />

invito<br />

Il Dos ha ereditato da Unix anche il concetto di variabile di ambiente. Il meccanismo è lo stesso<br />

ed è fondamentale la variabile di ambiente ‘PATH’, con la quale si possono indicare i percorsi di<br />

ricerca degli eseguibili. Tuttavia, il Dos ha delle caratteristiche speciali, per cui, è il caso di fare<br />

alcuni esempi di comandi:<br />

• C:\>C:\PRIMO\SECONDO.EXE<br />

questo comando avvia l’esecuzione del file ‘C:\PRIMO\SECONDO.EXE’;


90 volume VIII Argomenti avanzati e accessori<br />

• C:\>C:\PRIMO\SECONDO<br />

questo comando potrebbe avviare l’esecuzione del primo dei file seguenti che riesce a<br />

trovare;<br />

– ‘C:\PRIMO\SECONDO.COM’<br />

– ‘C:\PRIMO\SECONDO.EXE’<br />

– ‘C:\PRIMO\SECONDO.BAT’<br />

• C:\>SECONDO<br />

questo comando potrebbe avviare l’esecuzione del primo dei file seguenti che dovesse riuscire<br />

a trovare, ma in mancanza può continuare la ricerca nei percorsi indicati nella variabile<br />

di ambiente ‘PATH’.<br />

– ‘C:.\SECONDO.COM’<br />

– ‘C:.\SECONDO.EXE’<br />

– ‘C:.\SECONDO.BAT’<br />

I percorsi indicati nella variabile di ambiente ‘PATH’ sono separati da un punto e virgola; per<br />

esempio:<br />

C:\;C:\DOS;C:\FDOS\BIN<br />

Di solito, il Dos dà per scontato che si cerchino gli eseguibili a cominciare dalla directory corrente.<br />

Per questo, occorre considerare che è sempre come se la variabile di ambiente ‘PATH’<br />

contenesse questa indicazione prima delle altre: ‘.;C:\;C:\DOS;C:\FDOS\BIN’. È da osservare<br />

che FreeDOS si comporta in maniera differente, in quanto richiede espressamente<br />

questa indicazione della directory corrente.<br />

358.5 Caratteri jolly<br />

Il Dos imita l’utilizzo dei caratteri jolly come avviene nei sistemi Unix per opera delle shell.<br />

Tuttavia, nel Dos non si tratta di un’espansione che avviene per opera della shell, ma vi deve<br />

provvedere ogni programma per conto proprio. Questo rappresenta una gravissima deficienza del<br />

Dos, che però è irrimediabile.<br />

Su questa base, i comandi tendono a richiedere l’indicazione di un argomento che rappresenta il<br />

nome di uno o più file prima delle opzioni eventuali.<br />

Ma c’è un altro problema. Il punto che divide in due i nomi dei file e delle directory è un muro<br />

insuperabile per i caratteri jolly.<br />

I simboli che si possono utilizzare sono solo l’asterisco e il punto interrogativo. L’asterisco vale<br />

per una sequenza qualunque di caratteri, escluso il punto; il punto interrogativo vale per un<br />

carattere qualunque. 1<br />

1 Ci sono programmi di origine Unix, portati in Dos, che non hanno questa limitazione riferita al punto che separa<br />

l’estensione.


Dos: introduzione 91<br />

Modello<br />

Tabella 358.3. Alcuni esempi.<br />

Corrispondenza<br />

*.* Corrisponde a un nome qualunque.<br />

*.COM Un nome che termina con l’estensione ‘.COM’.<br />

CIAO.X?X<br />

Tutti i nomi che iniziano per ‘CIAO’ e hanno un’estensione composta da un lettera<br />

«X» iniziale e finale, senza specificare cosa ci sia al secondo posto.<br />

* Tutti i nomi che non hanno estensione (che non contengono il punto).<br />

358.6 Invito dell’interprete dei comandi<br />

Esiste un’altra variabile di ambiente fondamentale per il Dos. Si tratta di ‘PROMPT’, che consente<br />

di modificare l’aspetto dell’invito dell’interprete dei comandi. La cosa funziona un po’ come nelle<br />

shell Unix, per cui si assegna una stringa che può contenere dei simboli speciali, praticamente<br />

delle sequenze di escape che vengono espanse prima della visualizzazione. La tabella 358.4 riepiloga<br />

questi simboli particolari. In origine, il Dos mostrava in modo predefinito un invito simile<br />

all’esempio seguente, in cui appare solo l’unità di memorizzazione corrente:<br />

C:><br />

Questo tipo di impostazione corrisponderebbe alla stringa ‘$N$G’. In seguito, si è passati a<br />

un invito simile al prossimo esempio, in cui si aggiunge anche l’informazione della directory<br />

corrente:<br />

C:\BIN\><br />

Questo corrisponde alla stringa ‘$P$G’.<br />

Tabella 358.4. Sequenze di escape per definire dei componenti speciali all’interno di<br />

una stringa di invito.<br />

Simbolo<br />

Corrispondenza<br />

$Q =<br />

$$ $<br />

$T Ora corrente.<br />

$D Data corrente.<br />

$V Numero della versione.<br />

$N Lettera dell’unità corrente.<br />

$G ><br />

$L <<br />

$B |<br />

$H (cancella il carattere precedente)<br />

$E (1B16)<br />

$_ Codice di interruzione di riga.<br />

Cancellando il contenuto della variabile di ambiente ‘PROMPT’ si ripristina la stringa di invito<br />

predefinita.


92 volume VIII Argomenti avanzati e accessori<br />

358.7 Comandi interni principali<br />

I comandi interni sono quelli che non corrispondono a programmi di servizio veri e propri,<br />

ma sono funzionalità svolte direttamente dall’interprete dei comandi. Nelle sezioni seguenti ne<br />

vengono descritti brevemente alcuni.<br />

358.7.1 CH, CHDIR<br />

CH [percorso]<br />

CHDIR [percorso]<br />

‘CH’, o ‘CHDIR’, è un comando interno dell’interprete dei comandi, che consente di visualizzare<br />

o di cambiare la directory corrente. È indifferente l’uso di ‘CD’ o di ‘CHDIR’; se il comando non<br />

è seguito dal percorso, si ottiene solo la visualizzazione della directory corrente. Si osservi che<br />

se si indica un percorso assoluto di unità di memorizzazione, se questa non corrisponde a quella<br />

attuale, si cambia la directory corrente di quella unità.<br />

Segue la descrizione di alcuni esempi.<br />

• C:\>CD<br />

Visualizza la directory corrente.<br />

• C:\>CD \TMP\LAVORO<br />

Sposta la directory corrente in ‘\TMP\LAVORO\’.<br />

• C:\TMP\LAVORO>CD DATI\LETTERE<br />

Sposta la directory corrente in ‘DATI\LETTERE\’ che a sua volta discende dalla posizione<br />

iniziale precedente.<br />

• C:\TMP\LAVORO\DATI\LETTERE>CD ..<br />

Sposta la directory corrente nella posizione della directory genitrice di quella iniziale.<br />

• C:\TMP\LAVORO\DATI>CD F:\TMP<br />

Cambia la directory corrente dell’unità ‘F:’, senza intervenire nell’unità corrente.<br />

358.7.2 X:<br />

{A|B| ...|Z}:<br />

Il Dos gestisce le unità di memorizzazione in modo speciale. Per cambiare l’unità di memorizzazione<br />

corrente, non esiste un comando analogo a ‘CD’: si deve indicare il nome dell’unità a cui si<br />

vuole accedere.<br />

Segue la descrizione di alcuni esempi.<br />

• C:\>A:<br />

Cambia l’unità di memorizzazione attuale, facendola diventare ‘A:’.


Dos: introduzione 93<br />

• A:\>F:<br />

Cambia l’unità di memorizzazione attuale, facendola diventare ‘F:’.<br />

358.7.3 MD, MKDIR<br />

MD directory<br />

MKDIR directory<br />

‘MD’, o ‘MKDIR’, è un comando interno dell’interprete dei comandi, che consente di creare una<br />

directory vuota.<br />

Segue la descrizione di alcuni esempi.<br />

• C:\>MD LAVORO<br />

Crea la directory ‘LAVORO\’ a partire da quella corrente.<br />

• C:\>MD \TMP\DATA<br />

Crea la directory ‘\TMP\DATA\’ nell’unità corrente.<br />

• C:\>MD F:\TMP\DATA<br />

Crea la directory ‘\TMP\DATA\’ nell’unità ‘F:’.<br />

358.7.4 RD, RMDIR<br />

RM directory<br />

RMDIR directory<br />

‘RD’, o ‘RMDIR’, è un comando interno dell’interprete dei comandi, che consente di cancellare<br />

una directory vuota.<br />

Segue la descrizione di alcuni esempi.<br />

• C:\>RD LAVORO<br />

Cancella la directory ‘LAVORO\’ a partire da quella corrente.<br />

• C:\>RD \TMP\DATA<br />

Cancella la directory ‘\TMP\DATA\’ nell’unità corrente.<br />

• C:\>RD F:\TMP\DATA<br />

Cancella la directory ‘\TMP\DATA\’ nell’unità ‘F:’.


94 volume VIII Argomenti avanzati e accessori<br />

358.7.5 DIR<br />

DIR [directory|file] [/P] [/W]<br />

‘DIR’ è un comando interno dell’interprete dei comandi, che consente di visualizzare l’elenco del<br />

contenuto di una directory o l’elenco di un gruppo di file. L’argomento del comando può essere<br />

composto utilizzando caratteri jolly, secondo lo standard del Dos, ovvero i simboli ‘*’ e ‘?’.<br />

Tabella 358.5. Alcune opzioni.<br />

Opzione Descrizione<br />

Blocca lo scorrimento dell’elenco in attesa della pressione di<br />

/P<br />

un tasto quando questo è più lungo del numero di righe che<br />

possono apparire sullo schermo.<br />

Visualizza solo i nomi dei file e delle directory, senza altre<br />

/W<br />

informazioni, permettendo così di vedere più nomi assieme in<br />

un’unica schermata.<br />

Segue la descrizione di alcuni esempi.<br />

• C:\>DIR *.*<br />

Visualizza l’elenco di tutti i file contenuti nella directory corrente.<br />

• C:\>DIR ESEMPIO.*<br />

Visualizza l’elenco di tutti i file il cui nome inizia per ‘ESEMPIO’ e continua con<br />

un’estensione qualunque.<br />

• C:\>DIR *.DOC<br />

Visualizza l’elenco di tutti i file il cui nome termina con l’estensione ‘.DOC’.<br />

• C:\>DIR F:\DOC\*.*<br />

Visualizza l’elenco di tutti i file contenuti nella directory ‘\DOC\’ dell’unità ‘F:’.<br />

• C:\>DIR F:<br />

Visualizza l’elenco di tutti i file contenuti nella directory corrente dell’unità ‘F:’.<br />

358.7.6 COPY<br />

COPY file_origine [file_destinazione] [opzioni]<br />

COPY file_1 + file_2 [+ ...] [file_destinazione] [opzioni]<br />

‘COPY’ è un comando interno dell’interprete dei comandi, che consente di copiare uno o più file<br />

(sono escluse le directory). Anche qui è consentito l’uso di caratteri jolly, ma al contrario dei<br />

sistemi Unix, i caratteri jolly possono essere usati anche nella destinazione. Il ‘COPY’ del Dos<br />

consente anche di unire assieme più file.


Dos: introduzione 95<br />

Tabella 358.6. Alcune opzioni.<br />

Opzione Descrizione<br />

/V Fa in modo che venga verificato il risultato della copia.<br />

a sì che la copia avvenga in modo «binario». Questa opzione<br />

può servire quando si copia un file su un dispositivo e si<br />

/B<br />

vuole evitare che alcuni codici vengano interpretati in modo<br />

speciale.<br />

Non chiede conferma prima di sovrascrivere i file, se questi<br />

/Y<br />

esistono già nella destinazione.<br />

Segue la descrizione di alcuni esempi.<br />

• C:\>COPY ESEMPIO PROVA<br />

Copia il file ‘ESEMPIO’ nella directory corrente ottenendo il file ‘PROVA’, sempre nella<br />

directory corrente.<br />

• C:\>COPY C:\DOS\*.* C:\TMP<br />

Copia tutto il contenuto della directory ‘\DOS\’ dell’unità ‘C:’ nella directory ‘\TMP\’<br />

nella stessa unità ‘C:’, mantenendo gli stessi nomi.<br />

• C:\>COPY TESTA+CORPO+CODA LETTERA<br />

Copia, unendoli, i file ‘TESTA’, ‘CORPO’ e ‘CODA’, ottenendo il file ‘LETTERA’.<br />

• C:\>COPY *.DOC *.TXT<br />

Copia tutti i file che nella directory corrente hanno un nome che termina con l’estensione<br />

‘.DOC’, generando altrettanti file, con lo stesso prefisso, ma con l’estensione ‘.TXT’.<br />

• C:\>COPY PROVA.PRN PRN: /B<br />

Copia il file ‘PROVA.PRN’ nel dispositivo ‘PRN:’, ovvero sulla stampante, assicurandosi<br />

che la copia avvenga senza alterare alcunché.<br />

358.7.7 DEL, ERASE<br />

DEL file<br />

ERASE file<br />

‘DEL’, o ‘ERASE’, è un comando interno dell’interprete dei comandi, che consente di cancellare<br />

uno o più file (sono escluse le directory). È da considerare che i file che hanno l’attributo di sola<br />

lettura attivo, non possono essere modificati e nemmeno cancellati.<br />

Segue la descrizione di alcuni esempi.<br />

• C:\TMP>DEL *.*<br />

Cancella tutti i file nella directory corrente.<br />

• C:\TMP>DEL ESEMPIO.*<br />

Cancella tutti i file contenuti nella directory corrente, il cui nome inizia per ‘ESEMPIO’ e<br />

termina con qualunque estensione.


96 volume VIII Argomenti avanzati e accessori<br />

• C:\TMP>DEL *.BAK<br />

Cancella tutti i file contenuti nella directory corrente, il cui nome termina con l’estensione<br />

‘.BAK’.<br />

358.7.8 REN, RENAME<br />

REN file_origine nome_nuovo<br />

RENAME file_origine nome_nuovo<br />

‘REN’, o ‘RENAME’, è un comando interno dell’interprete dei comandi, che consente di cambiare<br />

il nome di uno o più file (sono escluse le directory). Il primo argomento può essere un percorso<br />

relativo o assoluto, completo anche dell’indicazione dell’unità, mentre il secondo argomento è il<br />

nuovo nome, che implicitamente non può essere collocato altrove.<br />

Segue la descrizione di alcuni esempi.<br />

• C:\>REN ESEMPIO PROVA<br />

Cambia il nome del file ‘ESEMPIO’, che si trova nella directory corrente, in ‘PROVA’.<br />

• C:\>REN *.TXT *.DOC<br />

Cambia il nome di tutti i file che, nella directory corrente, hanno l’estensione ‘.TXT’,<br />

trasformandoli in modo tale da avere un’estensione ‘.DOC’.<br />

358.7.9 SET<br />

SET [variabile_di_ambiente=stringa]<br />

‘SET’ è un comando interno dell’interprete dei comandi che ha lo scopo di assegnare un valore<br />

a una variabile di ambiente, oppure di leggere lo stato di tutte le variabili di ambiente esistenti.<br />

Quando si assegna un valore a una variabile, questa viene creata simultaneamente; quando non<br />

si assegna nulla a una variabile, la si elimina.<br />

Segue la descrizione di alcuni esempi.<br />

• C:\>SET<br />

Elenca le variabili di ambiente esistenti assieme al loro valore.<br />

• C:\>SET PROMPT=$P$G$G<br />

Assegna alla variabile di ambiente ‘PROMPT’ la stringa ‘$P$G$G’. Questo si traduce nella<br />

modifica dell’aspetto dell’invito dell’interprete dei comandi.<br />

• C:\>SET PATH=.;C:\BIN;D:\BIN<br />

Assegna alla variabile di ambiente ‘PATH’ la stringa ‘.;C:\BIN;D:\BIN’.<br />

• C:\>SET PROMPT=<br />

Elimina la variabile di ambiente ‘PROMPT’, assegnandole la stringa nulla.


Dos: introduzione 97<br />

358.7.10 TYPE<br />

TYPE file<br />

‘TYPE’ è un comando interno dell’interprete dei comandi, che consente di leggere ed emettere<br />

il contenuto di un file attraverso lo standard output. Questo si traduce in pratica nella<br />

visualizzazione del file in questione.<br />

Segue la descrizione di alcuni esempi.<br />

• C:\>TYPE LETTERA<br />

Emette il contenuto del file ‘LETTERA’ che si trova nella directory e nell’unità corrente.<br />

• C:\>TYPE C:\DOC\MANUALE<br />

Emette il contenuto del file ‘MANUALE’ che si trova nella directory ‘\DOC\’ dell’unità ‘C:’.<br />

358.8 Flussi standard<br />

Il Dos ha ereditato da Unix anche i concetti legati ai flussi standard. In pratica, i programmi<br />

hanno a disposizione tre flussi predefiniti: uno in lettura rappresentato dallo standard input, due<br />

in scrittura rappresentati dallo standard output e dallo standard error. Il meccanismo è lo stesso di<br />

Unix, anche se non funziona altrettanto bene; infatti, non è possibile ridirigere lo standard error<br />

attraverso l’interprete dei comandi.<br />

Secondo la tradizione delle shell Unix, la ridirezione dello standard output si ottiene con il simbolo<br />

‘>’ posto alla fine del comando interessato, seguito poi dal nome del file che si vuole generare<br />

in questo modo. Per esempio,<br />

C:\>TYPE LETTERA > PRN:<br />

invece di visualizzare il contenuto del file ‘LETTERA’, lo invia al dispositivo di stampa<br />

corrispondente al nome ‘PRN:’; inoltre,<br />

C:\>DIR *.* > ELENCO<br />

invece di visualizzare l’elenco dei file che si trovano nella directory corrente, crea il file ‘ELENCO’<br />

con questi dati.<br />

La ridirezione dello standard output fatta in questo modo, va a cancellare completamente il contenuto<br />

del file di destinazione, se questo esiste già; al contrario, si può utilizzare anche ‘>>’, con<br />

il quale, il file di destinazione viene creato se non esiste, oppure viene solo esteso.<br />

Lo standard input viene ridiretto utilizzando il simbolo ‘SORT < ELENCO > ORDINATO<br />

In questo modo, ‘SORT’ riceve dallo standard input il file ‘ELENCO’ e genera attraverso la<br />

ridirezione dello standard output il file ‘ORDINATO’.


98 volume VIII Argomenti avanzati e accessori<br />

Per mettere in contatto lo standard output di un comando con lo standard input del successivo, si<br />

utilizza il simbolo ‘|’. L’esempio seguente mostra un modo alternativo di ottenere l’ordinamento<br />

di un file:<br />

C:\>TYPE ELENCO | SORT > ORDINATO<br />

In generale, tutti i comandi che generano un risultato visuale che scorre sullo schermo, utilizzano<br />

semplicemente lo standard output, che può essere ridiretto in questo modo. Si osservi ancora<br />

l’esempio seguente che riordina il risultato del comando ‘DIR’, mostrandolo comunque sullo<br />

schermo:<br />

C:\>DIR *.DOC | SORT<br />

Nelle sezioni seguenti vengono mostrati alcuni comandi filtro.<br />

358.8.1 SORT<br />

SORT [opzioni] < file_da_ordinare > file_ordinato<br />

Il comando ‘SORT’, che dovrebbe corrispondere a un programma di servizio vero e proprio, riordina<br />

il file di testo che ottiene dallo standard input, generando un risultato che emette attraverso<br />

lo standard output.<br />

Tabella 358.7. Alcune opzioni.<br />

Opzione Descrizione<br />

/R Riordina in modo decrescente<br />

Riordina in base al testo che inizia a partire dalla colonna in-<br />

/+n_colonna<br />

dicata come argomento (si tratta di un numero a partire da<br />

uno, per indicare la prima colonna).<br />

L’esempio seguente emette l’elenco della directory corrente riordinato in base all’estensione, che<br />

è un’informazione collocata a partire dalla decima colonna:<br />

C:\>DIR *.DOC | SORT /+10<br />

358.8.2 MORE<br />

MORE < file_da_leggere<br />

MORE file_da_leggere<br />

Il comando ‘MORE’ legge un file, fornito come argomento o attraverso lo standard input, mostrandolo<br />

poi sullo schermo una pagina dopo l’altra. In questo modo, è possibile leggere il contenuto<br />

dei file più lunghi delle righe a disposizione sullo schermo.<br />

Per passare alla pagina successiva, basta premere un tasto qualunque, oppure ciò che viene<br />

indicato espressamente.<br />

Segue la descrizione di alcuni esempi.<br />

• C:\>DIR | MORE<br />

Permette di controllare lo scorrimento a video del risultato del comando ‘DIR’.


Dos: introduzione 99<br />

• C:\>MORE LETTERA.TXT<br />

Permette di controllare lo scorrimento a video del contenuto del file ‘LETTERA.TXT’.<br />

• C:\>TYPE LETTERA.TXT | MORE<br />

Si ottiene lo stesso risultato dell’esempio precedente, attraverso l’uso di una pipeline.<br />

358.9 Accesso diretto ai dispositivi<br />

Il Dos <strong>of</strong>fre poche occasioni per accedere direttamente ai dispositivi. Si tratta generalmente solo<br />

della console e della porta parallela. L’esempio seguente mostra come «copiare» un file sul<br />

dispositivo di stampa, per ottenere così la sua stampa diretta:<br />

C:\>COPY LETTERA PRN:<br />

La stessa cosa avrebbe potuto essere ottenuta con la ridirezione dei flussi standard:<br />

C:\>TYPE LETTERA > PRN:<br />

Può essere interessante la possibilità di copiare il flusso di ingresso della console in un file:<br />

C:\>COPY CON: LETTERA<br />

In questo caso, l’inserimento nel file ‘LETTERA’ prosegue fino a quando viene ricevuto un codice<br />

EOF, che si ottiene qui con la combinazione di tasti [ Ctrl+z ] seguita da [ Invio ].<br />

È bene ricordare che la console, ovvero il dispositivo ‘CON:’, riceve dati in ingresso attraverso la<br />

tastiera ed emette dati in uscita utilizzando lo schermo. In pratica, quando un programma attende<br />

dati dallo standard input non ridiretto, li riceve dalla console, cioè dalla tastiera; nello stesso<br />

modo, quando un programma emette dati attraverso lo standard output non ridiretto, li invia alla<br />

console, cioè sullo schermo.<br />

358.10 Riferimenti<br />

• FreeDOS<br />

<br />

• OpenDOS Un<strong>of</strong>ficial Home Page<br />

<br />

Appunti di informatica libera 2003.06.29 --- Copyright © 2000-2003 Daniele Giacomini -- daniele @ swlibero.org


Dos: dischi, file system, directory e file<br />

Capitolo 359<br />

La gestione dei dischi, ovvero delle unità di memorizzazione di massa, è molto particolare nel<br />

Dos. In generale, si fa riferimento a queste cose attraverso un lettera che ne rappresenta il dispositivo;<br />

tuttavia, tale dispositivo può indicare un disco intero o solo una partizione, riferendosi<br />

sempre solo a dischi e partizioni Dos.<br />

359.1 Suddivisione in partizioni<br />

Nel Dos, tutti i dischi rimovibili, come i dischetti, non vanno suddivisi in partizioni, mentre i<br />

dischi fissi devono essere preparati in questo modo. Il programma che si usa per queste cose è<br />

‘FDISK’.<br />

Secondo il Dos, le partizioni di un disco possono essere solo quattro. Tuttavia, una partizione<br />

normale può essere suddivisa in sottopartizioni, che vengono definite tradizionalmente «estese»,<br />

dove anche queste possono essere al massimo quattro. Una tale struttura ha condizionato<br />

in pratica anche altri sistemi operativi, per esempio GNU/Linux. Bisogna tenere in considerazione<br />

l’origine storica per comprendere che altri sistemi operativi possono comportarsi in<br />

modo completamente differente.<br />

Di solito, ‘FDISK’ ha una visione delle partizioni tutta orientata verso il Dos. Infatti, consente<br />

di creare una sola partizione primaria (ovvero una partizione normale) e altre partizioni estese<br />

(ovvero altre sottopartizioni di una seconda partizione primaria).<br />

Bisogna considerare che il settore di avvio del Dos viene collocato nel primo settore della partizione<br />

primaria utilizzata per il Dos. In questo modo, manca la sistemazione del primo settore del<br />

disco, l’MBR, che deve contenere il codice necessario a raggiungere il settore di avvio.<br />

FDISK [/MBR]<br />

In generale, sembra che le varie edizioni di ‘FDISK’ per Dos funzionino solo con il primo disco<br />

fisso.<br />

Fondamentalmente, il programma è interattivo, per cui si avvia una maschera con la quale si<br />

interviene per mezzo di un menù. Di norma viene consentito di cancellare le partizioni, di crearne<br />

una primaria e probabilmente una sola di estesa.<br />

Di solito, è possibile riscrivere il settore di avvio MBR attraverso l’opzione ‘/MBR’.<br />

359.2 Inizializzazione di un’unità di memorizzazione<br />

L’inizializzazione di un’unità di memorizzazione, intesa come un dischetto o una partizione, si<br />

ottiene con il comando ‘FORMAT’. Questo si occupa anche di predisporre il file system Dos-FAT<br />

ed eventualmente anche di trasferire il kernel, per renderlo avviabile.<br />

FORMAT lettera_unità: [/N:settori] [/T:cilindri] [/S] [/U]<br />

In alcune edizioni del Dos, questo comando non inizializza l’unità di memorizzazione, ma si<br />

limita a sovrascrivere la parte iniziale. Ciò viene fatto per accelerare il procedimento e per<br />

permettere eventualmente il recupero dei dati, in caso di ripensamenti. In generale, sarebbe<br />

meglio evitare questa scorciatoia quando si tratta di unità corrispondenti ai dischetti; così, per<br />

confermare la richiesta di un’inizializzazione tradizionale, si può aggiungere l’opzione ‘/U’.<br />

100


Dos: dischi, file system, directory e file 101<br />

Segue la descrizione di alcuni esempi.<br />

• C:\>FORMAT A: /U<br />

Inizializza l’unità ‘A:’, corrispondente a un dischetto. L’inizializzazione avviene in modo<br />

completo, essendo stata usata l’opzione ‘/U’; inoltre, dal momento che non sono state<br />

indicate altre cose, il formato usato è quello predefinito in base alla configurazione del<br />

firmware.<br />

• C:\>FORMAT A: /N:9 /T:40 /U<br />

Come nell’esempio precedente, con l’aggiunta dell’indicazione della geometria: nove settori<br />

per traccia e 40 cilindri; si sottintende la presenza di due tracce per cilindro. Pertanto,<br />

dal momento che ogni settore è di 512 byte: 2 * 40 * 9 * 512 byte = 360 Kibyte.<br />

• C:\>FORMAT A: /N:9 /T:80 /U<br />

Come nell’esempio precedente, ma con 80 cilindri: 2 * 80 * 9 * 512 byte = 720 Kibyte.<br />

• C:\>FORMAT A: /N:15 /T:80 /U<br />

Come nell’esempio precedente, ma con 15 settori per traccia: 2 * 80 * 15 * 512 byte =<br />

1200 Kibyte.<br />

• C:\>FORMAT A: /N:18 /T:80 /U<br />

Come nell’esempio precedente, ma con 18 settori per traccia: 2 * 80 * 18 * 512 byte =<br />

1440 Kibyte.<br />

• C:\>FORMAT A: /S<br />

Inizializza il dischetto corrispondente all’unità ‘A:’, trasferendo successivamente il kernel<br />

e probabilmente anche l’interprete dei comandi (‘COMMAND.COM’). Ciò avviene perché è<br />

stata usata l’opzione ‘/S’.<br />

359.3 Etichetta di un’unità di memorizzazione<br />

Tradizionalmente, il Dos prevede la possibilità di attribuire un nome a un’unità di memorizzazione.<br />

Questo nome viene definito solitamente «etichetta» e di fatto viene annotato come un file<br />

speciale nella directory radice (anche se poi non appare nell’elenco). Per modificare o attribuire<br />

questo nome si utilizza il comando ‘LABEL’:<br />

LABEL [lettera_unità:][nome]<br />

Se non si indica la lettera dell’unità di memorizzazione su cui intervenire, si tratta implicitamente<br />

di quella da cui è stato avviato il sistema; se non si indica il nome da attribuire, ‘LABEL’ funziona<br />

in modo interattivo, chiedendo il da farsi.<br />

In linea di principio, l’etichetta di un’unità non serve, salvo il caso di qualche programma che<br />

potrebbe utilizzarla per uno scopo particolare (per esempio i programmi di installazione per<br />

identificare i dischetti).


102 volume VIII Argomenti avanzati e accessori<br />

Esiste anche un altro comando interno per la verifica del nome di un’unità; si tratta di ‘VOL’:<br />

VOL [lettera_unità:]<br />

Il risultato è solo l’informazione del nome stesso, con l’aggiunta del numero di serie se questo<br />

dato è disponibile.<br />

359.4 Analisi e correzione del file system<br />

Esistono pochi strumenti di analisi e correzione degli errori nel file system. In origine si tratta del<br />

comando ‘CHKDSK’, a cui in seguito si è aggiunto ‘SCANDISK’.<br />

CHKDSK lettera_unità: [/F]<br />

‘CHKDSK’ può essere usato solo con l’indicazione di un’unità di memorizzazione; in tal caso<br />

restituisce le informazioni disponibili su questa. Se si aggiunge l’opzione ‘/F’, si richiede<br />

esplicitamente la correzione, per quanto possibile, degli errori rilevati.<br />

L’errore tipico di un file system Dos-FAT si traduce in «concatenamenti perduti», ovvero file,<br />

interi o parziali, di cui non si può conoscere il nome. Questi file potrebbero essere solo dati<br />

temporanei che è bene siano cancellati, ma questa non è la regola. ‘CHKDSK’ tende a salvare<br />

questi file assegnando loro un nome più o meno casuale, lasciando all’utilizzatore l’onere di<br />

decidere cosa farne.<br />

359.5 Copia<br />

Nei sistemi Dos la copia è un’attività piuttosto articolata. In pratica, il comando interno ‘COPY’<br />

consente solo di copiare file puri e semplici. Per copiare un dischetto occorre il comando<br />

‘DISKCOPY’; per copiare file e directory occorre il comando ‘XCOPY’.<br />

359.5.1 DISKCOPY<br />

DISKCOPY unità_di_origine: unità_di_destinazione:<br />

‘DISKCOPY’ permette di eseguire la copia di un’unità di memorizzazione, purché si tratti di un<br />

dischetto. Il dischetto di destinazione dovrebbe essere inizializzato preventivamente.<br />

L’unità indicata come secondo argomento, che rappresenta la destinazione, può essere la stessa<br />

di quella di origine. In questo caso, i dischetti andranno alternati nel dispositivo che li ospita,<br />

seguendo le istruzioni che dà ‘DISKCOPY’ stesso.<br />

L’esempio seguente esegue la copia di un dischetto usando lo stesso dispositivo fisico:<br />

C:\>DISKCOPY A: A:<br />

359.5.2 XCOPY<br />

XCOPY percorso_origine [percorso_destinazione] [/E] [/S] [/H] [/V]<br />

‘XCOPY’ consente di copiare uno o più file assieme alla struttura di directory. In altri termini, ciò<br />

significa che è possibile copiare anche una directory intera.


Dos: dischi, file system, directory e file 103<br />

Tabella 359.1. Alcune opzioni.<br />

Opzione Descrizione<br />

/S Copia solo le directory e le sottodirectory non vuote.<br />

/E Copia tutte le sottodirectory, anche se vuote.<br />

/H Copia anche i file nascosti e di sistema.<br />

/V Verifica la copia.<br />

L’esempio seguente copia tutta la struttura che si articola a partire dalla directory ‘\PIPPO\’,<br />

nella directory ‘\PAPPA\’, includendo anche i file nascosti e quelli di sistema:<br />

C:\>XCOPY \PIPPO\*.* \PAPPA\*.* /E /S /H /V<br />

359.6 Trasferimento del sistema<br />

Il Dos è un sistema operativo elementare. L’essenziale in assoluto è costituito dal kernel e dall’interprete<br />

dei comandi. Per rendere «avviabile» un dischetto o una partizione basta copiare questi<br />

file e sistemare il settore di avvio, in modo che punti correttamente al kernel. Questo si può ottenere<br />

con il comando ‘FORMAT’, quando lo si usa con l’opzione ‘/S’ (cosa che naturalmente<br />

implica anche l’inizializzazione dell’unità), oppure con il comando ‘SYS’, fatto appositamente<br />

per questo:<br />

FORMAT lettera_unità: /S<br />

SYS lettera_unità:<br />

A seconda del tipo di Dos, vengono copiati solo i file del kernel, oppure anche l’interprete dei<br />

comandi (necessario per avviare il sistema).<br />

359.7 Modifica delle unità<br />

La caratteristica del Dos per cui si distinguono le unità di memorizzazione, introduce l’esigenza<br />

di comandi particolari, che vengono descritti brevemente nelle sezioni seguenti. In particolare,<br />

si tratta della possibilità di attribuire una lettera di unità differente e di poter inserire un’unità in<br />

una directory come avviene con l’innesto di un file system nei sistemi Unix.<br />

359.7.1 ASSIGN<br />

ASSIGN lettera_unità_1[:]=lettera_unità_2[:]<br />

ASSIGN /STATUS<br />

ASSIGN<br />

Il comando ‘ASSIGN’ permette di modifica il nome di un’unità di memorizzazione. Per ottenere<br />

questo risultato, rimane attivo come programma residente in memoria. Quando si usa senza<br />

argomenti, ‘ASSIGN’ elimina tutte le ridefinizioni; con l’opzione ‘/STATUS’ si ottiene lo stato<br />

attuale delle ridefinizioni; quando si indicano le lettere di unità, la prima è l’unità virtuale che<br />

viene creata come riproduzione della seconda.<br />

Segue la descrizione di alcuni esempi.<br />

• C:\>ASSIGN E:=A:<br />

Dopo questo comando, per accedere all’unità corrispondente al primo dischetto, si potrà<br />

indicare l’unità ‘E:’.


104 volume VIII Argomenti avanzati e accessori<br />

• C:\>ASSIGN<br />

Cancella tutte le ridefinizioni delle unità di memorizzazione.<br />

359.7.2 JOIN<br />

JOIN lettera_unità: percorso<br />

JOIN lettera_unità: /D<br />

JOIN<br />

Il comando ‘JOIN’ permette di attaccare un’unità di memorizzazione in corrispondenza di un<br />

percorso (una directory). Si tratta in pratica di montare l’unità, come avviene nei sistemi Unix.<br />

Quando si usa ‘JOIN’ senza argomenti, si ottiene un elenco degli innesti attivi; quando si usa<br />

l’opzione ‘/D’, si vuole annullare il collegamento dell’unità.<br />

Segue la descrizione di alcuni esempi.<br />

• C:\>JOIN A: C:\MNT\A<br />

Innesta l’unità ‘A:’ nella directory ‘C:\MNT\A\’.<br />

• C:\>JOIN A: /D<br />

Distacca l’unità ‘A:’ da un collegamento precedente.<br />

359.7.3 SUBST<br />

SUBST lettera_unità: percorso<br />

SUBST /D<br />

Il comando ‘SUBST’ permette di creare un’unità virtuale a partire da una directory di un’altra<br />

unità. In pratica, si fa in modo di permettere l’identificazione di una certa directory attraverso<br />

l’uso di una lettera di unità.<br />

Quando si usa ‘JOIN’ con l’opzione ‘/D’, si vuole annullare l’unità virtuale relativa.<br />

Segue la descrizione di alcuni esempi.<br />

• C:\>SUBST E: C:\EXTRA\E<br />

Crea l’unità virtuale ‘E:’ a partire dal contenuto delle directory ‘C:\EXTRA\E\’.<br />

• C:\>JOIN E: /D<br />

Elimina l’unità virtuale ‘E:’.


Dos: dischi, file system, directory e file 105<br />

359.8 Altre particolarità<br />

La gestione del Dos di file e directory è molto strana. Nelle sezioni seguenti vengono descritti<br />

alcuni programmi tipici dei sistemi Dos riguardanti la gestione di file e directory, che non hanno<br />

trovato un’altra collocazione in questo documento, a causa della loro particolarità.<br />

359.8.1 VERIFY<br />

VERIFY [ON|OFF]<br />

Il comando interno ‘VERIFY’ permette di richiedere al sistema operativo di verificare la registrazione<br />

nelle unità di memorizzazione. Come si vede dallo schema sintattico, si attiva o si disattiva<br />

la modalità, attraverso l’uso delle parole chiave ‘ON’ oppure ‘OFF’. Di solito, questa modalità è<br />

disabilitata ed è difficile definire la reale importanza di questa impostazione.<br />

Se si usa il comando senza alcun argomento, si ottiene di sapere quale sia l’impostazione attuale.<br />

359.8.2 APPEND<br />

APPEND directory<br />

APPEND ;<br />

APPEND<br />

Il comando ‘APPEND’ consente di definire un percorso per la ricerca dei file di dati. In pratica,<br />

si vuole permettere ai programmi di accedere a file di dati anche quando questi si trovano fuori<br />

della collocazione prevista. ‘APPEND’ può essere usato più volte, per aggiungere altre directory.<br />

Se viene usato con l’argomento ‘;’, si intende cancellare tutto l’elenco di directory di ricerca dei<br />

file di dati. Se viene usato senza argomenti, si ottiene l’elenco di queste directory.<br />

L’esempio seguente aggiunge la directory ‘C:\DATI\’ all’elenco dei percorsi di ricerca per i file<br />

di dati:<br />

C:\>APPEND C:\DATI<br />

359.8.3 ATTRIB<br />

ATTRIB [+R|-R] [+A|-A] [+S|-S] [+H|-H] file<br />

Il comando ‘ATTRIB’ permette di visualizzare o cambiare gli attributi del file. In pratica,<br />

utilizzando la forma ‘+x’ si attiva l’attributo x, mentre con ‘-x’ si disattiva l’attributo stesso.<br />

Segue la descrizione di alcuni esempi.<br />

• C:\>ATTRIB *.*<br />

Mostra gli attributi di tutti i file contenuti nella directory corrente.<br />

• C:\>ATTRIB +R *.*<br />

Imposta l’attributo di sola lettura per tutti i file della directory corrente.


106 volume VIII Argomenti avanzati e accessori<br />

359.8.4 DELTREE<br />

DELTREE directory<br />

Il comando ‘DELTREE’ consente di eliminare una directory con tutto il suo contenuto,<br />

ricorsivamente.<br />

L’esempio seguente elimina la directory ‘C:\TEMP\CIAO\’ assieme a tutto il suo contenuto:<br />

C:\>DELTREE C:\TEMP\CIAO<br />

359.8.5 FIND<br />

FIND [opzioni] "stringa" [file]<br />

Il comando ‘FIND’ è uno dei più complessi nei sistemi Dos. Serve per fare una ricerca di una<br />

stringa in uno o più file, in base a quanto indicato nell’ultimo argomento, oppure all’interno<br />

dello standard input. Il risultato normale della ricerca è l’emissione delle righe che contengono<br />

la stringa cercata, assieme all’indicazione del file a cui appartengono.<br />

Tabella 359.2. Alcune opzioni.<br />

Opzione Descrizione<br />

/V<br />

La ricerca avviene per le righe che non contengono la stringa<br />

cercata.<br />

/C<br />

Mostra solo il totale delle righe che contengono la stringa<br />

cercata.<br />

/N Mostra il numero di ogni riga che contiene la stringa cercata.<br />

Ignora la differenza tra maiuscole e minuscole per il confronto<br />

/I<br />

con la stringa di ricerca.<br />

In alcune edizioni del Dos, questa modalità di funzionamento<br />

è predefinita.<br />

Segue al descrizione di alcuni esempi.<br />

• C:\>FIND "ciao" *.*<br />

Cerca la stringa ‘ciao’ in tutti i file della directory corrente.<br />

• C:\>FIND "ciao" < MIO.TXT<br />

Cerca la stringa ‘ciao’ nel file ‘MIO.TXT’ che viene fornito attraverso lo standard input.<br />

359.8.6 MOVE<br />

MOVE file_origine directory_destinazione<br />

MOVE directory_origine directory_destinazione<br />

Il comando ‘MOVE’ consente di spostare file o directory in altre collocazioni. In generale, ‘MOVE’<br />

si occupa di spostare e non di rinominare i file, che invece è una funzione del comando ‘REN’.<br />

Il comando ‘MOVE’ è ambiguo e si comporta in maniera differente da una realizzazione all’altra<br />

dei sistemi Dos. In generale bisogna considerare che la destinazione può esistere o meno,<br />

implicando dei comportamenti differenti da valutare.


Dos: dischi, file system, directory e file 107<br />

L’esempio seguente sposta i file e le directory contenute in ‘C:\CIAO\’ nella directory ‘C:\<br />

MIA\’. Se la directory di destinazione non c’è, questa dovrebbe essere creata automaticamente,<br />

ma la cosa va verificata:<br />

C:\>MOVE C:\CIAO\*.* C:\MIA<br />

359.8.7 TREE<br />

TREE [directory]<br />

Il comando ‘TREE’ consente di visualizzare la struttura della directory corrente, oppure di un’altra<br />

directory indicata come argomento.<br />

L’esempio seguente mostra la struttura della directory ‘C:\CIAO\’:<br />

C:\>TREE C:\CIAO<br />

359.8.8 COMP e FC<br />

COMP file_1 file_2 [opzioni]<br />

FC file_1 file_2 [opzioni]<br />

I comandi ‘COMP’ e ‘FC’ permettono di verificare se due file sono identici, oppure no. Non sono<br />

molto facili da utilizzare, specialmente il primo; probabilmente vale la pena di sapere che ci sono,<br />

senza poi pretendere di sfruttare tutte le loro possibilità.<br />

‘FC’ assomiglia molto vagamente a un comando ‘diff’ di Unix, dal momento che di fronte a file<br />

di testo cerca di comprendere quale cambiamento è stato fatto. In questo senso, è probabile che<br />

‘FC’ sia il più utile tra questi due.<br />

Appunti di informatica libera 2003.06.29 --- Copyright © 2000-2003 Daniele Giacomini -- daniele @ swlibero.org


Dos: configurazione<br />

Capitolo 360<br />

Nel Dos è un po’ difficile scindere i concetti di configurazione e script, perché per configurare il<br />

sistema, occorre predisporre degli script. Si tratta dei file ‘\CONFIG.SYS’ e ‘\AUTOEXEC.BAT’,<br />

collocati nell’unità di avvio. Questo fatto è già stato accennato nel capitolo introduttivo; in questo<br />

si vuole appr<strong>of</strong>ondire un po’ la cosa.<br />

360.1 CONFIG.SYS<br />

Il file ‘CONFIG.SYS’, collocato nella directory radice dell’unità di avvio, è uno script speciale<br />

avviato dal kernel prima dell’interprete dei comandi. In linea di massima, si tratta di una sequenza<br />

di direttive che occupano ognuna una riga; alcune versioni recenti del Dos consentono di<br />

suddividere le direttive in sezioni da scegliere in base a un menù iniziale.<br />

Le direttive di ‘CONFIG.SYS’ hanno la forma seguente:<br />

nome=valore<br />

In pratica, si assegna una stringa (senza delimitatori espliciti) a un nome che ha un significato<br />

particolare.<br />

In questo file, vengono ignorate le righe vuote, quelle bianche e quelle che iniziano con la parola<br />

chiave ‘REM’:<br />

REM annotazione<br />

È importante osservare che i nomi delle direttive non fanno differenza tra lettere maiuscole<br />

e minuscole. In generale, questo vale anche per le stringhe che vengono assegnate a questi<br />

nomi.<br />

360.1.1 BREAK<br />

BREAK={ON|OFF}<br />

Teoricamente, questa istruzione consente di attivare o di disattivare la funzionalità abbinata alla<br />

combinazione di tasti [ Ctrl+c ]. In condizioni normali, quando si assegna la parola chiave ‘ON’, si<br />

attiva il funzionamento della combinazione [ Ctrl+c ].<br />

360.1.2 BUFFERS<br />

BUFFERS=n_buffer[,n_buffer_secondari]<br />

Questa istruzione consente di definire la quantità di memoria tampone per gli accessi ai dischi.<br />

Si assegnano uno o due valori numerici, separati da una virgola. Il primo valore va da 1 a 99 ed<br />

esprime il numero di aree da usare come memoria tampone; il secondo valore, facoltativo, indica<br />

delle memorie tampone secondarie, con valori che vanno da uno a otto.<br />

108


Dos: configurazione 109<br />

360.1.3 COUNTRY<br />

COUNTRY=n_codice_paese[,[n_codifica][,file_informazioni_nazionali]]<br />

Questa istruzione, attraverso quanto contenuto in un file che tradizionalmente si chiama<br />

‘COUNTRY.SYS’, permette di configurare il sistema in base alla nazionalità. Per la precisione,<br />

si può specificare un codice riferito alla nazionalità, attraverso il quale si ottiene una forma particolare<br />

per le date e gli orari, con l’aggiunta eventuale di un altro codice che specifica la codifica<br />

dei caratteri prescelta (codepage). La tabella 360.1 riepiloga questi codici che fanno riferimento<br />

tradizionalmente anche a paesi che non esistono più.<br />

Si può osservare che la stringa assegnata alla direttiva ‘COUNTRY’ può contenere l’indicazione<br />

di un file (con il percorso, completo di unità o meno). Questo file è quello che contiene poi<br />

le indicazioni relative alla nazionalità prescelta; come già accennato, di solito si tratta del file<br />

‘COUNTRY.SYS’.<br />

Tabella 360.1. Codici di nazionalità.<br />

Località Codice di nazionalità Codifiche utili<br />

USA 001 437, 850<br />

Canada francese 002 863, 850<br />

America latina 003 850, 437<br />

Russia 007 866, 437<br />

Olanda 031 850, 437<br />

Belgio 032 850, 437<br />

Francia 033 850, 437<br />

Spagna 034 850, 437<br />

Ungheria 036 850, 852<br />

Jugoslavia 038 850, 852<br />

Italia 039 850, 437<br />

Svizzera 041 850, 437<br />

Cecoslovacchia 042 850, 852<br />

Regno unito 044 850, 437<br />

Danimarca 045 850, 865<br />

Svezia 046 850, 437<br />

Norvegia 047 850, 865<br />

Polonia 048 850, 852<br />

Germania 049 850, 437<br />

Brasile 055 850, 860<br />

Australia 061 850, 437<br />

Giappone 081 932, 437, 850, 942<br />

Corea 082 934, 437, 850, 944<br />

Cina 088 938, 437, 850, 948<br />

Turchia 090 857, 850<br />

Asia (inglese) 099 850, 437<br />

Portogallo 351 850, 860<br />

Islanda 354 850, 861<br />

Finlandia 358 850, 437<br />

L’esempio seguente predispone l’impostazione nazionale per l’Italia, utilizzando la codifica 850,<br />

che ha il vantaggio di essere quella più comune dei paesi che usano l’alfabeto latino:<br />

COUNTRY=039,850,C:\DOS\COUNTRY.SYS


110 volume VIII Argomenti avanzati e accessori<br />

360.1.4 DEVICE, DEVICEHIGH<br />

DEVICE=programma_di_gestione_dispositivo [opzioni]<br />

DEVICEHIGH=programma_di_gestione_dispositivo [opzioni]<br />

Si tratta di un modo per avviare un programma speciale che ha lo scopo di rimanere residente<br />

in memoria. In generale, tali programmi servono per la gestione di qualche dispositivo,<br />

indispensabile prima di avviare l’interprete dei comandi.<br />

La differenza tra le due direttive sta nel fatto che la seconda cerca di caricare il programma nella<br />

memoria «alta».<br />

Le opzioni riguardano il programma.<br />

L’esempio seguente avvia il programma ‘MOUSE.SYS’ che presumibilmente gestisce il mouse<br />

(l’opzione ‘/2’ serve probabilmente a utilizzare il mouse collegato alla seconda porta seriale):<br />

DEVICE=C:\MOUSE\MOUSE.SYS /2<br />

360.1.5 DOS<br />

DOS={HIGH|LOW}[,{UMB|NOUMB}]<br />

DOS=[{HIGH|LOW},]{UMB|NOUMB}<br />

Questa istruzione richiede al kernel di allocarsi nella memoria convenzionale, ‘LOW’, o in quella<br />

alta, ‘HIGH’. La parola chiave ‘UMB’ richiede di mantenere un collegamento tra la UMB e la<br />

memoria convenzionale; la parola chiave ‘NOUMB’ fa sì che questo collegamento non abbia luogo.<br />

360.1.6 DRIVEPARM<br />

DRIVEPARM=[opzioni]<br />

Si tratta di una direttiva attraverso cui si possono definire i parametri relativi ai dispositivi a<br />

blocchi, per la precisione si tratta solo di dischi, se questo può essere necessario. Le opzioni<br />

assomigliano a quelle dei programmi di servizio, iniziando con una barra obliqua normale: ‘/x...’.<br />

Tabella 360.2. Alcune opzioni.<br />

Opzione Descrizione<br />

/d:n_dispositivo_fisico<br />

Consente di indicare il dispositivo attraverso un numero, da 0<br />

a 255. Lo zero corrisponde alla prima unità a dischetti.<br />

/c<br />

Se si utilizza questa opzione, si intende che l’unità fisica è in<br />

grado di sapere se il disco è inserito o meno.<br />

/f:n_formato<br />

Stabilisce il formato del dispositivo fisico; in pratica, fissa la<br />

geometria.<br />

/f:0 Dischetto 160 Kibyte, 180 Kibyte, 320 Kibyte, 360 Kibyte.<br />

/f:1 Dischetto 1200 Kibyte.<br />

/f:2 Dischetto 720 Kibyte.<br />

/f:5 Disco fisso.<br />

/f:6 Nastro.<br />

/f:7 Dischetto 1440 Kibyte.<br />

/f:9 Dischetto 2880 Kibyte.<br />

/h:n_testine Definisce il numero di testine.<br />

/i Indica che si tratta di un dischetto da 3,5 pollici.


Dos: configurazione 111<br />

Opzione Descrizione<br />

/n Si tratta di un disco fisso.<br />

/s:n_settori Definisce il numero di settori per traccia.<br />

/t:n_cilindri<br />

Definisce il numero dei cilindri (in altri termini: il numero di<br />

tracce per faccia).<br />

360.1.7 FCBS<br />

FCBS=n_blocchi<br />

Permette di definire il numero di blocchi di controllo dei file (file control block). Il valore va da 1<br />

a 255, mentre il valore normale è di quattro blocchi.<br />

360.1.8 FILES<br />

FILES=n_blocchi<br />

Permette di indicare il numero massimo di file aperti. Il numero che può essere assegnato va da<br />

8 a 255. Il valore predefinito dovrebbe essere di otto file.<br />

360.1.9 INSTALL<br />

INSTALL=programma [opzioni]<br />

Si tratta di un’istruzione con la quale si può avviare preventivamente un programma (che dovrebbe<br />

essere residente in memoria), prima dell’avvio dell’interprete dei comandi. In questo caso, a<br />

differenza della direttiva ‘DEVICE’, o ‘DEVICEHIGH’, si tratta di un programma normale.<br />

Le opzioni riguardano il programma.<br />

360.1.10 LASTDRIVE<br />

LASTDRIVE=lettera_unità_finale<br />

Consente di specificare l’ultima lettera di unità che può essere richiesta. Questo consente di<br />

risparmiare risorse, se si è consapevoli del fatto che non servono lettere oltre un certo punto. La<br />

lettera in questione può essere indifferentemente maiuscola o minuscola, senza che ciò possa fare<br />

differenza.<br />

360.1.11 SHELL<br />

SHELL=programma [opzioni]<br />

Permette di indicare esplicitamente il programma da avviare alla fine della procedura di avvio<br />

del kernel. In generale si tratta dell’interprete dei comandi. Questa direttiva può consentire di avviare<br />

un interprete alternativo a quello normale, oppure permette di avviarlo da una collocazione<br />

insolita; inoltre permette di dare al programma in questione delle opzioni particolari.<br />

L’esempio seguente avvia il programma ‘COMMAND.COM’ che si trova nella directory ‘C:\DOS\’:<br />

SHELL=C:\DOS\COMMAND.COM


112 volume VIII Argomenti avanzati e accessori<br />

360.1.12 STACK<br />

STACK=n_livelli[,dimensione_in_byte]<br />

Con questa istruzione è possibile fissare la dimensione dello stack, utilizzando valori da 8 a 64,<br />

oltre allo zero. Il valore dopo la virgola indica la dimensione in byte di ogni livello dello stack.<br />

In questo caso i valori vanno da 32 a 512.<br />

360.2 AUTOEXEC.BAT<br />

Il file ‘AUTOEXEC.BAT’ collocato nella directory radice dell’unità di avvio, è inteso essere uno<br />

script che viene eseguito dall’interprete dei comandi, ‘COMMAND.COM’, dopo l’avvio del sistema.<br />

Questo script viene realizzato normalmente in modo sequenziale, senza strutture di controllo.<br />

In generale è importante per due cose: impostare alcune variabili di ambiente fondamentali, per<br />

esempio ‘PATH’; avviare dei programmi che poi restano residenti in memoria, quando questo non<br />

si ottiene già attraverso il file ‘\CONFIG.SYS’.<br />

360.3 Comandi ridondanti<br />

Anche nel Dos è molto importante l’uso delle variabili di ambiente. È già stato mostrato il<br />

comando ‘SET’, attraverso il quale si impostano o si annullano le variabili di ambiente:<br />

SET nome_variabile=stringa_assegnata<br />

Alcune variabili hanno un’importanza particolare, per cui esiste un comando interno apposito<br />

(dell’interprete dei comandi), che serve a inizializzarle senza nemmeno l’uso del comando ‘SET’.<br />

• PROMPT stringa_di_invito<br />

Il comando interno ‘PROMPT’ rappresenta un modo alternativo per impostare la variabile di<br />

ambiente con lo stesso nome. Se si usa il comando senza l’argomento, si ripristina l’invito<br />

predefinito.<br />

• PATH [percorsi_degli_eseguibili]<br />

Il comando interno ‘PATH’ rappresenta un modo alternativo per impostare la variabile di<br />

ambiente con lo stesso nome. Se non si indica l’argomento, si ottiene la visualizzazione<br />

dell’elenco dei percorsi attivo.<br />

Esistono altri comandi particolari che si sovrappongono alle istruzioni del file ‘CONFIG.SYS’.<br />

• BREAK [ON|OFF]<br />

Abilita o disabilita la funzionalità abbinata alla combinazione di tasti [ Ctrl+c ]. Utilizzando<br />

il comando senza argomento, si ottiene la visualizzazione dello stato attuale.


Dos: configurazione 113<br />

360.4 Localizzazione<br />

La localizzazione del Dos si riduce alla configurazione della mappa della tastiera e alla definizione<br />

dell’insieme di caratteri. L’insieme di caratteri dipende dalla scelta della nazionalità, fatta<br />

nel file ‘CONFIG.SYS’, attraverso la direttiva ‘COUNTRY’.<br />

Nelle sezioni seguenti vengono mostrati alcuni comandi utili per le impostazioni che riguardano<br />

la localizzazione.<br />

360.4.1 CHCP<br />

CHCP [n_codifica]<br />

Si tratta di un comando interno dell’interprete dei comandi che interviene nella definizione della<br />

codifica utilizzata. In pratica, se si utilizza senza argomenti, mostra il numero della codifica attiva;<br />

se si indica un numero come argomento, cambia la codifica attiva, purché questa sia una di quelle<br />

ammissibili in base alla nazionalità stabilita con la direttiva ‘COUNTRY’ nel file di configurazione<br />

‘CONFIG.SYS’.<br />

L’esempio seguente fa in modo che sia attivata la codifica corrispondente al numero 850:<br />

C:\>CHCP 850<br />

360.4.2 KEYB<br />

KEYB [sigla_nazionale[,[n_codifica][,file_informazioni_tastiere]]]<br />

‘KEYB’ è un comando esterno che consente di cambiare la configurazione della tastiera secondo<br />

alcuni modelli di nazionalità predefiniti. La sigla nazionale è un codice di due lettere che, assieme<br />

alla nazionalità, dovrebbe indicare anche la lingua utilizzata. La tabella 360.3 elenca queste sigle.<br />

Tabella 360.3. Sigle nazionali-linguistiche per l’impostazione della mappa della<br />

tastiera.<br />

Segue la descrizione di alcuni esempi.<br />

Sigla Corrispondenza<br />

US USA (predefinito)<br />

FR Francia<br />

GR Germania<br />

IT Italia<br />

SP Spagna<br />

UK Gran Bretagna<br />

PO Portogallo<br />

SG Svizzera tedesca<br />

SF Svizzera francese<br />

DK Danimarca<br />

BE Belgio<br />

NL Olanda (Nederland)<br />

NO Norvegia<br />

LA America latina<br />

SV Svezia<br />

SU Finlandia (Suomi)<br />

CF Canada francese


114 volume VIII Argomenti avanzati e accessori<br />

• C:\>KEYB<br />

Mostra la configurazione attuale.<br />

• C:\>KEYB IT<br />

Predispone la mappa dei tasti per la disposizione italiana.<br />

• C:\>KEYB IT,850,C:\DOS\KEYBOARD.SYS<br />

Predispone la mappa dei tasti per la disposizione italiana, specificando l’uso della codifica<br />

850 e del file ‘C:\DOS\KEYBOARD.SYS’ per trovare le impostazioni standard delle tastiere.<br />

360.4.3 GRAFTABL<br />

GRAFTABL [n_codifica]<br />

GRAFTABL /STATUS<br />

‘GRAFTABL’ è un comando esterno che consente di cambiare la codifica per i caratteri visualizzati<br />

sullo schermo. L’opzione ‘/STATUS’ permette di conoscere la situazione attuale, mentre<br />

l’indicazione di un numero di codifica cambia l’impostazione.<br />

L’esempio seguente imposta l’uso della codifica 850:<br />

C:\>GRAFTABL 850<br />

360.5 Orologio<br />

Il Dos consente di accedere all’orologio dell’elaboratore, per leggere la data e l’ora, o per cambiare<br />

tali informazioni. In generale, il Dos non prevede la gestione di un orologio hardware allineato<br />

al tempo universale; pertanto, l’orologio hardware deve corrispondere necessariamente all’ora<br />

locale, lasciando all’utente il problema legato alle variazioni dell’ora estiva.<br />

I comandi per accedere all’orologio sono ‘DATE’ e ‘TIME’:<br />

DATE [data]<br />

TIME [orario]<br />

Se non si indica la data o l’orario, viene mostrato quello attuale e viene richiesto all’utente di<br />

modificarlo o di confermarlo.<br />

Il modo in cui va scritta da data o l’ora, dipende dalla localizzazione. Per conoscere quello giusto,<br />

basta osservare in che modo vengono visualizzate tali informazioni.<br />

Appunti di informatica libera 2003.06.29 --- Copyright © 2000-2003 Daniele Giacomini -- daniele @ swlibero.org


Dos: script dell’interprete dei comandi<br />

Capitolo 361<br />

Uno script dell’interprete dei comandi, conosciuto solitamente con il nome di file batch, potrebbe<br />

essere definito come un file di testo normale in cui può essere indicato un elenco di comandi da<br />

eseguire. Tuttavia, questi script consentono l’uso anche di strutture di controllo elementari, per<br />

cui si possono realizzare dei programmi molto semplici, senza troppe pretese.<br />

È interessante osservare che questi script vengono individuati solo attraverso l’estensione che ha<br />

il nome: ‘.BAT’. Inoltre, non esiste la necessità di renderli «eseguibili» come si fa nei sistemi<br />

Unix.<br />

361.1 Parametri, variabili ed espansione<br />

Gli script dell’interprete dei comandi hanno accesso agli argomenti che vengono loro forniti. Si<br />

possono gestire solo nove di questi argomenti alla volta, attraverso i parametri posizionali relativi,<br />

da ‘%1’ a ‘%9’. Come avviene nelle shell Unix, è disponibile il comando interno ‘SHIFT’ per fare<br />

scorrere in avanti gli argomenti nei parametri disponibili.<br />

Bisogna ricordare che in Dos i caratteri jolly non vengono espansi dalla shell, per cui la<br />

limitazione a soli nove parametri posizionali, non dovrebbe costituire un problema.<br />

Nell’ambito di uno script possono essere dichiarate e utilizzate delle variabili di ambiente. È già<br />

stato mostrato in precedenza l’uso del comando ‘SET’ per impostare o eliminare le variabili di<br />

ambiente. Per fare riferimento al contenuto di una variabile, si usa la notazione seguente:<br />

%nome_variabile%<br />

L’esempio seguente rappresenta il caso tipico di estensione di un percorso di ricerca degli<br />

eseguibili, quando si ritiene che la variabile ‘PATH’ sia già stata usata:<br />

SET PATH=%PATH%;C:\PIPPO<br />

361.2 Chiamate di altri script<br />

Tradizionalmente, il Dos ha un baco molto grave, ormai divenuto una caratteristica fondamentale,<br />

riguardante l’avvio di script all’interno di altri script. In generale, quando si chiama un<br />

programma che in realtà corrisponde a uno script, al termine di questo non riprende l’esecuzione<br />

di quello chiamante. Per ottenere la ripresa dell’interpretazione dello script di partenza occorre<br />

usare il comando speciale ‘CALL’.<br />

CALL nome_script [argomenti_dello_script]<br />

361.3 Strutture di controllo<br />

Le strutture di controllo per la programmazione attraverso gli script dell’interprete dei comandi<br />

sono molto limitate. È disponibile una struttura condizionale semplificata e un ciclo di scansione<br />

di file, che vengono descritti brevemente.<br />

115


116 volume VIII Argomenti avanzati e accessori<br />

361.3.1 IF<br />

IF [NOT] ERRORLEVEL valore_di_uscita_ultimo_comando comando<br />

IF [NOT] stringa_1==stringa_2 comando<br />

IF [NOT] EXIST file comando<br />

La struttura condizionale degli script dell’interprete dei comandi Dos è in pratica un comando<br />

interno dello stesso interprete. Come si può vedere dagli schemi sintattici, viene fornita una<br />

condizione che può essere invertita con la parola chiave ‘NOT’ e il risultato è solo l’esecuzione di<br />

un altro comando se la condizione risulta vera.<br />

Nel primo caso, la condizione si riferisce alla verifica del valore di uscita dell’ultimo comando<br />

eseguito. La condizione si verifica se il numero indicato è inferiore o uguale al valore restituito<br />

effettivamente da tale comando; nel secondo, la condizione si verifica se le due stringhe<br />

(non delimitate) sono identiche; nel terzo si verifica la condizione se il file indicato esiste<br />

effettivamente.<br />

Segue la descrizione di alcuni esempi.<br />

• IF ERRORLEVEL 1 GOTO :errore<br />

Se il comando precedente ha restituito un valore maggiore o uguale a uno, salta all’etichetta<br />

‘:errore’.<br />

• IF %1==ciao ECHO L’argomento è corretto<br />

In questo caso, se l’espansione del parametro ‘%1’, corrispondente al primo argomento<br />

ricevuto all’avvio, si traduce nella stringa ‘ciao’, viene emesso un messaggio per mezzo<br />

del comando ‘ECHO’.<br />

• IF %1x==x ECHO L’argomento è mancante<br />

Quello che si vede è il trucco necessario per poter verificare se un parametro contiene<br />

la stringa nulla: si aggiunge una lettera, in questo caso una «x», verificando che la<br />

corrispondenza avvenga solo con la stessa lettera.<br />

• IF NOT EXIST LETTERA.TXT ECHO Scrivi! > LETTERA.TXT<br />

Qui, se non esiste il file ‘LETTERA.TXT’ nella directory corrente, questo file viene creato<br />

attraverso il comando ‘ECHO’ che invia il suo standard output verso un file con lo stesso<br />

nome.<br />

361.3.2 FOR<br />

FOR [%]%x IN (nome...) DO comando [argomenti_del_comando]<br />

Si tratta di un comando interno che svolge un ciclo di scansione di un gruppo di nomi, generalmente<br />

file, attraverso il quale viene creato un parametro variabile speciale, il cui nome si compone<br />

di una sola lettera, a cui viene assegnato a ogni ciclo uno dei nomi contenuti tra parentesi tonde.<br />

A ogni ciclo viene eseguito il comando, che a sua volta può fare uso del parametro. 1<br />

1 Questo parametro assomiglia a una variabile di ambiente, ma non si comporta allo stesso modo. Si tratta di una<br />

particolarità del comando ‘FOR’.


Dos: script dell’interprete dei comandi 117<br />

Quando viene usato all’interno di uno script dell’interprete dei comandi, il parametro viene<br />

indicato con due simboli di percentuale (‘%%x’); al contrario, se il comando viene impartito<br />

dalla riga di comando, se ne usa uno solo.<br />

Segue la descrizione di alcuni esempi.<br />

• FOR %A IN (uno due tre) DO ECHO %A<br />

•<br />

In questo modo, si ottiene la visualizzazione delle parole ‘uno’, ‘due’ e ‘tre’. In pratica, è<br />

come se fosse stato fatto:<br />

ECHO uno<br />

ECHO due<br />

ECHO tre<br />

Volendo fare la stessa cosa dalla riga di comando, è necessario il raddoppio del simbolo ‘%’:<br />

C:\>FOR %%A IN (uno due tre) DO ECHO %%A<br />

• FOR %A IN (*.TMP *.BAD) DO DEL %A<br />

Cancella, uno a uno, tutti i file che terminano con le estensioni ‘.TMP’ e ‘.BAD’.<br />

361.3.3 GOTO<br />

GOTO etichetta<br />

Gli script dell’interprete dei comandi dispongono dell’istruzione di salto incondizionato, non<br />

avendo di meglio. Anche questa istruzione può essere presa come un comando interno<br />

dell’interprete, con la differenza che non c’è modo di utilizzarlo al di fuori di uno script.<br />

Nel corso di uno script del genere, possono apparire delle righe che contengono solo un’etichetta,<br />

nella forma:<br />

:nome_etichetta<br />

La posizione corrispondente a queste etichette può essere raggiunta con il comando ‘GOTO’, che<br />

può fare riferimento solo al nome dell’etichetta, oppure a tutta l’etichetta, includendo anche i due<br />

punti.<br />

Segue la descrizione di alcuni esempi.<br />

•<br />

•<br />

IF EXIST LETTERA.TXT GOTO riprendi<br />

ECHO Il file LETTERA.TXT è assente<br />

:riprendi<br />

In questo esempio, se il file ‘LETTERA.TXT’ esiste, si salta all’etichetta ‘:riprendi’;<br />

altrimenti si esegue il comando ‘ECHO’.<br />

IF EXIST LETTERA.TXT GOTO :riprendi<br />

ECHO Il file LETTERA.TXT è assente<br />

:riprendi<br />

Esattamente come nell’esempio precedente, con la differenza che il comando ‘GOTO’ indica<br />

l’etichetta con i suoi due punti iniziali.


118 volume VIII Argomenti avanzati e accessori<br />

361.3.4 Emulazione di un ciclo iterativo<br />

Dal momento che non è disponibile una struttura di controllo per il ciclo iterativo, questo può<br />

essere ottenuto solo attraverso l’uso del comando ‘GOTO’. Vale la pena di mostrare in che modo<br />

si può ottenere tale risultato.<br />

:etichetta_di_ingresso<br />

IF condizione GOTO :etichetta_di_uscita<br />

...<br />

...<br />

...<br />

GOTO etichetta_di_ingresso<br />

:etichetta_di_uscita<br />

:etichetta_di_ingresso<br />

...<br />

...<br />

...<br />

IF condizione GOTO :etichetta_di_ingresso<br />

:etichetta_di_uscita<br />

I due modelli sintattici mostrano due esempi di cicli iterativi. Nel primo caso si verifica una<br />

condizione, in base alla quale si decide se proseguire o se terminare il ciclo; nel secondo si<br />

esegue una volta il ciclo e quindi si verifica una condizione per decidere se ripeterlo o se uscire.<br />

361.4 Comandi utili negli script<br />

Alcuni comandi sono particolarmente utili all’interno di script dell’interprete dei comandi.<br />

Vengono descritti brevemente nelle sezioni seguenti.<br />

361.4.1 REM<br />

REM commento<br />

I commenti negli script dell’interprete dei comandi si indicano attraverso un comando apposito:<br />

‘REM’. Il funzionamento è evidente: tutto quello che segue il comando, fino alla fine della riga,<br />

viene ignorato.<br />

Alcune edizioni del Dos hanno introdotto anche l’uso del punto e virgola, come simbolo per<br />

indicare l’inizio di un commento. Segue un esempio tipico di utilizzo di questo comando:<br />

REM<br />

REM (c) 2000 Pinco pallino<br />

REM<br />

361.4.2 ECHO<br />

ECHO [ON|OFF]<br />

ECHO stringa<br />

Il comando interno ‘ECHO’ ha un significato duplice: da una parte consente di visualizzare un testo;<br />

dall’altra controlla la visualizzazione dei comandi contenuti in uno script. Infatti, si distingue<br />

il fatto che l’eco dei comandi sia attivo o meno. utilizzando il comando ‘ECHO’ senza argomenti,<br />

si ottiene l’informazione sul suo stato di attivazione. Di solito si disattiva l’eco dei comandi negli<br />

script.


Dos: script dell’interprete dei comandi 119<br />

Per disattivare l’eco di un comando particolare, senza disattivare l’eco in generale, basta<br />

inserire inizialmente il simbolo ‘@’.<br />

Segue la descrizione di alcuni esempi.<br />

• @ECHO OFF<br />

Disattiva l’eco dei comandi, facendo in modo che anche questo comando non venga<br />

visualizzato (si usa per questo il simbolo ‘@’).<br />

• ECHO Premi un tasto per continuare<br />

Mostra un messaggio per spiegare come comportarsi.<br />

361.4.3 PAUSE<br />

PAUSE<br />

Il comando interno ‘PAUSE’ sospende l’esecuzione di uno script in attesa della pressione di un<br />

tasto. Il comando emette attraverso lo standard output un messaggio di avvertimento in tal senso.<br />

Di solito, per evitare di vedere tale messaggio, si ridirige lo standard output in un file nullo.<br />

L’esempio seguente, prima mostra un messaggio in cui si avverte che per proseguire occorre<br />

premere un tasto, quindi si usa il comando ‘PAUSE’ che sospende l’esecuzione dello script, senza<br />

però mostrare altri messaggi:<br />

ECHO Premere un tasto per proseguire<br />

PAUSE > C:\NULL<br />

361.4.4 CLS<br />

CLS<br />

Il comando interno ‘CLS’ ripulisce lo schermo. Si utilizza senza argomenti.<br />

361.4.5 CHOICE<br />

CHOICE [opzioni] [testo_di_invito]<br />

Il comando ‘CHOICE’ serve a presentare una richiesta per l’inserimento di una lettera, tra un elenco<br />

determinato. La pressione del tasto corrispondente alla lettera scelta, da parte dell’utilizzatore,<br />

provoca la conclusione del funzionamento di ‘CHOICE’ che restituisce un valore corrispondente<br />

alla scelta: zero per la prima lettera, uno per la seconda,...<br />

Si osservi che l’ultimo argomento rappresenta un messaggio che serve all’utente per comprendere<br />

il senso della scelta che sta facendo.


120 volume VIII Argomenti avanzati e accessori<br />

Tabella 361.1. Alcune opzioni.<br />

Opzione Descrizione<br />

Permette di fissare l’elenco di lettere che possono essere usate<br />

/C:lettera[lettera...]<br />

nella risposta. Se non si indica questa opzione, la scelta sarà<br />

solo tra ‘y’ e ‘n’.<br />

Questa opzione fa in modo di escludere la visualizzazione<br />

delle lettere che possono essere scelte. In questo modo si fa<br />

/N<br />

affidamento esclusivamente sul testo indicato come ultimo<br />

argomento.<br />

Distingue tra maiuscole e minuscole per quanto riguarda le<br />

/S<br />

lettere tra cui scegliere.<br />

L’esempio seguente, salta a un punto differente dello script in base alla scelta di una lettera da «a»<br />

a «f». Si osservi che non sarebbe possibile eseguire l’analisi secondo una sequenza differente,<br />

perché ‘IF ERRORLEVEL’ prende in considerazione tutti i valori di uscita maggiori o uguali a<br />

quanto indicato nella condizione.<br />

CHOICE /C:abcdef Inserisci una lettera<br />

IF ERRORLEVEL 5 GOTO :f<br />

IF ERRORLEVEL 4 GOTO :e<br />

IF ERRORLEVEL 3 GOTO :d<br />

IF ERRORLEVEL 2 GOTO :c<br />

IF ERRORLEVEL 1 GOTO :b<br />

IF ERRORLEVEL 0 GOTO :a<br />

...<br />

Appunti di informatica libera 2003.06.29 --- Copyright © 2000-2003 Daniele Giacomini -- daniele @ swlibero.org


Dos: gestione della memoria centrale<br />

Capitolo 362<br />

Quando è nato il Dos non si prevedeva l’uso di memoria centrale oltre il singolo mebibyte (1 Mibyte).<br />

In base a questa considerazione veniva articolata l’architettura hardware degli elaboratori<br />

«XT» e poi «AT», dove si prevedeva l’uso di un massimo di 640 Kibyte di memoria centrale,<br />

riservando la parte successiva, fino alla fine di 1 Mibyte, per la memoria video e altri dispositivi<br />

fisici.<br />

In questo senso, il Dos tradizionale può operare con un massimo di 640 Kibyte di memoria<br />

centrale; per sfruttarne di più occorrono degli accorgimenti non facili da applicare.<br />

362.1 Gestione particolare<br />

Per sfruttare la memoria oltre il primo mebibyte, si fa uso normalmente di due programmi, avviati<br />

attraverso ‘CONFIG.SYS’, prima ancora dell’interprete di comandi. Si tratta di ‘HIMEM.SYS’ e di<br />

‘EMM386.EXE’. In generale, le cose si fanno nel modo seguente:<br />

DEVICE=C:\DOS\HIMEM.SYS<br />

DEVICE=C:\DOS\EMM386.EXE<br />

Il primo dei due programmi può essere utilizzato a partire da architetture i286, mentre il secondo<br />

si può inserire solo a partire da architetture i386.<br />

‘HIMEM.SYS’ è in grado di utilizzare solo una piccola parte di memoria aggiuntiva, mentre<br />

‘EMM386.EXE’ permette teoricamente di sfruttare tutto il resto.<br />

In generale, è molto difficile la gestione ottimale della memoria centrale, perché le applicazioni<br />

si comportano in maniera differente. Di solito si possono solo fare dei tentativi.<br />

362.2 Comandi appositi<br />

Per sfruttare la memoria centrale che supera la soglia convenzionale, sono disponibili alcuni comandi<br />

specifici. In generale, si comincia dalla configurazione con il file ‘CONFIG.SYS’: dopo<br />

l’attivazione dei gestori speciali della memoria, è possibile indicare di collocare parte dell’interprete<br />

dei comandi e dello spazio richiesto dai programmi residenti in memoria, oltre il limite<br />

della memoria convenzionale:<br />

DOS=HIGH,UMB<br />

In seguito, sempre nell’ambito del file ‘CONFIG.SYS’, si può richiedere esplicitamente l’avvio di<br />

programmi nella memoria alta attraverso la direttiva ‘DEVICEHIGH’, come si vede nell’esempio<br />

seguente:<br />

DEVICEHIGH=C:\MOUSE\MOUSE.SYS /2<br />

Per quanto riguarda i programmi avviati attraverso l’interprete dei comandi, è disponibile il<br />

comando ‘LH’, ovvero ‘LOADHIGH’:<br />

LH programma [argomenti_del_programma]<br />

LOADHIGH programma [argomenti_del_programma]<br />

Per esempio, si potrebbe tentare di avviare in questo modo il programma di gestione della tastiera:<br />

C:\>LH KEYB IT<br />

121


122 volume VIII Argomenti avanzati e accessori<br />

362.3 Verifica<br />

Il Dos <strong>of</strong>fre un solo programma molto semplice per la verifica dell’utilizzo della memoria: ‘MEM’.<br />

MEM [opzioni]<br />

Se ‘MEM’ viene usato senza opzioni, visualizza brevemente la quantità di memoria utilizzata rispetto<br />

al totale disponibile. È interessante l’opzione ‘/CLASSIFY’, attraverso la quale è possibile<br />

distinguere l’utilizzo della memoria da parte dei programmi residenti; inoltre è interessante<br />

l’opzione ‘/FREE’, con cui si hanno informazioni dettagliate sulla memoria libera.<br />

Le opzioni disponibili del comando ‘MEM’ variano molto da una realizzazione all’altra. In<br />

generale conviene verificare prima di utilizzarlo, per conoscere le possibilità effettive.<br />

Appunti di informatica libera 2003.06.29 --- Copyright © 2000-2003 Daniele Giacomini -- daniele @ swlibero.org


FreeDOS<br />

Capitolo 363<br />

FreeDOS è il nome di un progetto per la realizzazione di un sistema operativo libero compatibile<br />

con il Dos. Il Dos, per quanto limitato, ha delle caratteristiche che lo possono rendere ancora<br />

interessante per elaboratori con architettura i86 particolarmente poveri di risorse, come nel caso<br />

dei sistemi cosiddetti embedded.<br />

363.1 Installazione<br />

L’installazione della distribuzione standard di FreeDOS è abbastanza semplice. Si parte da un dischetto<br />

di avvio, con il quale si predispone la partizione e la si inizializza, quindi si prosegue con<br />

il programma di installazione che chiede l’inserimento dei dischetti successivi. La riproduzione<br />

del dischetto di avvio a partire dalla sua immagine avviene come al solito attraverso il programma<br />

‘RAWRITE.EXE’, oppure per mezzo di un sistema Unix nei modi già mostrati per GNU/Linux<br />

e altri sistemi simili.<br />

C:\>RAWRITE FULL.BIN A:<br />

L’esempio mostra l’uso di ‘RAWRITE.EXE’ per ottenere un dischetto dall’immagine<br />

rappresentata dal file ‘FULL.BIN’.<br />

La distribuzione standard di FreeDOS si compone di un file-immagine del dischetto di avvio,<br />

che potrebbe chiamarsi ‘FULL.BIN’, e da una serie di file con estensione ‘.ZIP’ che servono<br />

per ottenere i dischetti successivi. Ognuno di questi file compressi rappresenta il contenuto di un<br />

dischetto, che quindi deve essere prima estratto:<br />

C:\>A:<br />

A:\>UNZIP C:\TMP\BASE1.ZIP<br />

L’esempio mostra in breve il procedimento: ci si sposta nell’unità ‘A:’ e da lì si estrae il file<br />

compresso che probabilmente si trova da qualche parte nel disco fisso.<br />

Questi file compressi rappresentano una raccolta di applicativi e hanno una struttura particolare<br />

che viene descritta nel seguito.<br />

• nome_raccolta.1<br />

L’archivio compresso deve contenere un file che rappresenta il nome della raccolta, con<br />

un’estensione numerica. La raccolta potrebbe essere suddivisa in più archivi ed è per questo<br />

che si usa l’estensione numerica, che indica il numero di sequenza dell’archivio nell’ambito<br />

della raccolta.<br />

Il file contiene l’elenco dei pacchetti contenuti, con l’indicazione dell’opzione di installazione<br />

predefinita o meno. Si osservi l’estratto seguente (la lettera «Y» rappresenta la<br />

conferma all’installazione predefinita):<br />

asgn14x: Y<br />

attr063x: Y<br />

bwb210x: Y<br />

choic20x: Y<br />

• nome_raccolta.END<br />

Si tratta di un file vuoto, che rappresenta la conclusione della raccolta, nel senso che non ci<br />

sono altri dischetti ulteriori.<br />

123


124 volume VIII Argomenti avanzati e accessori<br />

• nome_pacchetto.LSM<br />

Si tratta di un file che descrive un pacchetto applicativo. Quello che segue è l’esempio del<br />

contenuto del file ‘DELTR10X.LSM’:<br />

Begin3<br />

Title: deltree<br />

Version: 1.02b<br />

Entered-date: 27 Jul 1999<br />

Description: Delete a directory and all directories under it<br />

Keywords: freedos delete<br />

Author: raster@highfiber.com<br />

Maintained-by: raster@highfiber.com<br />

Primary-site: http://www.highfiber.com/~raster/freeware.htm<br />

Alternate-site: www.freedos.org<br />

Original-site: http://www.highfiber.com/~raster/freeware.htm<br />

Platforms: dos<br />

Copying-policy: GPL<br />

End<br />

• nome_pacchetto.ZIP<br />

Si tratta dell’archivio compresso che contiene i file dell’applicativo. In base alla struttura<br />

standard di FreeDOS, potrebbe distribuirsi nelle directory ‘BIN\’, ‘DOC\’ e ‘HELP\’.<br />

Dopo aver preparato i dischetti, si può procedere con l’avvio del sistema attraverso il dischetto<br />

di avvio; quindi si passa a predisporre la partizione:<br />

A:\>FDISK<br />

Purtroppo, il kernel di FreeDOS non è in grado di gestire partizioni più grandi di 512 Mibyte,<br />

per cui occorre tenerne conto durante l’uso di ‘FDISK’. Dopo aver preparato la partizione la si<br />

inizializza:<br />

A:\>FORMAT C: /U<br />

Successivamente si trasferisce il sistema, con il comando ‘SYS’:<br />

A:\>SYS C:<br />

Infine si avvia il programma di installazione che provvederà a chiedere la sostituzione dei<br />

dischetti:<br />

A:\>INSTALL<br />

363.2 Impostazione e configurazione<br />

Da quanto è stato descritto sull’installazione di FreeDOS si intende che, pur trattandosi di un<br />

sistema Dos, si cerca di introdurre qualche buona idea proveniente da Unix. In particolare, è<br />

prevista una struttura per la collocazione dei file:<br />

• ‘BIN\’ per contenere i file eseguibili;<br />

• ‘DOC\’ per contenere la documentazione che si articola in altre sottodirectory successive,<br />

come avviene con GNU/Linux<br />

• ‘HELP\’ per contenere i file della guida interna relativa.


FreeDOS 125<br />

Questa struttura potrebbe essere collocata anche a partire da un punto differente della radice<br />

dell’unità, in base alle scelte fatte in fase di installazione. In ogni caso, occorre poi predisporre<br />

coerentemente alcune variabili di ambiente: ‘PAGER’ per indicare il programma da utilizzare per<br />

lo scorrimento dei file delle guide; ‘HELPPATH’ per indicare la directory contenente i file delle<br />

guide; ‘EMACS’ per indicare la directory contenente i file di Emacs.<br />

In condizioni normali, gli applicativi FreeDOS vengono installati a partire dalla directory<br />

‘\FDOS\’, per cui la configurazione si traduce nelle istruzioni seguenti nel file ‘AUTOEXEC.BAT’:<br />

SET PAGER=MORE<br />

SET HELPPATH=C:\FDOS\HELP<br />

SET EMACS=C:\FDOS\EMACS\<br />

In base alla documentazione originale, nel caso della variabile di ambiente ‘EMACS’ deve essere<br />

indicata la barra obliqua inversa finale.<br />

A seconda della distribuzione di FreeDOS, può darsi che il file ‘CONFIG.SYS’ debba essere<br />

sostituito con uno avente un nome differente. Potrebbe trattarsi del file ‘FDCONFIG.SYS’.<br />

363.3 RxDOS<br />

RxDOS è un altro progetto analogo a FreeDOS, scritto in maniera indipendente. È provvisto di<br />

un proprio interprete dei comandi e non ha ancora un suo sistema di installazione. Per provare il<br />

funzionamento di RxDOS ci si può avvalere solo di un dischetto, realizzato nel modo seguente:<br />

1. si inizializza il dischetto in qualche modo, assicurando che alla fine sia disponibile un<br />

file system Dos-FAT; 1<br />

2. si esegue lo script ‘MAKEBOOT.BAT’, il cui scopo è la predisposizione del settore di avvio<br />

nel dischetto;<br />

3. si copiano ordinatamente nel dischetto i file elencati qui sotto.<br />

• ‘RXDOSBIO.SYS’<br />

• ‘RXDOS.SYS’<br />

• ‘RXDOSCMD.EXE’<br />

• ‘RXDVDISK.SYS’<br />

• ‘AUTOEXEC.DEF’<br />

• ‘CONFIG.DEF’<br />

363.4 Riferimenti<br />

• FreeDOS<br />

<br />

Appunti di informatica libera 2003.06.29 --- Copyright © 2000-2003 Daniele Giacomini -- daniele @ swlibero.org<br />

1 Il dischetto non deve avere l’etichetta, ovvero non deve avere un nome.


Progetto GNUish<br />

Capitolo 364<br />

Il progetto «GNUish» è una sorta di derivazione povera del progetto GNU, con lo scopo di<br />

rendere disponibile parte del s<strong>of</strong>tware che compone il sistema GNU anche nei sistemi Dos. Il<br />

progetto ha un’importanza molto piccola, ma viene ancora mantenuto. Evidentemente, date le<br />

peculiarità dei sistemi Dos, il s<strong>of</strong>tware che viene adattato non può avere le stesse potenzialità che<br />

ha invece in un sistema Unix.<br />

I siti principali da cui si può ottenere copia del materiale prodotto dal progetto GNUish sono<br />

quelli elencati all’interno dal documento seguente:<br />

• <br />

In questo capitolo viene mostrato il funzionamento di alcuni programmi, nell’ambito del sistema<br />

Dos, per i quali è il caso di spendere qualche parola.<br />

364.1 Programmi di servizio vari<br />

Molti dei programmi di servizio del progetto GNU sono disponibili anche per Dos. Tuttavia, è il<br />

caso di osservare alcune particolarità che possono confondere chi è abituato a usare sistemi Dos.<br />

La prima cosa da notare è il fatto che i percorsi si possono indicare secondo lo stile Unix,<br />

utilizzando barre oblique normali. Per esempio:<br />

C:\>MV C:/PRIMO/SECONDO C:/TERZO<br />

Diversamente, utilizzando lo stesso comando, ma secondo l’indicazione tipica del Dos, la cosa<br />

può funzionare ugualmente, oppure si possono presentare delle segnalazioni di errore. Bisogna<br />

tenere presente la possibilità.<br />

Un’altra cosa da notare è l’uso dei caratteri jolly, che con questi programmi segue la logica<br />

di Unix, dove l’asterisco indica qualunque nome, senza trattare in modo speciale il punto di<br />

separazione dell’estensione:<br />

C:\>CP C:/PRIMO/SECONDO/* C:/TERZO<br />

L’esempio mostra proprio questo fatto: vengono copiati tutti i file contenuti nella directory ‘C:\<br />

PRIMO\SECONDO\’, nella directory ‘C:\TERZO\’.<br />

364.2 Gnuplot<br />

Il funzionamento generale di Gnuplot è descritto nel capitolo 337. Per funzionare, questa edizione<br />

di Gnuplot richiede due file: ‘GNUPLOT.EXE’ e ‘GNUPLOT.GIH’. Il primo dei due è l’eseguibile in<br />

grado di gestire la grafica VGA, mentre il secondo contiene le informazioni della guida interna.<br />

Se si vuole accedere alla guida interna, è necessario che il file ‘GNUPLOT.GIH’ si trovi nella<br />

directory corrente. Forse è sufficiente utilizzare il comando ‘APPEND’ del Dos per risolvere il<br />

problema.<br />

126


Progetto GNUish 127<br />

364.3 Spreadsheet Calculator<br />

Il funzionamento generale di SC (Spreadsheet Calculator) è descritto nel capitolo 336. La versione<br />

per Dos funziona correttamente (è sufficiente disporre dell’eseguibile ‘SC.EXE’), riconoscendo<br />

anche l’uso dei tasti freccia, per cui non si è più costretti a utilizzare le lettere ‘h’, ‘j’, ‘k’ e<br />

‘l’.<br />

364.4 Ispell<br />

Ispell è descritto in generale nel capitolo 273. Questa edizione di Ispell richiede due file:<br />

‘ISPELL.EXE’ e ‘ISPELL.DIC’. Come si intuisce, il primo è l’eseguibile, mentre il secondo<br />

è il file del dizionario. Purtroppo, il file ‘ISPELL.DIC’ non è sostituibile o eliminabile; l’unica<br />

cosa che si può fare è predisporre un dizionario personalizzato che si richiama con l’opzione<br />

‘-p’.<br />

ISPELL [-d dizionario_standard] [-p dizionario_personale] file<br />

Quella che si vede è la sintassi essenziale su cui si può contare nell’edizione di Ispell per Dos. Il<br />

file del dizionario standard, ‘ISPELL.DIC’, può essere collocato nella stessa directory in cui si<br />

trova il file eseguibile; altrimenti si deve usare l’opzione ‘-d’ per indicarlo esplicitamente.<br />

Il dizionario personale è un file di testo normale (Dos), che può anche essere creato inizialmente<br />

dallo stesso Ispell. L’esempio seguente, mostra il caso in cui si voglia analizzare il file<br />

‘LETTERA.TXT’ attraverso il dizionario standard e il dizionario personale ‘VOCAB.TXT’. Se il<br />

file ‘VOCAB.TXT’ non dovesse esistere, verrebbe creato per l’occasione.<br />

C:\LETTERE>ISPELL -p VOCAB.TXT LETTERA.TXT<br />

364.5 Perl<br />

Perl è un linguaggio di programmazione descritto in generale a partire dal capitolo 299. L’edizione<br />

Dos dell’interprete Perl richiede due file: ‘PERL.EXE’ e ‘PERLGLOB.EXE’. È sufficiente che<br />

questi siano disponibili nei percorsi degli eseguibili della variabile di ambiente ‘PATH’.<br />

Bisogna tenere a mente che si tratta di una versione molto vecchia del linguaggio, per cui alcune<br />

novità non saranno disponibili. Inoltre, l’avvio dei programmi può avvenire solo richiamando<br />

direttamente l’interprete:<br />

C:\ESERCIZI>PERL FATT.PL 5<br />

L’esempio mostra l’avvio del programma Perl contenuto nel file ‘FATT.PL’, che riceve un<br />

argomento costituito dal numero cinque.<br />

364.6 Riferimenti<br />

• François Pinard, GNUish MSDOS Project<br />

<br />

Appunti di informatica libera 2003.06.29 --- Copyright © 2000-2003 Daniele Giacomini -- daniele @ swlibero.org


The valuable DOS Freeware page<br />

Links to valuable free Dos programs working on low equipped computers.<br />

Chapter 365<br />

This material appeared originally at ‘http://www.geocities.com/SiliconValley/<br />

7737/’, in 1996. Now it is incorporated inside the Italian document ‘‘Appunti<br />

di informatica libera’’, and it might be reached at the URI .<br />

Questo materiale è apparso in origine, nel 1996, presso ‘http://www.geocities.com/<br />

SiliconValley/7737/’. Adesso viene incorporato nel documento «Appunti di<br />

informatica libera» e può essere raggiunto attraverso l’URI . L’intento dell’autore è solo quello di continuare a curare<br />

un vecchio lavoro che potrebbe essere ancora utile, nonostante si tratti di riferimenti a<br />

s<strong>of</strong>tware in parte libero e in parte solo gratuito, oltre che evidentemente obsoleto.<br />

365.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128<br />

365.2 OS and GUI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129<br />

365.3 Utility . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129<br />

365.4 Network . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130<br />

365.5 Compilers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132<br />

365.6 Typesetting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132<br />

365.7 More Dos s<strong>of</strong>tware sources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133<br />

365.8 Search engines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133<br />

365.1 Introduction<br />

The Dos operating system meant much for many people. Today, proprietary Dos-like operating<br />

systems seem to be no more developed. In this situation, the only possible future for Dos is the<br />

‘‘free’’ s<strong>of</strong>tware, and it is not just a matter <strong>of</strong> money anymore.<br />

Unfortunately, ‘‘free’’ is a word with many meanings. Today, this is still the biggest obstacle to<br />

the future <strong>of</strong> the Dos world. There is so much s<strong>of</strong>tware for Dos, with so many different license<br />

agreements. The typical Dos user doesn’t mind to it. But this problem prevents the realization <strong>of</strong><br />

big serious projects based on it.<br />

Today, the Dos world needs philosophy, and the GNU idea is still the right one ().<br />

The author <strong>of</strong> this space would like to list here only ‘‘free s<strong>of</strong>tware’’ in the sense stated by the<br />

Free S<strong>of</strong>tware Foundation, but it is impossible, as there isn’t enough good real free s<strong>of</strong>tware for<br />

Dos.<br />

The listed s<strong>of</strong>tware is meant to work on i286 and below.<br />

It is attempted to give some kind <strong>of</strong> classification about the legal condition <strong>of</strong> the s<strong>of</strong>tware presented<br />

here. The definition used might be outdated, or there might be other wrong assumption.<br />

128


The valuable DOS Freeware page 129<br />

In particular, the definition ‘‘public domain’’ means here, in most cases, that there is the source,<br />

but there is no clear license statement.<br />

Beside the URI links <strong>of</strong> some FTP services there is an additional ‘‘search link’’ that queries a<br />

FTP search engine for the same file. These additional links should be used when there are troubles<br />

with the main links.<br />

Anyone can link this document anywhere, so, there is no need to ask for it. Anyway, it is better to<br />

link to this document using at the file name .<br />

In the future, many links may disappear on this page, because <strong>of</strong> more selective choices<br />

concerning s<strong>of</strong>tware license.<br />

365.2 OS and GUI<br />

• FreeDOS 1 <br />

• FreeGEM 2 <br />

365.3 Utility<br />

archive, backup<br />

• Gzip 3 - ‘.GZ’ archive compressor and extractor. <br />

• TAR 4 - portable TAR - DOS/UNIX backup, compressor, with hardware<br />

support. <br />

• Untgz 5 - ‘.TGZ’, ‘.TAR’, ‘.GZ’, ‘.ZIP’ file extractor. <br />

• Info-ZIP 6 - ‘.ZIP’ compatible compression and extraction utility. <br />

<br />

• Restaur 7 - Replacement for Dos Restore, <br />

communication<br />

• DosFax 8 - Send a fax using Dos command line <br />

• Bgfax 9 <br />

• Rifs 10 - Disk sharing over a serial line, <br />

<br />

1<br />

FreeDOS GNU GPL<br />

2<br />

FreeGEM GNU GPL<br />

3<br />

Gzip GNU GPL<br />

4<br />

TAR (Dos) public domain<br />

5<br />

Untgz GNU GPL<br />

6<br />

Info-ZIP free s<strong>of</strong>tware with special license<br />

7<br />

Restaur cannot be sold for pr<strong>of</strong>it<br />

8<br />

DosFax public domain<br />

9<br />

Bgfax promised to become free s<strong>of</strong>tware<br />

10<br />

Rifs cannot be sold for pr<strong>of</strong>it


130 volume VIII Argomenti avanzati e accessori<br />

directory, file<br />

disk<br />

help<br />

shell<br />

system<br />

text<br />

See also:<br />

• WCD 11 - Powerful chdir for Dos and Unix <br />

• Fips 12 - Non-destructive splitting <strong>of</strong> hard disk partitions <br />

• Part 13 - MBR partition manager <br />

• NG_clone 14 - Norton Guides clone <br />

<br />

• DC 15 - The Dos Controller - A Norton Commander clone <br />

• Cmos 16 - Save/Restore extended C/MOS <br />

• KGB 17 - Utility to monitor some Dos functions and reporting into a<br />

log file <br />

• Vim 18 - VI improved, a small text editor that can handle very big files with low<br />

RAM <br />

• Richard L. Green, Free s<strong>of</strong>tware for Dos<br />

<br />

365.4 Network<br />

packet driver<br />

11<br />

WCD GNU GPL<br />

12<br />

Fips GNU GPL<br />

13<br />

Part public domain<br />

14<br />

NG_clone public domain<br />

15<br />

DC public domain (no license at all, and no sources)<br />

16<br />

Cmos public domain<br />

17<br />

KGB public domain<br />

18<br />

Vim free s<strong>of</strong>tware with special license


The valuable DOS Freeware page 131<br />

TCP/IP<br />

See also:<br />

• PC/TCP Packet Driver Collection 19 <br />

• WATTCP 20 - TCP/IP library routines <br />

• DOS PPPD 21 - Dos port <strong>of</strong> Linux PPP packet driver <br />

• Comring 22 - packet driver emulating ethernet over serial link(s) <br />

• WATTCP apps 23 - some common client application using WATTCP library <br />

• MiniTelnet 24 - TELNET client <br />

• Bobcat 25 - Text based web browser (derived<br />

from DosLynx, )<br />

• PCroute 26 - IP routing program for IBM PC <br />

• PPRD 27 - Turn a dedicated PC (XT/AT) into a LPD server <br />

<br />

• NCSA Telnet 28 - Telnet, Ftp,... NCSA <br />

<br />

• NOS (KA9Q) 29 - A complete mini TCP/IP system <br />

To use NOS you need documentation, for example the package <br />

• SSHDOS 30 - SSH client for Dos <br />

• Talk 31 - Talk client for Dos <br />

• ABC-nslookup 32 - DNS query clients for Dos <br />

• Marc S. Ressl, Dos Internet Pages<br />

<br />

• Smash-Co Communications, TCP/IP for MS-DOS<br />

<br />

19<br />

Crynwr packet driver collection GNU GPL<br />

20<br />

WATTCP free <strong>of</strong> charge library<br />

21<br />

DOS PPPD mixed licenses<br />

22<br />

Comring GNU GPL<br />

23<br />

WATTCP apps cannot be sold<br />

24<br />

MiniTelnet free s<strong>of</strong>tware with a special license<br />

25<br />

Bobcat GNU GPL<br />

26<br />

PCroute cannot distribute modifications<br />

27<br />

PPRD s<strong>of</strong>tware non libero: licenza Artistic<br />

28<br />

NCSA Telnet public domain<br />

29<br />

NOS public domain<br />

30<br />

SSHDOS GNU GPL<br />

31<br />

Talk GNU GPL<br />

32<br />

ABC-nslookup UCB BSD


132 volume VIII Argomenti avanzati e accessori<br />

• The U-M S<strong>of</strong>tware Archive<br />

<br />

<br />

365.5 Compilers<br />

assembler<br />

batch<br />

C/C++<br />

Perl<br />

Rexx<br />

xBase<br />

See also:<br />

See the FreeDOS project () for assembler compilers.<br />

• BAT2EXE 33 - Compile batch files for speed <br />

See the FreeDOS project () for C and C++ compilers.<br />

Perl 34 - Practical Extraction Report Language <br />

• BREXX 35 - Rexx interpreter for Dos/Unix <br />

• nanoBase 36 - Mini, but nearly complete xBase <br />

• David Muir Sham<strong>of</strong>f, Catalog <strong>of</strong> free compilers and interpreters<br />

<br />

365.6 Typesetting<br />

• Nro 37 - A Nr<strong>of</strong>f implementation for Dos <br />

<br />

• Ghostscript 38 - ‘‘GNU’’ original edition - PostScript previewing, conversion, and printing<br />

<br />

• emTeX 39 - TeX-LaTeX distribution for Dos <br />

33 BAT2EXE public domain<br />

34 Perl GNU GPL or Artistic<br />

35 BREXX public domain<br />

36 nanoBase GNU GPL<br />

37 Nro public domain<br />

38 Ghostscript GNU GPL<br />

39 emTeX LPPL but some files have different conditions


The valuable DOS Freeware page 133<br />

365.7 More Dos s<strong>of</strong>tware sources<br />

• <br />

• <br />

365.8 Search engines<br />

• <br />

• <br />

Appunti di informatica libera 2003.06.29 --- Copyright © 2000-2003 Daniele Giacomini -- daniele @ swlibero.org


Clean the Clipper 5.2<br />

Chapter 366<br />

A different way to program using Clipper 5.2 without commands, that is, without the file<br />

‘STD.CH’.<br />

This material appeared originally at ‘http://www.geocities.com/SiliconValley/<br />

7737/clipper52clean.html’, in 1996. Now it is incorporated inside the Italian document<br />

‘‘Appunti di informatica libera’’, and might be reached at the URI .<br />

Questo materiale è apparso in origine, nel 1996, presso ‘http://www.geocities.com/<br />

SiliconValley/7737/clipper52clean.html’. Adesso viene incorporato nel documento<br />

«Appunti di informatica libera» e può essere raggiunto attraverso l’URI . L’intento dell’autore è solo quello di conservare un<br />

vecchio lavoro che potrebbe essere ancora utile, nonostante si tratti di considerazioni su un<br />

compilatore proprietario, ormai obsoleto.<br />

366.1 Step 1: try to compile with the /P parameter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135<br />

366.2 Step 2: understand well the use <strong>of</strong> code blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .136<br />

366.3 Step 3: understand the object programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137<br />

366.3.1 Classes and methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .137<br />

366.3.2 Class definition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137<br />

366.3.3 Object creation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .137<br />

366.3.4 Instantiating an object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138<br />

366.3.5 The ‘‘send’’ symbol . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138<br />

366.3.6 More about objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138<br />

366.4 Step 4: understand the get object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138<br />

366.5 Step 5: trying to stop using commands . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140<br />

366.5.1 ?/?? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141<br />

366.5.2 @...BOX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141<br />

366.5.3 @...GET . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .141<br />

366.5.4 @...SAY . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141<br />

366.5.5 @...TO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141<br />

366.5.6 APPEND . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142<br />

366.5.7 APPEND FROM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142<br />

366.5.8 CLEAR . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142<br />

366.5.9 CLOSE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142<br />

366.5.10 COMMIT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142<br />

366.5.11 CONTINUE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143<br />

366.5.12 COPY . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143<br />

366.5.13 COUNT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .143<br />

366.5.14 CREATE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144<br />

134


Clean the Clipper 5.2 135<br />

366.5.15 DEFAULT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .144<br />

366.5.16 DELETE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144<br />

366.5.17 EJECT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144<br />

366.5.18 ERASE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144<br />

366.5.19 FIND . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144<br />

366.5.20 GO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144<br />

366.5.21 INDEX ON . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144<br />

366.5.22 JOIN . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145<br />

366.5.23 KEYBOARD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145<br />

366.5.24 LABEL FORM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145<br />

366.5.25 LIST . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .145<br />

366.5.26 LOCATE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145<br />

366.5.27 PACK . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .146<br />

366.5.28 QUIT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146<br />

366.5.29 READ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146<br />

366.5.30 RECALL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .146<br />

366.5.31 REINDEX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146<br />

366.5.32 RELEASE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146<br />

366.5.33 RENAME . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147<br />

366.5.34 REPLACE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147<br />

366.5.35 REPORT FORM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147<br />

366.5.36 RESTORE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147<br />

366.5.37 RESTORE FROM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147<br />

366.5.38 RUN . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147<br />

366.5.39 SAVE SCREEN TO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148<br />

366.5.40 SAVE TO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148<br />

366.5.41 SEEK . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148<br />

366.5.42 SELECT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148<br />

366.5.43 SET . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148<br />

366.5.44 SKIP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152<br />

366.5.45 SORT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152<br />

366.5.46 STORE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152<br />

366.5.47 SUM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152<br />

366.5.48 TOTAL ON . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152<br />

366.5.49 UNLOCK . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152<br />

366.5.50 UPDATE FROM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152<br />

366.5.51 USE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152<br />

366.5.52 ZAP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153<br />

366.6 Step 6: free yourself from STD.CH - /U . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153<br />

366.7 Step 7: take control over all include files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153


136 volume VIII Argomenti avanzati e accessori<br />

Clipper 5.2, as the xBase tradition imposes, is not an ordered, clear, simple programming language.<br />

The question is: which is the right way to write a Clipper program? If the intention is not<br />

to make a xBase program, but a Clipper program, maybe it can be decided that it is better to use<br />

Clipper without commands.<br />

366.1 Step 1: try to compile with the /P parameter<br />

Supposing to compile the file ‘TEST.PRG’ this way:<br />

C:\>CLIPPER TEST.PRG /P<br />

It generates a preprocessed output file (‘test.PPO’), that is a source file without comments,<br />

where commands are translated into real Clipper instructions. That is, all the ‘#COMMAND’ substitution<br />

are executed and the translation is sent to the ‘.PPO’ file. It may be difficult to read this<br />

file the first time.<br />

366.2 Step 2: understand well the use <strong>of</strong> code blocks<br />

The code block is a small piece <strong>of</strong> executable program code that can be stored inside a variable,<br />

or can be used as a literal constant. The good <strong>of</strong> it, is that pieces <strong>of</strong> code may be sent to functions.<br />

A code block is something like a little user defined function where only a sequence <strong>of</strong> expressions<br />

(functions and/or assignments) may appear: no loops, no conditional structures.<br />

A code block may receive arguments and return a value after execution, just like a function. The<br />

syntax is the following, where curly brackets are part <strong>of</strong> the code block:<br />

{ | [argument_list] | exp_list }<br />

That is: the argument_list is optional; the exp_list may contain one or more expressions separated<br />

with a comma.<br />

For example, calling the following code block will give the string ‘‘hello world’’ as result.<br />

{ || "hello world" }<br />

The following code block requires a numeric argument and returns the number passed as argument<br />

incremented:<br />

{ | n | n+1 }<br />

The following code block requires two numeric arguments and returns the sum <strong>of</strong> the two square<br />

radix:<br />

{ | nFirst, nSecond | SQRT(nFirst) + SQRT(nSecond) }<br />

But code blocks may contain more expressions and the result <strong>of</strong> the execution <strong>of</strong> the code block<br />

is the result <strong>of</strong> the last expression. The following code block executes in sequence some functions<br />

and gives ‘‘hello world’’ as a result.<br />

{ | a, b | functionOne(a), functionTwo(b), "hello world" }<br />

To start the execution <strong>of</strong> a code block a function is used: ‘EVAL()’. For example, a code block is<br />

assigned to a variable and then executed.<br />

B := { || "hello world" }<br />

EVAL( B ) == "hello world"


Clean the Clipper 5.2 137<br />

Another example with one parameter.<br />

B := { | n | n+1 }<br />

EVAL( B, 1 ) == 2<br />

Another example with two parameters.<br />

B := { | nFirst, nSecond | SQRT(nFirst) + SQRT(nSecond) }<br />

EVAL( B, 2, 4 ) == 20<br />

And so on.<br />

366.3 Step 3: understand the object programming<br />

Clipper 5.2 do not permit to create objects, but it gives some good objects to use: ‘GET’ and<br />

‘TBROWSE’. Before starting to clean programming from commands, it is necessary to understand<br />

how to use well the Clipper objects.<br />

366.3.1 Classes and methods<br />

A class defines the structure <strong>of</strong> a ‘‘black box’’, that is a data container; a method is an action<br />

to make on a piece <strong>of</strong> data contained inside the black box. There is no way to reach the data<br />

contained inside the black box without a method.<br />

The black box can be called object.<br />

The methods may be seen as they where special functions which interact with a specific piece <strong>of</strong><br />

data contained inside the object.<br />

366.3.2 Class definition<br />

Supposing that Clipper permits to define classes (unluckily only predefined classes can be used),<br />

the hypothetical syntax could be:<br />

CLASS ClassName [FROM ParentClass]<br />

VAR Var1 [,Var2[,...VarN]]<br />

METHOD {method_definition_1} [, ...{method_definition_n} ]<br />

ENDCLASS<br />

This way, the class defines a group <strong>of</strong> variables and a group <strong>of</strong> method to use with these variables.<br />

366.3.3 Object creation<br />

The presence <strong>of</strong> classes permits to create objects: the black boxes.<br />

Variable_name := ClassName<br />

This way, a variable contains (is) an object. Please note that inside Clipper, an object may be<br />

generated also from a function, that is, a function can return an object. This way the example can<br />

be:<br />

Variable_name := classfunction( ... )<br />

The next problem is to handle this object.


138 volume VIII Argomenti avanzati e accessori<br />

366.3.4 Instantiating an object<br />

As already stated before, methods are used to handle data contained inside an object. This is said<br />

to be instantiating an object. Clipper permits also to handle directly (apparently without methods)<br />

some variables contained inside objects. These are called ‘‘Exported Instance Variables’’. So, an<br />

object can be instantiated this way:<br />

object:exported_instance_variable := new_value<br />

object:method()<br />

An exported instance variable may be read and/or modified depending on the allowed access to it;<br />

a method, inside Clipper, is something like a function with or without parameters (if parameters<br />

are present, these are usually used to modify data inside the object), that normally returns a value.<br />

366.3.5 The ‘‘send’’ symbol<br />

To instantiate an object or simply to access an exported instance variable, the ‘‘send’’ symbol<br />

(colon) is used.<br />

366.3.6 More about objects<br />

If this is not enough to understand objects inside Clipper, the following document should be read:<br />

Peter M. Freese, o:Clip - An Object Oriented Extension to Clipper 5.01 1991, CyberS<strong>of</strong>t<br />

<br />

366.4 Step 4: understand the get object<br />

What happens with a command like the following:<br />

@ nTop, nLeft GET Var<br />

A get object is created containing all the necessary information for editing the variable Var at<br />

the screen position nTop, nLeft. After that, this get object is added to a get objects array (usually<br />

called ‘GetList’). The get objects array will contain all the get objects used during a ‘READ’.<br />

So, what happens when a ‘READ’ command is encountered. The get objects array (‘GetList’) is<br />

read and the editing <strong>of</strong> all get objects is executed. After that, the get objects array is cleared.<br />

This method hides what Clipper really makes. The suggestion here is to create a ‘GET()’ function<br />

that will substitute the ‘@...GET’ command and to use the ‘READMODAL()’ function to read the get<br />

objects array. Here is an example <strong>of</strong> it:<br />

function GET( aoGet, nRow, nCol, bVar, cGetPicture,<br />

cColorString, bPreValid, bPostValid )<br />

// declare a local get object<br />

local oGet<br />

// create the get object using the function GETENV()<br />

oGet := GETENV( nRow, nCol, bVar, NIL, cGetPicture, cGetColor )<br />

// send to the get object the pre-validation condition code block (WHEN)<br />

oGet:preBlock := bPreValid


Clean the Clipper 5.2 139<br />

// send to the get object the post-validation condition code block (VALID)<br />

oGet:postBlock := bPostValid<br />

// display the get on the screen using the display() method<br />

oGet:display()<br />

// add the get object to the get objects array<br />

AADD( aoGet, oGet )<br />

return NIL<br />

• ‘aoGet’ is the get objects array (so here is explicitly passed). This get objects array is<br />

modified (grown) and there is no need to return it as inside Clipper, arrays are always<br />

passed by reference to functions.<br />

• ‘nRow’ and ‘nCol’ are the screen coordinates where the get field should appear at, as it<br />

works with the ‘@...GET’ command.<br />

• ‘bVar’ is a special code block that permits the editing <strong>of</strong> a variable. If the variable ‘Var’ is<br />

to be edited, the code block is:<br />

{ |x| iif( pcount() > 0, Var := x, Var }<br />

• ‘cGetPicture’ is the picture to use: same as the ‘@...GET’ command.<br />

• ‘cColorString’ is the color string to use: same as the ‘@...GET’ command.<br />

• ‘bPreValid’ is a code block containing the condition that must be valid before the cursor<br />

can reach this get field. It is equivalent to the ‘WHEN’ condition used with the ‘@...GET’<br />

command, but it must be converted into a code block. For example, if the condition is<br />

‘A > B’, the code block is ‘{|| A > B}’<br />

• ‘bPostValid’ is a code block containing the condition that must be valid before the cursor<br />

can leave this get field. It is equivalent to the ‘VALID’ condition used with the ‘@...GET’<br />

command, but it must be converted into a code block. For example, if the condition is<br />

‘A > B’, the code block is ‘{|| A > B}’<br />

If there is a get function like the above one, screen I/O may be performed like the following<br />

example:<br />

function do_some_editing()<br />

// define a variable to use as a get objects array<br />

// and initialise it to the empty array<br />

local aoGet := {}<br />

...<br />

...<br />

// add a new get object to the get objects array<br />

get(;<br />

aoGet,;<br />

10, 10,;<br />

{ |x| iif( pcount() > 0, myVariable := x, myVariable },;<br />

"@s30@",;<br />

"gb+/b, n/w, n, n, w/n",;<br />

{ || .T. },;<br />

{ || .T. };<br />

)


140 volume VIII Argomenti avanzati e accessori<br />

...<br />

// read the get objects array<br />

readmodal( aoGet )<br />

// clear the get objects array<br />

aoGet := {}<br />

...<br />

return ...<br />

If the function ‘GET()’ is not liked, the above I/O may be done as it follows:<br />

function do_some_editing()<br />

// define a variable to use as a get object<br />

local aoGet<br />

// define a variable to use as a get objects array<br />

// and initialise it to the empty array<br />

local aoGet := {}<br />

...<br />

...<br />

// add a new get object to the get objects array<br />

oGet :=;<br />

GETENV(;<br />

10, 10,;<br />

{ |x| iif( pcount() > 0, myVariable := x, myVariable },;<br />

NIL,;<br />

"@s30@",;<br />

"gb+/b, n/w, n, n, w/n",;<br />

)<br />

AADD( aoGet, oGet )<br />

...<br />

// read the get objects array<br />

readmodal( aoGet )<br />

// clear the get objects array<br />

aoGet := {}<br />

...<br />

return ...<br />

366.5 Step 5: trying to stop using commands<br />

To stop using commands, it is important to understand how commands are or may be translated<br />

into functions. Sometimes Clipper uses some undocumented functions: these are functions that<br />

start with a underline.


Clean the Clipper 5.2 141<br />

366.5.1 ?/??<br />

? [exp_list]<br />

qout([exp_list])<br />

?? [exp_list]<br />

qqout([exp_list])<br />

366.5.2 @...BOX<br />

@ nTop, nLeft, nBottom, nRight BOX cnBoxString [COLOR cColorString]<br />

dispbox(nTop, nLeft, nBottom, nRight, [cnBoxString], [cColorString])<br />

366.5.3 @...GET<br />

@ nTop, nLeft GET Var [PICTURE cGetPicture] [COLOR cColorString] [WHEN lPreExpression] ←↪<br />

↩→[VALID lPostExpression]<br />

setpos(nTop, nLeft)<br />

aadd( GetList, _GET_( Var, "Var", cGetPicture, [{|| lPostExpression}],←↪<br />

↩→[{|| lPreExpression}] ):display() ) atail(GetList):colorDisp(cColorString)<br />

This is the command substitution made automatically, but it shouldn’t be used to make clean<br />

programs. The step 4 (366.1) suggests to create a get function.<br />

366.5.4 @...SAY<br />

@ nTop, nLeft SAY exp [COLOR cColorString]<br />

devpos(nTop, nLeft)<br />

devout(exp [, cColorString])<br />

@ nTop, nLeft SAY exp PICTURE cSayPicture [COLOR cColorString]<br />

devpos(nTop, nLeft)<br />

devoutpic(exp, cSayPicture, [cColorString])<br />

366.5.5 @...TO<br />

@ nTop, nLeft TO nBottom, nRight DOUBLE [COLOR cColorString]<br />

dispbox(nTop, nLeft, nBottom, nRight, 2 [,cColorString])<br />

@ nTop, nLeft TO nBottom, nRight [COLOR cColorString]<br />

dispbox(nTop, nLeft, nBottom, nRight, 1 [,cColorString])<br />

@ nTop, nLeft CLEAR [TO nBottom, nRight]<br />

scroll([nTop], [nLeft], [nBottom, nRight])<br />

setpos(nRow, nCol)


142 volume VIII Argomenti avanzati e accessori<br />

366.5.6 APPEND<br />

APPEND BLANK<br />

dbappend()<br />

366.5.7 APPEND FROM<br />

APPEND FROM xcFile [FIELDS idField_list] [scope] [WHILE lCondition]←↪<br />

↩→[FOR lCondition] [VIA xcDriver]<br />

__dbApp( cFileName, [acFields], [bForCondition], [bWhileCondition], [nNextRecords],←↪<br />

↩→[nRecord], [lRest], [cDriver] )<br />

APPEND FROM xcFile [FIELDS idField_list] [scope] [WHILE lCondition] [FOR lCondition]←↪<br />

↩→DELIMITED xcDelimiter<br />

__dbDelim( .f., cFileName, [cDelimiter], [acFields], [bForCondition], [bWhileCondition]<br />

,←↪<br />

↩→[nNextRecords], [nRecord], [lRest] )<br />

APPEND FROM xcFile [FIELDS idField_list] [scope] [WHILE lCondition]←↪<br />

↩→[FOR lCondition] SDF<br />

__dbSDF( .f., cFileName, [acFields], [bForCondition], [bWhileCondition], [nNextRecords]<br />

,←↪<br />

↩→[nRecord], [lRest] )<br />

366.5.8 CLEAR<br />

CLEAR<br />

Scroll()<br />

SetPos(0,0)<br />

ReadKill(.T.)<br />

GetList := {}<br />

CLEAR GETS<br />

ReadKill(.T.)<br />

GetList := {}<br />

CLEAR SCREEN | CLS<br />

Scroll()<br />

SetPos(0,0)<br />

366.5.9 CLOSE<br />

CLOSE<br />

dbCloseArea()<br />

CLOSE idAlias<br />

idAlias->( dbCloseArea() )<br />

CLOSE ALTERNATE<br />

Set(19, "")<br />

CLOSE DATABASES<br />

dbCloseAll()<br />

CLOSE INDEXES<br />

dbClear<strong>Index</strong>()


Clean the Clipper 5.2 143<br />

366.5.10 COMMIT<br />

COMMIT<br />

dbCommitAll()<br />

366.5.11 CONTINUE<br />

CONTINUE<br />

__dbContinue()<br />

366.5.12 COPY<br />

COPY FILE xcSourceFile TO xcTargetFile|xcDevice<br />

__CopyFile( cSourceFile, cTargetFile|cDevice )<br />

COPY STRUCTURE [FIELDS idField_list] TO xcDatabase<br />

__dbCopyStruct( cDatabase, [acFields] )<br />

COPY STRUCTURE EXTENDED TO xcExtendedDatabase<br />

__dbCopyXStruct( cExtendedDatabase )<br />

COPY TO xcFile [FIELDS idField_list] [scope] [WHILE lCondition]←↪<br />

↩→[FOR lCondition] [VIA xcDriver]<br />

__dbCopy( cFileName, [acFields], [bForCondition], [bWhileCondition], [nNextRecords],←↪<br />

↩→[nRecord], [lRest], [cDriver] )<br />

COPY TO xcFile [FIELDS idField_list] [scope] [WHILE lCondition] [FOR lCondition]←↪<br />

↩→DELIMITED xcDelimiter<br />

__dbDelim( .t., cFileName, [cDelimiter], [acFields], [bForCondition], [bWhileCondition]<br />

,←↪<br />

↩→[nNextRecords], [nRecord], [lRest] )<br />

COPY TO xcFile [FIELDS idField_list] [scope] [WHILE lCondition]←↪<br />

↩→[FOR lCondition] SDF<br />

__dbSDF( .t., cFileName, [acFields], [bForCondition], [bWhileCondition], [nNextRecords]<br />

,←↪<br />

↩→[nRecord], [lRest] )<br />

366.5.13 COUNT<br />

COUNT TO idVar [FOR lForCondition] [WHILE lWhileCondition] [NEXT nNextRecords]←↪<br />

↩→[RECORD nRecord] [REST] [ALL]<br />

dbeval( {||idVar:=idVar+1}, {||lForCondition}, {||lWhileCondition},←↪<br />

↩→nNextRecords, nRecord, lRest )


144 volume VIII Argomenti avanzati e accessori<br />

366.5.14 CREATE<br />

CREATE xcDatabase FROM xcExtendedDatabase [NEW] [ALIAS cAlias] [VIA cDriver]<br />

__dbCreate( cDatabase, cExtendedDatabase, [cDriver], [lNew], [cAlias] )<br />

366.5.15 DEFAULT<br />

DEFAULT xVar TO xDefaultValue<br />

if xVar == NIL<br />

xVar := xDefaultValue<br />

end<br />

366.5.16 DELETE<br />

DELETE<br />

dbDelete()<br />

DELETE [FOR lForCondition] [WHILE lWhileCondition] [NEXT nNextRecords]←↪<br />

↩→[RECORD nRecord] [REST] [ALL]<br />

dbeval( {||dbDelete()}, {||lForCondition}, {||lWhileCondition},←↪<br />

↩→nNextRecords, nRecord, lRest )<br />

DELETE FILE xcFile<br />

ferase( cFile )<br />

366.5.17 EJECT<br />

EJECT<br />

qqout( chr(13) )<br />

366.5.18 ERASE<br />

ERASE xcFile<br />

ferase( cFile )<br />

366.5.19 FIND<br />

FIND xcSearchString<br />

dbSeek( cSearchString )<br />

366.5.20 GO<br />

GO[TO] nRecord<br />

dbgoto(nRecord)<br />

GO[TO] BOTTOM<br />

dbGoBottom()<br />

GO[TO] TOP<br />

dbgotop()


Clean the Clipper 5.2 145<br />

366.5.21 INDEX ON<br />

INDEX ON expKey TO xc<strong>Index</strong>Name [UNIQUE] [FOR lForCondition]←↪<br />

↩→[WHILE lWhileCondition] [[EVAL lEvalCondition] [EVERY nRecords]] [ASCENDING|<br />

DESCENDING]<br />

ordCondSet( [cForCondition], [bForCondition], , [bWhileCondition],←↪<br />

↩→[bEvalCondition], [nRecords], RECNO(), , , , lDescending )<br />

ordCreate( c<strong>Index</strong>Name, , cExpKey, bExpKey, lUnique )<br />

366.5.22 JOIN<br />

JOIN WITH xcAlias TO xcDatabase [FOR lCondition] [FIELDS idField_list]<br />

__dbJoin( cAlias, cDatabase, [acFields], [bForCondition] )<br />

366.5.23 KEYBOARD<br />

KEYBOARD cString<br />

__Keyboard( [cString] ) --> NIL<br />

366.5.24 LABEL FORM<br />

LABEL FORM xcLabel [TO PRINTER] [TO FILE xcFile] [NOCONSOLE] [scope]←↪<br />

↩→[WHILE lCondition] [FOR lCondition] [SAMPLE]<br />

__LabelForm( cLabel, [lToPrinter], [cFile], [lNoConsole],←↪<br />

↩→[bForCondition], [bWhileCondition], [nNextRecords], [nRecord],←↪<br />

↩→[lRest], [lSample] )<br />

366.5.25 LIST<br />

LIST exp_list [TO PRINTER] [TO FILE xcFile] [scope]←↪<br />

↩→[WHILE lCondition] [FOR lCondition] [OFF]<br />

__dbList( [lToDisplay], abListColumns, [lAll], [bForCondition], [bWhileCondition],←↪<br />

↩→[nNextRecords], [nRecord], [lRest], [lToPrinter], [cFileName] )<br />

366.5.26 LOCATE<br />

LOCATE [scope] FOR lCondition [WHILE lCondition]<br />

__dbLocate( [bForCondition], [bWhileCondition], [nNextRecords], [nRecord], [lRest] )


146 volume VIII Argomenti avanzati e accessori<br />

366.5.27 PACK<br />

PACK<br />

__dbPack()<br />

366.5.28 QUIT<br />

QUIT<br />

__Quit()<br />

366.5.29 READ<br />

READ<br />

ReadModal(GetList)<br />

GetList := {}<br />

READ SAVE<br />

ReadModal(GetList)<br />

366.5.30 RECALL<br />

RECALL<br />

dbRecall()<br />

RECALL [FOR lForCondition] [WHILE lWhileCondition] [NEXT nNextRecords]←↪<br />

↩→[RECORD nRecord] [REST] [ALL]<br />

dbeval( {||dbRecall()}, {||lForCondition}, {||lWhileCondition},←↪<br />

↩→nNextRecords, nRecord, lRest )<br />

366.5.31 REINDEX<br />

REINDEX [EVAL lEvalCondition] [EVERY nRecords]<br />

ordCondSet(, , , , [bEvalCondition], [nRecords], , , , , , , )<br />

ordListRebuild()<br />

366.5.32 RELEASE<br />

RELEASE idMemvar<br />

__MXRelease( "idMemvar" )<br />

RELEASE ALL<br />

__MRelease("*", .t.)<br />

RELEASE ALL LIKE skeleton<br />

__MRelease( "skeleton", .t. )<br />

RELEASE ALL EXCEPT skeleton<br />

__MRelease( "skeleton", .F. )


Clean the Clipper 5.2 147<br />

366.5.33 RENAME<br />

RENAME xcOldFile TO xcNewFile<br />

frename( cOldFile, cNewFile )<br />

366.5.34 REPLACE<br />

REPLACE idField1 WITH exp1 [, idField2 WITH exp2...]←↪<br />

↩→[FOR lForCondition] [WHILE lWhileCondition] [NEXT nNextRecords]←↪<br />

↩→[RECORD nRecord] [REST] [ALL]<br />

dbeval( {|| idField1 := exp1 [, idField2 := exp2...]},←↪<br />

↩→{||lForCondition}, {||lWhileCondition}, nNextRecords,←↪<br />

↩→nRecord, lRest )<br />

REPLACE idField1 WITH exp1<br />

idField1 := exp1<br />

366.5.35 REPORT FORM<br />

REPORT FORM xcReport [TO PRINTER] [TO FILE xcFile] [NOCONSOLE] [scope]←↪<br />

↩→[WHILE lCondition] [FOR lCondition] [PLAIN | HEADING cHeading] [NOEJECT] ←↪<br />

↩→[SUMMARY]<br />

__ReportForm( cForm, [lToPrinter], [cToFile], [lNoConsole], [bForCondition], ←↪<br />

↩→[bWhileCondition], [nNext], [nRecord], [lRest], [lPlain], [cbHeading], ←↪<br />

↩→[lBeforeEject], [lSummary] )<br />

366.5.36 RESTORE<br />

RESTORE SCREEN FROM cScreen<br />

restscreen( 0, 0, Maxrow(), Maxcol(), cScreen )<br />

366.5.37 RESTORE FROM<br />

RESTORE FROM xcMemFile [ADDITIVE]<br />

__MRestore( cMemFileName, [lAdditive] )<br />

366.5.38 RUN<br />

RUN xcCommandLine<br />

__Run( cCommand )


148 volume VIII Argomenti avanzati e accessori<br />

366.5.39 SAVE SCREEN TO<br />

SAVE SCREEN TO cScreen<br />

cScreen := savescreen( 0, 0, maxrow(), maxcol() )<br />

366.5.40 SAVE TO<br />

SAVE TO xcMemFile [ALL [LIKE|EXCEPT skeleton]]<br />

_MSave( cMemFileName, [cSkeleton], [lLike] )<br />

366.5.41 SEEK<br />

SEEK expSearch [SOFTSEEK]<br />

dbSeek( expSearch [, lS<strong>of</strong>tSeek] )<br />

366.5.42 SELECT<br />

SELECT xnWorkArea | idAlias<br />

dbSelectArea( nWorkArea | cIdAlias )<br />

366.5.43 SET<br />

Most <strong>of</strong> the ‘SET...’ commands are translated into the ‘SET()’ function that distinguishes different<br />

modes depending on a number. As this number is difficult to handle during programming<br />

(essentially because it is difficult to remember the meaning <strong>of</strong> it), Clipper <strong>of</strong>fers the ‘SET.CH’<br />

include file that helps with manifest constants.<br />

#define _SET_EXACT 1<br />

#define _SET_FIXED 2<br />

#define _SET_DECIMALS 3<br />

#define _SET_DATEFORMAT 4<br />

#define _SET_EPOCH 5<br />

#define _SET_PATH 6<br />

#define _SET_DEFAULT 7<br />

#define _SET_EXCLUSIVE 8<br />

#define _SET_SOFTSEEK 9<br />

#define _SET_UNIQUE 10<br />

#define _SET_DELETED 11<br />

#define _SET_CANCEL 12<br />

#define _SET_DEBUG 13<br />

#define _SET_TYPEAHEAD 14<br />

#define _SET_COLOR 15<br />

#define _SET_CURSOR 16<br />

#define _SET_CONSOLE 17<br />

#define _SET_ALTERNATE 18<br />

#define _SET_ALTFILE 19<br />

#define _SET_DEVICE 20<br />

#define _SET_EXTRA 21<br />

#define _SET_EXTRAFILE 22<br />

#define _SET_PRINTER 23


Clean the Clipper 5.2 149<br />

#define _SET_PRINTFILE 24<br />

#define _SET_MARGIN 25<br />

#define _SET_BELL 26<br />

#define _SET_CONFIRM 27<br />

#define _SET_ESCAPE 28<br />

#define _SET_INSERT 29<br />

#define _SET_EXIT 30<br />

#define _SET_INTENSITY 31<br />

#define _SET_SCOREBOARD 32<br />

#define _SET_DELIMITERS 33<br />

#define _SET_DELIMCHARS 34<br />

#define _SET_WRAP 35<br />

#define _SET_MESSAGE 36<br />

#define _SET_MCENTER 37<br />

#define _SET_SCROLLBREAK 38<br />

SET ALTERNATE TO xcFile [ADDITIVE]<br />

Set( _SET_ALTFILE, cFile, lAdditive )<br />

SET ALTERNATE ON | OFF | xlToggle<br />

Set( _SET_ALTERNATE, "ON" | "OFF" | lToggle )<br />

SET BELL ON | OFF | xlToggle<br />

Set( _SET_BELL, "ON" | "OFF" | lToggle )<br />

SET COLOR | COLOUR TO (cColorString)<br />

SetColor( cColorString )<br />

SET CONFIRM ON | OFF | xlToggle<br />

Set( _SET_CONFIRM, "ON" | "OFF" | lToggle )<br />

SET CONSOLE ON | OFF | xlToggle<br />

Set( _SET_CONSOLE, "ON" | "OFF" | lToggle )<br />

SET CURSOR ON | OFF | xlToggle<br />

SetCursor( 1 | 0 | iif( lToggle, 1, 0 ) )<br />

SET DATE FORMAT [TO] cDateFormat<br />

Set( _SET_DATEFORMAT, cDateFormat )<br />

SET DECIMALS TO<br />

Set( _SET_DECIMALS, 0 )<br />

SET DECIMALS TO nDecimals<br />

Set( _SET_DECIMALS, nDecimals )<br />

SET DEFAULT TO<br />

Set( _SET_DEFAULT, "" )<br />

SET DEFAULT TO xcPathspec<br />

Set( _SET_DEFAULT, cPathspec )<br />

SET DELETED ON | OFF | xlToggle<br />

Set( _SET_DELETED, "ON" | "OFF" | lToggle )


150 volume VIII Argomenti avanzati e accessori<br />

SET DELIMITERS ON | OFF | xlToggle<br />

Set( _SET_DELIMITERS, "ON" | "OFF" | lToggle )<br />

SET DELIMITERS TO [DEFAULT]<br />

Set( _SET_DELIMCHARS, "::" )<br />

SET DELIMITERS TO cDelimiters<br />

Set( _SET_DELIMCHARS, cDelimiters )<br />

SET DEVICE TO SCREEN | PRINTER<br />

Set( _SET_DEVICE, "SCREEN" | "PRINTER" )<br />

SET EPOCH TO nYear<br />

Set( _SET_EPOCH, nYear )<br />

SET ESCAPE ON | OFF | xlToggle<br />

Set( _SET_ESCAPE, "ON" | "OFF" | lToggle )<br />

SET EXACT ON | OFF | xlToggle<br />

Set( _SET_EXACT, "ON" | "OFF" | lToggle )<br />

SET EXCLUSIVE ON | OFF | xlToggle<br />

Set( _SET_EXCLUSIVE, "ON" | "OFF" | lToggle )<br />

SET FILTER TO<br />

dbclearfilter()<br />

SET FILTER TO lCondition<br />

dbsetfilter( bCondition, cCondition )<br />

SET FIXED ON | OFF | xlToggle<br />

Set( _SET_FIXED, "ON" | "OFF" | lToggle )<br />

SET FUNCTION nFunctionKey TO cString<br />

__SetFunction( nFunctionKey, cString )<br />

SET INDEX TO [xc<strong>Index</strong> [, xc<strong>Index</strong>1... ] ]<br />

ordListClear()<br />

ordListAdd( c<strong>Index</strong> )<br />

ordListAdd( c<strong>Index</strong>1 )<br />

...<br />

SET INTENSITY ON | OFF | xlToggle<br />

Set( _SET_INTENSITY, "ON" | "OFF" | lToggle )<br />

SET KEY nInkeyCode [TO]<br />

SetKey( nInkeyCode, NIL )<br />

SET KEY nInkeyCode TO [idProcedure]<br />

SetKey( nInkeyCode, { |p, l, v| idProcedure(p, l, v)} )<br />

SET MARGIN TO<br />

Set( _SET_MARGIN, 0 )<br />

SET MARGIN TO [nPageOffset]<br />

Set( _SET_MARGIN, nPageOffset )


Clean the Clipper 5.2 151<br />

SET MESSAGE TO<br />

Set( _SET_MESSAGE, 0 )<br />

Set( _SET_MCENTER, .F. )<br />

SET MESSAGE TO [nRow [CENTER | CENTRE]]<br />

Set( _SET_MESSAGE, nRow )<br />

Set( _SET_MCENTER, lCenter )<br />

SET ORDER TO [n<strong>Index</strong>]<br />

ordSetFocus( n<strong>Index</strong> )<br />

SET PATH TO<br />

Set( _SET_PATH, "" )<br />

SET PATH TO [xcPathspec [, cPathspec1... ] ]<br />

Set( _SET_PATH, cPathspec [, cPathspec1... ] )<br />

SET PRINTER ON | OFF | xlToggle<br />

Set( _SET_PRINTER, "ON" | "OFF" | lToggle )<br />

SET PRINTER TO<br />

Set( _SET_PRINTFILE, "" )<br />

SET PRINTER TO [xcDevice|xcFile [ADDITIVE]]<br />

Set( _SET_PRINTFILE, cDevice|cFile, lAdditive )<br />

SET RELATION TO<br />

dbclearrelation()<br />

SET RELATION TO [expKey1 INTO xcAlias1] [, [TO] expKey2 INTO xcAlias2...]<br />

dbClearRel()<br />

dbSetRelation( cAlias1, {||expKey1}, ["expKey1"] )<br />

dbSetRelation( cAlias2, {||expKey2}, ["expKey1"] )<br />

SET RELATION TO [expKey1 INTO xcAlias1]←↪<br />

↩→[, [TO] expKey2 INTO xcAlias2...] ADDITIVE<br />

dbSetRelation( cAlias1, {||expKey1}, ["expKey1"] )<br />

dbSetRelation( cAlias2, {||expKey2}, ["expKey1"] )<br />

SET SCOREBOARD ON | OFF | xlToggle<br />

Set( _SET_SCOREBOARD, "ON" | "OFF" | lToggle )<br />

SET SOFTSEEK ON | OFF | xlToggle<br />

Set( _SET_SOFTSEEK, "ON" | "OFF" | lToggle )<br />

SET TYPEAHEAD TO nKeyboardSise<br />

Set( _SET_TYPEAHEAD, nKeyboardSise )<br />

SET UNIQUE ON | OFF | xlToggle<br />

Set( _SET_UNIQUE, "ON" | "OFF" | lToggle )<br />

SET WRAP ON | OFF | xlToggle<br />

Set( _SET_WRAP, "ON" | "OFF" | lToggle )


152 volume VIII Argomenti avanzati e accessori<br />

366.5.44 SKIP<br />

SKIP [nRecords] [ALIAS idAlias|nWorkArea]<br />

[idAlias|nWorkArea -> ]( dbSkip([nRecords]) )<br />

366.5.45 SORT<br />

SORT TO xcDatabase ON idField1 [/[A|D][C]] [, idField2 [/[A|D][C]] ...]←↪<br />

↩→[scope] [WHILE lCondition] [FOR lCondition]<br />

__dbSort( cDatabase, [acFields], [bForCondition], [bWhileCondition],←↪<br />

↩→[nNextRecords], [nRecord], [lRest] )<br />

366.5.46 STORE<br />

STORE value TO variable<br />

variable := value<br />

366.5.47 SUM<br />

SUM nExp1 [, nExp2...] TO idVar1 [, idVar2...] [FOR lForCondition]←↪<br />

↩→[WHILE lWhileCondition] [NEXT nNextRecords] [RECORD nRecord] [REST] [ALL]<br />

dbeval( {||idVar1:=idVar1+nExp1 [, idVar2:=idVar2+nExp2...] },←↪<br />

↩→{||lForCondition}, {||lWhileCondition}, nNextRecords, nRecord, lRest )<br />

366.5.48 TOTAL ON<br />

TOTAL ON expKey [FIELDS idField_list] TO xcDatabase [scope]←↪<br />

↩→[WHILE lCondition] [FOR lCondition]<br />

__dbTotal( cDatabase, bKey, [acFields], [bForCondition], [bWhileCondition],←↪<br />

↩→[nNextRecords], [nRecord], [lRest] )<br />

366.5.49 UNLOCK<br />

UNLOCK<br />

dbUnlock()<br />

UNLOCK ALL<br />

dbUnlockAll()<br />

366.5.50 UPDATE FROM<br />

UPDATE FROM xcAlias ON expKey [RANDOM] REPLACE idField1←↪<br />

↩→WITH exp [, idField2 WITH exp ...]<br />

__dbUpdate( cAlias, bKey, [lRandom], [bReplacement] )<br />

Example:


Clean the Clipper 5.2 153<br />

__dbUpdate( "INVOICE", {|| LAST}, .T.,; {|| FIELD->TOTAL1 := ←↪<br />

↩→INVOICE->SUM1,; FIELD->TOTAL2 := INVOICE->SUM2 } )<br />

366.5.51 USE<br />

USE<br />

dbclosearea()<br />

USE [xcDatabase]←↪<br />

↩→[INDEX xc<strong>Index</strong>1 [, xc<strong>Index</strong>2...] [ALIAS xcAlias] [EXCLUSIVE|SHARED] [NEW] ←↪<br />

↩→[READONLY] [VIA cDriver]]<br />

dbUseArea( [lNewArea], [cDriver], cDatabase, [cAlias], [lShared], [lReadOnly] )<br />

[dbSet<strong>Index</strong>( c<strong>Index</strong>1 )]<br />

[dbSet<strong>Index</strong>( c<strong>Index</strong>2 )]<br />

...<br />

366.5.52 ZAP<br />

ZAP<br />

dbZap()<br />

366.6 Step 6: free yourself from STD.CH - /U<br />

Now that no command is used, the standard include file ‘STD.CH’ is no more necessary. Clipper<br />

uses ‘STD.CH’ automatically, unless specified differently. Just compile this way:<br />

C:>CLIPPER TEST.PRG /U<br />

366.7 Step 7: take control over all include files<br />

Clipper comes with so many include files (‘ * .CH’). To avoid confusion, a single ‘STANDARD.CH’<br />

file containing all what is needed for the application may be prepared. At least, it is necessary the<br />

following.<br />

*=================================================================<br />

* DISPBOX()<br />

*=================================================================<br />

* Single-line box<br />

#define BOX_SINGLE;<br />

(;<br />

CHR(218) +;<br />

CHR(196) +;<br />

CHR(191) +;<br />

CHR(179) +;<br />

CHR(217) +;<br />

CHR(196) +;<br />

CHR(192) +;<br />

CHR(179);<br />

)<br />

* Double-line box


154 volume VIII Argomenti avanzati e accessori<br />

#define BOX_DOUBLE;<br />

(;<br />

CHR(201) +;<br />

CHR(205) +;<br />

CHR(187) +;<br />

CHR(186) +;<br />

CHR(188) +;<br />

CHR(205) +;<br />

CHR(200) +;<br />

CHR(186);<br />

)<br />

* Single-line top, double-line sides<br />

#define BOX_SINGLE_DOUBLE;<br />

(;<br />

CHR(214) +;<br />

CHR(196) +;<br />

CHR(183) +;<br />

CHR(186) +;<br />

CHR(189) +;<br />

CHR(196) +;<br />

CHR(211) +;<br />

CHR(186);<br />

)<br />

* Double-line top, single-line sides<br />

#define BOX_DOUBLE_SINGLE;<br />

(;<br />

CHR(213) +;<br />

CHR(205) +;<br />

CHR(184) +;<br />

CHR(179) +;<br />

CHR(190) +;<br />

CHR(205) +;<br />

CHR(212) +;<br />

CHR(179);<br />

)<br />

*=================================================================<br />

* ERRORS<br />

*=================================================================<br />

* Severity levels (e:severity)<br />

#define ERROR_SEVERITY_WHOCARES 0<br />

#define ERROR_SEVERITY_WARNING 1<br />

#define ERROR_SEVERITY_ERROR 2<br />

#define ERROR_SEVERITY_CATASTROPHIC 3<br />

* Generic error codes (e:genCode)<br />

#define ERROR_GENERIC_ARG 1<br />

#define ERROR_GENERIC_BOUND 2<br />

#define ERROR_GENERIC_STROVERFLOW 3<br />

#define ERROR_GENERIC_NUMOVERFLOW 4<br />

#define ERROR_GENERIC_ZERODIV 5<br />

#define ERROR_GENERIC_NUMERR 6<br />

#define ERROR_GENERIC_SYNTAX 7<br />

#define ERROR_GENERIC_COMPLEXITY 8<br />

#define ERROR_GENERIC_MEM 11<br />

#define ERROR_GENERIC_NOFUNC 12<br />

#define ERROR_GENERIC_NOMETHOD 13<br />

#define ERROR_GENERIC_NOVAR 14<br />

#define ERROR_GENERIC_NOALIAS 15


Clean the Clipper 5.2 155<br />

#define ERROR_GENERIC_NOVARMETHOD 16<br />

#define ERROR_GENERIC_BADALIAS 17<br />

#define ERROR_GENERIC_DUPALIAS 18<br />

#define ERROR_GENERIC_CREATE 20<br />

#define ERROR_GENERIC_OPEN 21<br />

#define ERROR_GENERIC_CLOSE 22<br />

#define ERROR_GENERIC_READ 23<br />

#define ERROR_GENERIC_WRITE 24<br />

#define ERROR_GENERIC_PRINT 25<br />

#define ERROR_GENERIC_UNSUPPORTED 30<br />

#define ERROR_GENERIC_LIMIT 31<br />

#define ERROR_GENERIC_CORRUPTION 32<br />

#define ERROR_GENERIC_DATATYPE 33<br />

#define ERROR_GENERIC_DATAWIDTH 34<br />

#define ERROR_GENERIC_NOTABLE 35<br />

#define ERROR_GENERIC_NOORDER 36<br />

#define ERROR_GENERIC_SHARED 37<br />

#define ERROR_GENERIC_UNLOCKED 38<br />

#define ERROR_GENERIC_READONLY 39<br />

#define ERROR_GENERIC_APPENDLOCK 40<br />

#define ERROR_GENERIC_LOCK 41<br />

*=================================================================<br />

* INKEY()<br />

*=================================================================<br />

#define K_UP 5 // Up arrow, Ctrl-E<br />

#define K_DOWN 24 // Down arrow, Ctrl-X<br />

#define K_LEFT 19 // Left arrow, Ctrl-S<br />

#define K_RIGHT 4 // Right arrow, Ctrl-D<br />

#define K_HOME 1 // Home, Ctrl-A<br />

#define K_END 6 // End, Ctrl-F<br />

#define K_PGUP 18 // PgUp, Ctrl-R<br />

#define K_PGDN 3 // PgDn, Ctrl-C<br />

#define K_CTRL_UP 397 // * Ctrl-Up arrow<br />

#define K_CTRL_DOWN 401 // * Ctrl-Down arrow<br />

#define K_CTRL_LEFT 26 // Ctrl-Left arrow, Ctrl-Z<br />

#define K_CTRL_RIGHT 2 // Ctrl-Right arrow, Ctrl-B<br />

#define K_CTRL_HOME 29 // Ctrl-Home, Ctrl-<br />

#define K_CTRL_END 23 // Ctrl-End, Ctrl-W<br />

#define K_CTRL_PGUP 31 // Ctrl-PgUp, Ctrl-Hyphen<br />

#define K_CTRL_PGDN 30 // Ctrl-PgDn, Ctrl-^<br />

#define K_ALT_UP 408 // * Alt-Up arrow<br />

#define K_ALT_DOWN 416 // * Alt-Down arrow<br />

#define K_ALT_LEFT 411 // * Alt-Left arrow<br />

#define K_ALT_RIGHT 413 // * Alt-Right arrow<br />

#define K_ALT_HOME 407 // * Alt-Home<br />

#define K_ALT_END 415 // * Alt-End<br />

#define K_ALT_PGUP 409 // * Alt-PgUp<br />

#define K_ALT_PGDN 417 // * Alt-PgDn<br />

#define K_ENTER 13 // Enter, Ctrl-M<br />

#define K_RETURN 13 // Return, Ctrl-M<br />

#define K_SPACE 32 // Space bar<br />

#define K_ESC 27 // Esc, Ctrl-<br />

#define K_CTRL_ENTER 10 // Ctrl-Enter<br />

#define K_CTRL_RETURN 10 // Ctrl-Return


156 volume VIII Argomenti avanzati e accessori<br />

#define K_CTRL_RET 10 // Ctrl-Return (Compat.)<br />

#define K_CTRL_PRTSCR 379 // * Ctrl-Print Screen<br />

#define K_CTRL_QUESTION 309 // Ctrl-?<br />

#define K_ALT_ENTER 284 // * Alt-Enter<br />

#define K_ALT_RETURN 284 // * Alt-Return<br />

#define K_ALT_EQUALS 387 // * Alt-Equals<br />

#define K_ALT_ESC 257 // * Alt-Esc<br />

#define KP_ALT_ENTER 422 // * Keypad Alt-Enter<br />

#define KP_CTRL_5 399 // * Keypad Ctrl-5<br />

#define KP_CTRL_SLASH 405 // * Keypad Ctrl-/<br />

#define KP_CTRL_ASTERISK 406 // * Keypad Ctrl-*<br />

#define KP_CTRL_MINUS 398 // * Keypad Ctrl--<br />

#define KP_CTRL_PLUS 400 // * Keypad Ctrl-+<br />

#define KP_ALT_5 5 // * Keypad Alt-5<br />

#define KP_ALT_SLASH 420 // * Keypad Alt-/<br />

#define KP_ALT_ASTERISK 311 // * Keypad Alt-*<br />

#define KP_ALT_MINUS 330 // * Keypad Alt--<br />

#define KP_ALT_PLUS 334 // * Keypad Alt-+<br />

#define K_INS 22 // Ins, Ctrl-V<br />

#define K_DEL 7 // Del, Ctrl-G<br />

#define K_BS 8 // Backspace, Ctrl-H<br />

#define K_TAB 9 // Tab, Ctrl-I<br />

#define K_SH_TAB 271 // Shift-Tab<br />

#define K_CTRL_INS 402 // * Ctrl-Ins<br />

#define K_CTRL_DEL 403 // * Ctrl-Del<br />

#define K_CTRL_BS 127 // Ctrl-Backspace<br />

#define K_CTRL_TAB 404 // * Ctrl-Tab<br />

#define K_ALT_INS 418 // * Alt-Ins<br />

#define K_ALT_DEL 419 // * Alt-Del<br />

#define K_ALT_BS 270 // * Alt-Backspace<br />

#define K_ALT_TAB 421 // * Alt-Tab<br />

#define K_CTRL_A 1 // Ctrl-A, Home<br />

#define K_CTRL_B 2 // Ctrl-B, Ctrl-Right arrow<br />

#define K_CTRL_C 3 // Ctrl-C, PgDn, Ctrl-ScrollLock<br />

#define K_CTRL_D 4 // Ctrl-D, Right arrow<br />

#define K_CTRL_E 5 // Ctrl-E, Up arrow<br />

#define K_CTRL_F 6 // Ctrl-F, End<br />

#define K_CTRL_G 7 // Ctrl-G, Del<br />

#define K_CTRL_H 8 // Ctrl-H, Backspace<br />

#define K_CTRL_I 9 // Ctrl-I, Tab<br />

#define K_CTRL_J 10 // Ctrl-J<br />

#define K_CTRL_K 11 // Ctrl-K<br />

#define K_CTRL_L 12 // Ctrl-L<br />

#define K_CTRL_M 13 // Ctrl-M, Return<br />

#define K_CTRL_N 14 // Ctrl-N<br />

#define K_CTRL_O 15 // Ctrl-O<br />

#define K_CTRL_P 16 // Ctrl-P<br />

#define K_CTRL_Q 17 // Ctrl-Q<br />

#define K_CTRL_R 18 // Ctrl-R, PgUp<br />

#define K_CTRL_S 19 // Ctrl-S, Left arrow<br />

#define K_CTRL_T 20 // Ctrl-T<br />

#define K_CTRL_U 21 // Ctrl-U<br />

#define K_CTRL_V 22 // Ctrl-V, Ins<br />

#define K_CTRL_W 23 // Ctrl-W, Ctrl-End<br />

#define K_CTRL_X 24 // Ctrl-X, Down arrow<br />

#define K_CTRL_Y 25 // Ctrl-Y


Clean the Clipper 5.2 157<br />

#define K_CTRL_Z 26 // Ctrl-Z, Ctrl-Left arrow<br />

#define K_ALT_A 286 // Alt-A<br />

#define K_ALT_B 304 // Alt-B<br />

#define K_ALT_C 302 // Alt-C<br />

#define K_ALT_D 288 // Alt-D<br />

#define K_ALT_E 274 // Alt-E<br />

#define K_ALT_F 289 // Alt-F<br />

#define K_ALT_G 290 // Alt-G<br />

#define K_ALT_H 291 // Alt-H<br />

#define K_ALT_I 279 // Alt-I<br />

#define K_ALT_J 292 // Alt-J<br />

#define K_ALT_K 293 // Alt-K<br />

#define K_ALT_L 294 // Alt-L<br />

#define K_ALT_M 306 // Alt-M<br />

#define K_ALT_N 305 // Alt-N<br />

#define K_ALT_O 280 // Alt-O<br />

#define K_ALT_P 281 // Alt-P<br />

#define K_ALT_Q 272 // Alt-Q<br />

#define K_ALT_R 275 // Alt-R<br />

#define K_ALT_S 287 // Alt-S<br />

#define K_ALT_T 276 // Alt-T<br />

#define K_ALT_U 278 // Alt-U<br />

#define K_ALT_V 303 // Alt-V<br />

#define K_ALT_W 273 // Alt-W<br />

#define K_ALT_X 301 // Alt-X<br />

#define K_ALT_Y 277 // Alt-Y<br />

#define K_ALT_Z 300 // Alt-Z<br />

#define K_ALT_1 376 // Alt-1<br />

#define K_ALT_2 377 // Alt-2<br />

#define K_ALT_3 378 // Alt-3<br />

#define K_ALT_4 379 // Alt-4<br />

#define K_ALT_5 380 // Alt-5<br />

#define K_ALT_6 381 // Alt-6<br />

#define K_ALT_7 382 // Alt-7<br />

#define K_ALT_8 383 // Alt-8<br />

#define K_ALT_9 384 // Alt-9<br />

#define K_ALT_0 385 // Alt-0<br />

#define K_F1 28 // F1, Ctrl-Backslash<br />

#define K_F2 -1 // F2<br />

#define K_F3 -2 // F3<br />

#define K_F4 -3 // F4<br />

#define K_F5 -4 // F5<br />

#define K_F6 -5 // F6<br />

#define K_F7 -6 // F7<br />

#define K_F8 -7 // F8<br />

#define K_F9 -8 // F9<br />

#define K_F10 -9 // F10<br />

#define K_F11 -40 // * F11<br />

#define K_F12 -41 // * F12<br />

#define K_CTRL_F1 -20 // Ctrl-F1<br />

#define K_CTRL_F2 -21 // Ctrl-F2<br />

#define K_CTRL_F3 -22 // Ctrl-F4<br />

#define K_CTRL_F4 -23 // Ctrl-F3<br />

#define K_CTRL_F5 -24 // Ctrl-F5<br />

#define K_CTRL_F6 -25 // Ctrl-F6<br />

#define K_CTRL_F7 -26 // Ctrl-F7<br />

#define K_CTRL_F8 -27 // Ctrl-F8<br />

#define K_CTRL_F9 -28 // Ctrl-F9<br />

#define K_CTRL_F10 -29 // Ctrl-F10<br />

#define K_CTRL_F11 -44 // * Ctrl-F11<br />

#define K_CTRL_F12 -45 // * Ctrl-F12


158 volume VIII Argomenti avanzati e accessori<br />

#define K_ALT_F1 -30 // Alt-F1<br />

#define K_ALT_F2 -31 // Alt-F2<br />

#define K_ALT_F3 -32 // Alt-F3<br />

#define K_ALT_F4 -33 // Alt-F4<br />

#define K_ALT_F5 -34 // Alt-F5<br />

#define K_ALT_F6 -35 // Alt-F6<br />

#define K_ALT_F7 -36 // Alt-F7<br />

#define K_ALT_F8 -37 // Alt-F8<br />

#define K_ALT_F9 -38 // Alt-F9<br />

#define K_ALT_F10 -39 // Alt-F10<br />

#define K_ALT_F11 -46 // * Alt-F11<br />

#define K_ALT_F12 -47 // * Alt-F12<br />

#define K_SH_F1 -10 // Shift-F1<br />

#define K_SH_F2 -11 // Shift-F2<br />

#define K_SH_F3 -12 // Shift-F3<br />

#define K_SH_F4 -13 // Shift-F4<br />

#define K_SH_F5 -14 // Shift-F5<br />

#define K_SH_F6 -15 // Shift-F6<br />

#define K_SH_F7 -16 // Shift-F7<br />

#define K_SH_F8 -17 // Shift-F8<br />

#define K_SH_F9 -18 // Shift-F9<br />

#define K_SH_F10 -19 // Shift-F10<br />

#define K_SH_F11 -42 // * Shift-F11<br />

#define K_SH_F12 -43 // * Shift-F12<br />

*=================================================================<br />

* MEMOEDIT()<br />

*=================================================================<br />

* User function entry modes<br />

#define MEMOEDIT_IDLE 0 // idle, all keys processed<br />

#define MEMOEDIT_UNKEY 1 // unknown key, memo unaltered<br />

#define MEMOEDIT_UNKEYX 2 // unknown key, memo altered<br />

#define MEMOEDIT_INIT 3 // initialization mode<br />

* User function return codes<br />

#define MEMOEDIT_DEFAULT 0 // perform default action<br />

#define MEMOEDIT_IGNORE 32 // ignore unknown key<br />

#define MEMOEDIT_DATA 33 // treat unknown key as data<br />

#define MEMOEDIT_TOGGLEWRAP 34 // toggle word-wrap mode<br />

#define MEMOEDIT_TOGGLESCROLL 35 // toggle scrolling mode<br />

#define MEMOEDIT_WORDRIGHT 100 // perform word-right operation<br />

#define MEMOEDIT_BOTTOMRIGHT 101 // perform bottom-right operation<br />

*=================================================================<br />

* SET()<br />

*=================================================================<br />

#define _SET_EXACT 1<br />

#define _SET_FIXED 2<br />

#define _SET_DECIMALS 3<br />

#define _SET_DATEFORMAT 4<br />

#define _SET_EPOCH 5<br />

#define _SET_PATH 6<br />

#define _SET_DEFAULT 7<br />

#define _SET_EXCLUSIVE 8<br />

#define _SET_SOFTSEEK 9<br />

#define _SET_UNIQUE 10


Clean the Clipper 5.2 159<br />

#define _SET_DELETED 11<br />

#define _SET_CANCEL 12<br />

#define _SET_DEBUG 13<br />

#define _SET_TYPEAHEAD 14<br />

#define _SET_COLOR 15<br />

#define _SET_CURSOR 16<br />

#define _SET_CONSOLE 17<br />

#define _SET_ALTERNATE 18<br />

#define _SET_ALTFILE 19<br />

#define _SET_DEVICE 20<br />

#define _SET_EXTRA 21<br />

#define _SET_EXTRAFILE 22<br />

#define _SET_PRINTER 23<br />

#define _SET_PRINTFILE 24<br />

#define _SET_MARGIN 25<br />

#define _SET_BELL 26<br />

#define _SET_CONFIRM 27<br />

#define _SET_ESCAPE 28<br />

#define _SET_INSERT 29<br />

#define _SET_EXIT 30<br />

#define _SET_INTENSITY 31<br />

#define _SET_SCOREBOARD 32<br />

#define _SET_DELIMITERS 33<br />

#define _SET_DELIMCHARS 34<br />

#define _SET_WRAP 35<br />

#define _SET_MESSAGE 36<br />

#define _SET_MCENTER 37<br />

#define _SET_SCROLLBREAK 38<br />

*=================================================================<br />

* SETCURSOR()<br />

*=================================================================<br />

#define SETCURSOR_NONE 0 // No cursor<br />

#define SETCURSOR_NORMAL 1 // Normal cursor (underline)<br />

#define SETCURSOR_INSERT 2 // Insert cursor (lower half block)<br />

#define SETCURSOR_SPECIAL1 3 // Special cursor (full block)<br />

#define SETCURSOR_SPECIAL2 4 // Special cursor (upper half block)<br />

*=================================================================<br />

* RDD REQUESTs<br />

*=================================================================<br />

external dbfndx<br />

external dbfntx // default<br />

Appunti di informatica libera 2003.06.29 --- Copyright © 2000-2003 Daniele Giacomini -- daniele @ swlibero.org


nanoBase 1997<br />

A free xBase for Dos.<br />

Chapter 367<br />

This material appeared originally at ‘http://www.geocities.com/SiliconValley/<br />

7737/nanobase.html’, in 1997. Now it is incorporated inside the Italian document<br />

‘‘Appunti di informatica libera’’, and might be reached at the URI .<br />

Questo materiale è apparso in origine, nel 1997, presso ‘http://www.geocities.com/<br />

SiliconValley/7737/nanobase.html’. Adesso viene incorporato nel documento «Appunti<br />

di informatica libera» e può essere raggiunto attraverso l’URI .<br />

367.1 What is it<br />

nanoBase 1 is a Dos program that works essentially as:<br />

• a dot command line xBase,<br />

• a menu driven xBase,<br />

• a xBase program interpreter.<br />

nanoBase 1997 is compiled in two versions: a small one to be used with old computers (i86 with<br />

640 Kibyte RAM), and a second one to be used with better computers, at least i286 (or better)<br />

with 2 Mibyte RAM.<br />

367.2 The dot command line<br />

Figure 367.1. The dot line.<br />

The dot command line is the first face <strong>of</strong> nanoBase, the one that appears starting the program<br />

normally. It recalls the dot line command <strong>of</strong> the old xBases.<br />

Please note that nanoBase recognise only expressions (that is: no commands).<br />

1 nanoBase GNU GPL<br />

160


nanoBase 1997 161<br />

367.3 The menu<br />

Figure 367.2. The file menu.<br />

Pressing [ F10 ] the nanoBase menu appears.<br />

From this menu the operations are easier than writing all commands on a prompt line, but it is<br />

always possible to come back to the dot line to do an operation not available from the menu.<br />

367.4 The macro recording, compiling and execution<br />

Figure 367.3. The macro menu.<br />

nanoBase is able to record some actions made with the menu and all what is correctly typed from<br />

the dot prompt. This may be the begin for a little program (called macro inside nanoBase) that<br />

can be executed as it is (ASCII), or compiled into another format, faster to execute.<br />

Macros for nanoBase are made with a reduced set <strong>of</strong> the Clipper syntax. The statements recognised<br />

from nanoBase are:<br />

PROCEDURE procedure_name<br />

statements...<br />

[RETURN]<br />

statements...<br />

ENDPROCEDURE


162 volume VIII Argomenti avanzati e accessori<br />

DO PROCEDURE procedure_name<br />

BEGIN SEQUENCE<br />

statements...<br />

[BREAK]<br />

statements...<br />

END<br />

DO CASE<br />

CASE lCondition1<br />

statements...<br />

[CASE lCondition2]<br />

statements...<br />

[OTHERWISE]<br />

statements...<br />

END<br />

WHILE lCondition<br />

statements...<br />

[EXIT]<br />

statements...<br />

[LOOP]<br />

statements...<br />

END<br />

IF lCondition1<br />

statements...<br />

[ELSE]<br />

statements...<br />

END<br />

• the ‘FOR’ loop is not available (too difficult to implement),<br />

• there may be no user defined functions (code blocks may be created instead),<br />

• procedure calls cannot transfer variables,<br />

• there are only public (global) variables.<br />

Beside these limitations, there are many added functions to the standard language that make the<br />

programming easier.<br />

All you need is inside ‘NB.EXE’:<br />

• the utility to handle manually the data,<br />

• the macro compiler,<br />

• the macro executor.


nanoBase 1997 163<br />

367.5 The report system<br />

Figure 367.4. The report menu.<br />

nanoBase can handle label (‘.LBL’) and form (‘.FRM’) files in the dBaseIII format. Labels and<br />

forms may be created and edited inside nanoBase. Beside these old report system there is another<br />

way to make a little bit complicated reports without making a complex macro: it is called RPT.<br />

A RPT file is a ASCII file with text mixed with code. The text may contain variables (usually a<br />

field or an expression containing fields).<br />

To make a complex report some work is needed, but surely less than the time needed to make a<br />

report program.<br />

The main purpose <strong>of</strong> it was to be able to print text with variables (typically names and addresses)<br />

for every record <strong>of</strong> a particular ‘.DBF’ file. Now the RPT system makes something more.<br />

367.6 The integrated text editor<br />

Figure 367.5. The integrated text editor.<br />

nanoBase contains an integrated text editor not particularly good, but very usefull for RPT files<br />

(as the expression insertion is very easy with the use <strong>of</strong> the [ F2 ] key) and whenever there isn’t<br />

any other editor there.


164 volume VIII Argomenti avanzati e accessori<br />

367.7 The internal documentation<br />

Figure 367.6. The internal documentation.<br />

nanoBase’s documentation si translated also inside the HTF format: ‘NB.HLP’. Pressing [ F1 ],<br />

normally, a contextual piece <strong>of</strong> the manual appears.<br />

Some standard functions have its own internal help, contained inside the ‘.EXE’ file. This was<br />

made to help programming with nanoBase.<br />

367.8 Download it<br />

Here is the 1997 edition <strong>of</strong> nanoBase.<br />

• EXE for small computers.<br />

<br />

<br />

• EXE for i286 with more than 2 Mibyte.<br />

<br />

<br />

• Runtime for small computers.<br />

<br />

<br />

• Documentation in many different formats.<br />

<br />

<br />

• Macro programming examples.<br />

<br />

<br />

• Source for version 96.06.16, without mouse support (1996).<br />

<br />


nanoBase 1997 165<br />

• Source for version 1997.<br />

<br />

<br />

367.9 Bugs and known problems<br />

Here is the list <strong>of</strong> known bugs and problems.<br />

• Comparison with floating point numbers may fail. It is better to convert numbers into string<br />

before comparing them.<br />

• Macros may be contained inside ASCII files or a ‘‘compiled’’ ‘.DBF’ file. In the second<br />

case, when nanoBase executes the macro, a work area (the last available one) is used, so it<br />

should not be closed or the macro execution will be stopped. A ‘dbcloseall()’ will stop<br />

execution <strong>of</strong> the macro. In substitution <strong>of</strong> ‘dbcloseall()’, ‘DBCLOSE()’ should be used.<br />

• To simplify the macro interpretation, lines such as this:<br />

qqout( "You can’t do that // you can’t do that!" )<br />

will generate an error as the interpreter will read only:<br />

qqout( "You can’t do that<br />

• nanoBase works good also if you have a screen configuration that permits you to show more<br />

than the usual 80 columns and 25 lines, but the library used to handle the mouse is not able<br />

to work outside the 80×25 area.<br />

Appunti di informatica libera 2003.06.29 --- Copyright © 2000-2003 Daniele Giacomini -- daniele @ swlibero.org


nanoBase 1997 user manual<br />

Chapter 368<br />

This is the documentation <strong>of</strong> nanoBase 1997, with minor modifications, that appeared originally<br />

at ‘http://www.geocities.com/SiliconValley/7737/nb.htm’. Now it is incorporated<br />

inside the Italian document ‘‘Appunti di informatica libera’’, and might be reached at<br />

the URI .<br />

Questo è il manuale di nanoBase 1997, modificato leggermente, che è apparso inizialmente<br />

presso ‘http://www.geocities.com/SiliconValley/7737/nb.htm’. Adesso<br />

viene incorporato nel documento «Appunti di informatica libera» e può essere raggiunto attraverso<br />

l’URI .<br />

368.1 Dos xBase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166<br />

368.2 Composition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171<br />

368.3 How to use nB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173<br />

368.4 Status line . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174<br />

368.5 The dot line . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175<br />

368.6 The menu system . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175<br />

368.7 The text editor DOC() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187<br />

368.8 The help text file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187<br />

368.9 Macro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188<br />

368.10 Data types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192<br />

368.11 Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .200<br />

368.12 Delimiters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201<br />

368.13 Code blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202<br />

368.14 Standard functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202<br />

368.15 nB functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261<br />

368.16 Normal command substitution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299<br />

368.17 nB command substitution functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305<br />

368.18 RPT: the nB print function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 310<br />

368.19 How can I... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313<br />

368.20 The source files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314<br />

nB (‘‘nano Base’’: ‘‘n’’ = ‘‘nano’’ = 10**(-9) = ‘‘very little’’) is a little Dos xBase written in<br />

Clipper 5.2 that can help to access ‘.DBF’ file created with different standards.<br />

nB is:<br />

• a dot command interpreter,<br />

166


nanoBase 1997 user manual 167<br />

• a menu driven xBase,<br />

• a xBase program interpreter.<br />

368.1 Dos xBase<br />

This section is a brief description <strong>of</strong> the functionality <strong>of</strong> a typical Dos xBase.<br />

The first purpose <strong>of</strong> a xBase program is to handle data inside a ‘.DBF’ file. These files may be<br />

indexed with the help <strong>of</strong> index files and more ‘.DBF’ files may be linked with a relation to obtain<br />

something like a relational database.<br />

368.1.1 .DBF files<br />

‘.DBF’ files are files organised in a table structure:<br />

.--------------------------.<br />

| field1 | field2 | field3 | record1<br />

|--------|--------|--------|<br />

| | | | record2<br />

|--------|--------|--------|<br />

| | | | record3<br />

|--------|--------|--------|<br />

| | | | record4<br />

|--------|--------|--------|<br />

| | | | record5<br />

|--------|--------|--------|<br />

| | | | record6<br />

‘--------------------------’<br />

The lines <strong>of</strong> this table are records and the columns are fields. Records are numbered starting from<br />

the first that is number 1.<br />

Columns are defined as fields and fields are distinguished by name and these names are saved<br />

inside the ‘.DBF’ file.<br />

Every field (column) can contain only one specified kind <strong>of</strong> data with a specified dimension:<br />

• ‘C’, character, originally the maximum dimension was 254 characters, minimum is 1;<br />

• ‘N’, numeric, a numeric field that can contain also sign and decimal values;<br />

• ‘D’, date, a field dedicated to date information;<br />

• ‘L’, logic, a filed that may contain only ‘T’ for True or ‘F’ for False used as a boolean<br />

variable;<br />

• ‘M’, memo, a character field with no predefined dimension, not allocated directly inside the<br />

‘.DBF’, but inside a ‘.DBT’ file, automatically linked.<br />

No other field type is available for a typical xBase ‘.DBF’ file.<br />

To access the data contained inside a ‘.DBF’ file the following list <strong>of</strong> action may be followed:<br />

• Open a ‘.DBF’ file inside the current area, where these areas are something like file handlers.<br />

• After the ‘.DBF’ file is opened, it referenced only by the alias name that usually correspond<br />

to the original filename without extention.


168 volume VIII Argomenti avanzati e accessori<br />

• Move the record pointer to the desired location.<br />

• Lock the current record to avoid access from other users.<br />

• Do some editing with the data contained inside the current record using the field names like<br />

they were variables.<br />

• Release the lock.<br />

• Move the record pointer to another desired location.<br />

• Lock the current record to avoid access from other users.<br />

• ...<br />

• Close the alias.<br />

Before you go further, you have to understand that:<br />

• A ‘.DBF’ file is opened using a free WORK AREA that may be associated to the concept<br />

<strong>of</strong> the file handler.<br />

• The ‘.DBF’ file is opened with a alias name that permit to open the same ‘.DBF’ file more<br />

times when using different alias names.<br />

• After the ‘.DBF’ file is opened, we don’t speak any more <strong>of</strong> file, but alias.<br />

• If the work area "n" is used from the alias "myAlias", speaking <strong>of</strong> work area "n" or <strong>of</strong> alias<br />

"myAlias" is the same thing.<br />

368.1.2 <strong>Index</strong> files<br />

‘.DBF’ files are organised with record number, that is, you can reach a specific record and not a<br />

specific information unless that you scan record by record.<br />

To obtain to "see" a ‘.DBF’ file somehow logically ordered (when physically it is not), index files<br />

are used.<br />

A index file, also called INDEX BAG, is a file that contains one or more indexes<br />

<strong>Index</strong>es are rules by which a ‘.DBF’ file may be seen ordered.<br />

A typical index file may contain only one index.<br />

A index file may have the following extention:<br />

• ‘.NDX’, single index, dBase III and dBase III plus;<br />

• ‘.NTX’, single index, Clipper;<br />

• ‘.MBX’, multiple index, dBase IV;<br />

• ‘.CDX’, multiple index, FoxPro.<br />

Every index file may be used only in association with the ‘.DBF’ for what it was made. The<br />

problem is that normally there is no way to avoid errors when the user try to associate the right<br />

‘.DBF’ file with the wrong index.


nanoBase 1997 user manual 169<br />

To access the data contained inside a ‘.DBF’ file the following list <strong>of</strong> action may be followed:<br />

• Open a ‘.DBF’ file.<br />

• Open a index file.<br />

• Select a particular order.<br />

• Search for a key or move the record pointer on a different way.<br />

• Lock the current record to avoid access from other users.<br />

• Do some editing with the data contained inside the current record using the field names like<br />

they were variables.<br />

• Release the lock.<br />

• Move the record pointer to another desired location.<br />

• Lock the current record to avoid access from other users.<br />

• ...<br />

• Close the alias.<br />

Before you go further, you have to understand that:<br />

• As orders are contained inside a INDEX BAG file physically distinguished form the ‘.DBF’<br />

file, it may happen that a ‘.DBF’ file is wrongly opened and edited without the index. In<br />

this case, the INDEX BAG is not updated and when the INDEX BAG will be opened, the<br />

records contained inside the ‘.DBF’ file may not correspond.<br />

• For the same reason, an improper program termination may result in an incomplete data<br />

update. That is: ‘.DBF’ file may be all right, INDEX BAG not.<br />

• This is why xBase programs are "weak" relational databases or they are not relational<br />

databases at all.<br />

• When troubles occurs, indexes must be rebuild.<br />

368.1.3 Relations<br />

Many ‘.DBF’ files with indexes may be opened simultaneously. Data contained inside more<br />

‘.DBF’ files may be somehow connected together. See the example.


170 volume VIII Argomenti avanzati e accessori<br />

.----------------------------------------.<br />

| Date | Time IN | Time OUT | Employee # |<br />

|------|---------|-----------------------|<br />

| xxxx | xxxxxxx | xxxxxxxx | 01 | -------->.<br />

|------|---------|----------|------------| |<br />

| yyyy | yyyyyyy | yyyyyyyy | 02 | |<br />

|------|---------|----------|------------| |<br />

| zzzz | zzzzzzz | zzzzzzzz | 01 | -------->|<br />

‘----------------------------------------’ |<br />

[...] |<br />

|<br />

.| 01 | aaaaaaa | aaaaaaa |.....|<br />

|------------|---------|---------|-----|<br />

| 02 | bbbbbbb | bbbbbbb |.....|<br />

|------------|---------|---------|-----|<br />

| 03 | ccccccc | ccccccc |.....|<br />

‘--------------------------------------’<br />

The first ‘.DBF’ file contains some data that refers to an Employee number that may appear<br />

repeated on more records.<br />

Employee informations are stored inside another ‘.DBF’ file that contains only one record for<br />

every employee.<br />

Establishing a relation from the first ‘.DBF’ file to the second, moving the record pointer <strong>of</strong> the<br />

first ‘.DBF’ file, that is the first alias, the record pointer <strong>of</strong> the second, the child alias, is moved<br />

automatically to the record containing the right data.<br />

The relation is an expression that should result in a number if the child alias is opened without<br />

index, or in a valid index key if the child alias is opened with an index.<br />

To relate two ‘.DBF’ files the following list <strong>of</strong> action may be followed:<br />

• Open the first ‘.DBF’ file.<br />

• Open a index file for the first alias.<br />

• Select a particular order.<br />

• Open the second ‘.DBF’ file.<br />

• Open a index file for the second alias.<br />

• Select a particular order.<br />

• Select the first alias.<br />

• Define a relation form the first alias and the second alias: the child alias.<br />

• Search for a key or move the record pointer <strong>of</strong> the first alias (don’t care about the Child<br />

alias).<br />

• Lock the current record to avoid access from other users.<br />

• If data contained inside the Child alias should be edited (usually it doesn’t happen), lock<br />

the current record <strong>of</strong> the Child alias.


nanoBase 1997 user manual 171<br />

• Do some editing with the data contained inside the current record using the field names like<br />

they were variables.<br />

• Release the lock (also with the Child alias if a lock was made).<br />

• Move the record pointer to another desired location.<br />

• Lock the current record to avoid access from other users.<br />

• [...]<br />

• Release the relation.<br />

• Close the Child alias.<br />

• Close the first alias.<br />

As may be seen, relations are not saved inside files, but are obtained with lines <strong>of</strong> code.<br />

368.2 Composition<br />

nB is composed from the following files, where xx is the the version code.<br />

NBASExx1.ZIP EXEs for small PCs<br />

NBASExx2.ZIP Runtime EXEs for small PCs<br />

NBASExx3.ZIP EXEs for i286 with 2M+<br />

NBASExx4.ZIP DOCs<br />

NBASExx5.ZIP EXAMPLEs<br />

NBASExx6.ZIP SRCs for version 96.06.16<br />

NBASExx7.ZIP SRCs for the current version<br />

Every archive file contains:<br />

‘COPYING.TXT’ GNU General Public License version 2 in Dos text format.<br />

‘README.TXT’ the readme file.<br />

‘FILE_ID.DIZ’ definition.<br />

The file ‘NBASExx1.ZIP’ contains also the following files.<br />

‘NB.EXE’<br />

the executable program for DBFNTX and DBFNDX files,<br />

linked with RTLINK.<br />

‘NB.HLP’ this manual in "Help Text File" format.<br />

The file NBASExx2.ZIP contains also the following files.<br />

‘NB.EXE’<br />

The file ‘NBASExx3.ZIP’ contains also the following files.<br />

the run-time to execute macro programs for DBFNTX and<br />

DBFNDX files handling, linked with RTLINK.<br />

‘NB.EXE’<br />

the executable program for DBFCDX, DBFMDX, DBFNDX<br />

and DBFNTX files, linked with EXOSPACE.<br />

‘NB.HLP’ the user manual in "Help Text File" format.


172 volume VIII Argomenti avanzati e accessori<br />

The file ‘NBASExx4.ZIP’ contains also the following files.<br />

‘NB.PRN’ the user manual in printed text format.<br />

‘NB.RTF’ the user manual in RTF format.<br />

‘NB.TXT’ the user manual in ASCII text format.<br />

‘NB.HTM’ the user manual in HTML format.<br />

The file ‘NBASExx5.ZIP’ contains also the following files.<br />

‘_ADDRESS.DBF’ an example database file.<br />

‘_ADDRESS.NTX’ index file associated to ‘_ADDRESS.DBF’.<br />

‘_ADDRESS.LBL’<br />

a label form file used to print data contained inside<br />

‘_ADDRESS.DBF’ .<br />

‘_ADDRESS.FRM’<br />

a report form file used to print data contained inside<br />

‘_ADDRESS.DBF’ .<br />

‘_ADDRESS.RPT’<br />

a RPT text file<br />

‘_ADDRESS.DBF’ .<br />

used to print data contained inside<br />

‘_MAINMNU.&’<br />

a macro program source example <strong>of</strong> a menu that executes<br />

some others macro programs. This example is made to<br />

demonstrate how nB can execute directly a source code without<br />

compiling it. This example is made only to taste it: it is<br />

very slow and only a speedy machine can give the idea <strong>of</strong> it.<br />

a macro program source example <strong>of</strong> a menu that executes<br />

‘0MAINMNU.&’<br />

some others macro programs. It is the same as ‘_MAINMNU.&’<br />

but it is made to start the execution <strong>of</strong> the compiled macros.<br />

‘0MAINMNU.NB’ compiled macro program ‘0MAINMNU.&’<br />

‘0MAINMNU.BAT’<br />

a batch file to<br />

‘0MAINMNU.NB’<br />

show how to run the execution <strong>of</strong><br />

‘1ADDRESS.&’<br />

a macro program source example for handling a ‘.DBF’ file<br />

containing addresses in various ways.<br />

‘1ADDRESS.NB’ compiled macro ‘1ADDRESS.&’ .<br />

a macro program source example for handling a ‘.DBF’ file<br />

‘2ADDRESS.&’<br />

containing addresses in various ways: a little bit more complicated<br />

than 1ADDRESS.&.<br />

‘2ADDRESS.NB’ compiled macro ‘2ADDRESS.&’ .<br />

a macro program source example for handling a ‘.DBF’ file<br />

‘3ADDRESS.&’<br />

containing addresses in various ways: a little bit more complicated<br />

than ‘2ADDRESS.&’ .<br />

‘3ADDRESS.NB’ compiled macro ‘3ADDRESS.&’.<br />

a macro program source example for handling a ‘.DBF’ file<br />

‘4ADDRESS.&’<br />

containing addresses in various ways: a little bit more complicated<br />

than ‘3ADDRESS.&’ .<br />

‘4ADDRESS.NB’ compiled macro ‘4ADDRESS.&’ .<br />

‘ABIORITM.&’<br />

a macro program source example for calculating the personal<br />

bio wave.<br />

‘ABIORITM.NB’ compiled macro ‘ABIORITM.&’ .<br />

‘_STUDENT.DBF’ a ‘.DBF’ file used inside the BSTUDENT macro example.<br />

‘_STUDENT.NTX’ index file used for ‘_STUDENT.DBF’ .<br />

‘_STUDSTD.DBF’ a ‘.DBF’ file used inside the BSTUDENT macro example.<br />

‘_STUDENT.RPT’<br />

a RPT text file<br />

‘_STUDENT.DBF’ .<br />

used to print data contained inside<br />

‘_STUDSTD.RPT’<br />

a RPT text file<br />

‘_STUDSTD.DBF’ .<br />

used to print data contained inside<br />

‘BSTUDENT.&’<br />

a macro program source example for students evaluation: a<br />

description about students is obtained linking other standard<br />

descriptions.<br />

‘BSTUDENT.NB’ compiled macro ‘BSTUDENT.&’ .


nanoBase 1997 user manual 173<br />

‘CBATMAKE.&’<br />

a macro program source example to generate a batch file to be<br />

used to back up an entire hard disk.<br />

‘CBATMAKE.NB’ compiled macro ‘CBATMAKE.&’ .<br />

‘BROWSE.&’<br />

‘BROWSE.NB’ compiled macro ‘BROWSE.&’ .<br />

a macro program source example to start an automatic<br />

browse.<br />

‘BROWSE.BAT’<br />

batch file to start a ‘.DBF’ browse with the BROWSE macro<br />

program.<br />

‘MENU.&’ a macro program source example for a Dos menu.<br />

‘MENU.NB’ compiled macro ‘MENU.&’ .<br />

‘MENU.BAT’ batch file to use the MENU macro.<br />

The file ‘NBASExx6.ZIP’ contains also the following files: source code for the version 96.06.16.<br />

‘NB.PRG’ the main source file for version 96.06.16.<br />

‘NB_REQ.PRG’ the source file containing links to all the standard functions.<br />

‘NB.LNK’ link file for compilation.<br />

‘NB_NRMAL.RMK’ rmake file to compile with RTLink.<br />

‘NB_EXOSP.RMK’ rmake file to compile with Exospace.<br />

‘NB_RUNTI.RMK’<br />

rmake file to compile with RTLink defining RUNTIME to obtain<br />

a small nB runtime version.<br />

‘MACRO.LNK’ link file to compile and link a macro.<br />

‘MACRO.RMK’ rmake file to compile and link a macro.<br />

The file ‘NBASExx7.ZIP’ contains also the following files: source code for the current version.<br />

‘NB.PRG’ the main source file.<br />

‘REQUEST.PRG’ the source file containing links to all the Clipper functions.<br />

‘STANDARD.PRG’ the source file for standard functions.<br />

‘EXTRA.PRG’ the source file for other standard functions.<br />

‘STANDARD.CH’<br />

general include file that substitutes all include file normally<br />

used for normal Clipper compilations.<br />

‘NB.CH’ include file specific for nB.<br />

‘NB.LNK’ link file for compilation.<br />

‘NB_RUNTI.LNK’ link file for runtime compilation.<br />

‘NB_NRMAL.RMK’ rmake file to compile with RTLink.<br />

‘NB_EXOSP.RMK’ rmake file to compile with Exospace.<br />

‘NB_RUNTI.RMK’<br />

rmake file to compile with RTLink defining RUNTIME to obtain<br />

a small nB runtime version.<br />

‘MACRO.CH’ include file to compile and link a macro.<br />

‘MACRO.LNK’ link file to compile and link a macro.<br />

‘MACRO.RMK’ rmake file to compile and link a macro.<br />

‘CLIPMOUSE.ZIP’<br />

368.3 How to use nB<br />

nB normal syntax is:<br />

nB [nB_parameters] [macro_filename] [macro_parameters]<br />

a simple free library for mouse support under Clipper (c) 1992<br />

Martin Brousseau.<br />

To run nB, just type the word "NB" and press [ Enter ] to execute. It will run in command mode,<br />

this means that it will look like an old xBASE command prompt.


174 volume VIII Argomenti avanzati e accessori<br />

To run the program as a macro interpreter, type the word NB followed from the macro file name<br />

with extention (no default extention is supposed). If parameters are given, after the macro file<br />

name, these will be available inside the public variables: c_Par1, c_Par2, ..., c_Par9. c_Par0 will<br />

contain the macro file name (see the macro file BROWSE.&). nB will terminate execution when<br />

the macro terminates.<br />

These parameters are available for nB:<br />

-c<br />

-w<br />

-?<br />

nB macro "compilation" syntax is:<br />

nB -m source_macro_filename [destination_macro_filename]<br />

Suppress the copyright notice. It is usefull when using nB for<br />

macro interpretation.<br />

Suppress the "Wait-Wheel" if not desired. It is the "Wheel"<br />

that appears at top-left when a macro is interpreted or other<br />

long elaborations are executed.<br />

Shows a short help.<br />

With the -m parameter, nB "compiles" the ASCII source_macro_filename into<br />

destination_macro_filename.<br />

368.4 Status line<br />

nB shows a "status line" at the top <strong>of</strong> the screen when the nB command prompt or the menu<br />

system is active. It shows some important informations.<br />

| |DBFNTX ||*| 1|ADDRESS | 1/ 4|ADDRESS.NTX |<br />

| | | | | | |<br />

| | | | | | |<br />

| | | | | | Last record (7).<br />

| | | | | |<br />

| | | | | Record pointer position (6).<br />

| | | | |<br />

| | | | Active alias (5).<br />

| | | |<br />

| | | Current Work Area (4)<br />

| | |<br />

| | Deleted record appearance (3)<br />

| |<br />

| Actual default database driver (2).<br />

|<br />

Macro recorder indicator (1).<br />

| 1/ 4|ADDRESS.NTX | 1|ADDRESS |<br />

| | |<br />

| | |<br />

| | Order Tag Name (10)<br />

| |<br />

| Order number (9)<br />

|<br />

Order Bag name (8)<br />

(1) This is the place for the macro recorder indicator. The symbol used is "&". Blank means that<br />

the macro recorder is OFF; & blinking means that the macro recorder is ON; & fixed means that<br />

the macro recorder is PAUSED.<br />

(2) The name <strong>of</strong> the default database driver. It is not necessarily the database driver for the active<br />

alias; it is only the database driver that will be used for the next open/create operation.


nanoBase 1997 user manual 175<br />

(3) An asterisk (*) at this position indicates that SET DELETED is OFF. This means that deleted<br />

records are not filtered. When a BLANK is in this place, SET DELETED is ON, so that deleted<br />

records are filtered.<br />

(4) The active work area number, that is, the area <strong>of</strong> the active alias.<br />

(5) The active alias name. Note that the alias name is not necessarily equal to the ‘.DBF’ file<br />

name.<br />

(6) The actual record pointer position for the active alias.<br />

(7) The number <strong>of</strong> records contained inside the active alias.<br />

(8) The Order Bag name; that is the index file name.<br />

(9) The order number.<br />

(10) The order tag (name). When DBFNTX database driver is used, it correspond to the Order<br />

Bag name.<br />

368.5 The dot line<br />

Starting nB without parameters, the dot line appears. This is the place where commands in form<br />

<strong>of</strong> functions may be written and executed like a old xBase.<br />

The functions written inside the command line that don’t result in an error, are saved inside a<br />

history list. This history list may be recalled with [ F2 ] and then the selected history line may be<br />

reused (eventually edited). Key [ up ]/[ down ] may be used to scroll inside the history list without<br />

showing the all list with [ F2 ].<br />

[ Enter ] is used to tell nB to execute the written function.<br />

As the dot line is not an easy way to use such a program, a menu is available pressing [ F10 ] or<br />

[ Alt+M ]. The [ F10 ] key starts the ASSIST() menu. This menu may be started also entering the<br />

name <strong>of</strong> the function: "ASSIST()".<br />

nB includes a simple built-in text editor: DOC(). It may be started from the dot line entering<br />

"DOT()". No special key is dedicated to start this function.<br />

368.6 The menu system<br />

The nB menu system appears differently depending on the place where it is "called". When<br />

available, the menu system appears pressing [ Alt+M ] or [ F10 ].<br />

The Menu system is organised into horizontal menu, vertical menu, and pop-up menu.<br />

The horizontal menu contains selectable items organised horizontally:<br />

One Two Three Four Five<br />

The cursor may be moved on a different position using arrow keys [ Left ]/[ Right ]; [ Esc ] terminates<br />

the menu; [ Enter ] opens a vertical menu.<br />

The vertical menu contains selectable items organised vertically:<br />

One Two Three Four Five<br />

.----------.<br />

|First |<br />

|Second |<br />

|Third |<br />

‘----------’


176 volume VIII Argomenti avanzati e accessori<br />

The cursor may be moved on a different position using arrow keys [ Up ]/[ Down ]; the arrow keys<br />

[ Left ]/[ Right ] change the vertical menu; [ Esc ] closes the vertical the menu; [ Enter ] starts the selected<br />

menu function.<br />

The vertical menu contains selectable items organised vertically:<br />

One Two Three Four Five<br />

.----------.<br />

|First |<br />

|Second >|--------------.<br />

|Third |Sub function 1|<br />

‘----------|Sub function 2|<br />

‘--------------’<br />

The cursor may be moved on a different position using arrow keys [ Up ]/[ Down ]; [ Esc ] closes the<br />

pop-up the menu; [ Enter ] starts the selected menu function.<br />

The following sections describe the menu system.<br />

368.6.1 Menu File<br />

The menu File contains important function on ‘.DBF’ file, indexes, relations and Replaceable<br />

database drivers.<br />

For database files are considered two aspects: the physical aspect, and the logical alias. When a<br />

‘.DBF’ file is opened, it becomes a alias.<br />

<strong>Index</strong>es are considered as index files and index orders.<br />

It follows a brief menu function description.<br />

Change directory<br />

Changes the actual drive and directory.<br />

File .DBF<br />

Contains a pop-up menu for ‘.DBF’ operations.<br />

New .DBF<br />

A ‘.DBF’ file is a table where columns, called Fields, must be specified and lines,<br />

called records, are added, edited and deleted by the program.<br />

Field characteristics are:<br />

the field name must be unique inside the same file, it is<br />

NAME<br />

composed <strong>of</strong> letters, number and underscore (_), but it<br />

must start with a letter and it is not case sensitive.<br />

TYPE the field type determinates the type <strong>of</strong> data it can hold.<br />

is the field total length in characters; it doesn’t matter<br />

LENGTH<br />

DECIMAL<br />

Field Types:<br />

<strong>of</strong> the type <strong>of</strong> data.<br />

is the length <strong>of</strong> positions after decimal point. This information<br />

is used normally for numeric fields. In this<br />

case, take note that the DECIMAL length, together<br />

with the decimal point, will subtract space for the integer<br />

part <strong>of</strong> the number from the total LENGTH <strong>of</strong> the<br />

filed.


nanoBase 1997 user manual 177<br />

C Character<br />

it is a text field long LENGTH characters.<br />

it is a numeric field long LENGTH characters with<br />

DECIMAL characters for decimal positions. Note that<br />

if LENGTH is 4 and DECIMAL is 0 (zero), the<br />

N Numeric<br />

field may contain integers from -999 to 9999; but if<br />

LENGTH is 4 and DECIMAL 1, the field may contain<br />

numbers from -9.9 to 99.9: two position for the integer<br />

part, one position for the decimal point and one position<br />

for decimal.<br />

D Date<br />

it is a date field: it contains only dates; the length<br />

should not be specified as it is automatically 8.<br />

it is a logical (boolean) field: it contains only TRUE,<br />

L Logic<br />

represented by "Y" or "T", or FALSE, represented by<br />

"N" or "F". The length should not be specified as it is<br />

automatically 1.<br />

it is a character field with unknown dimension. It is<br />

recorded into a parallel file with ‘.DBT’ extention. The<br />

M Memo<br />

original ‘.DBF’ file holds a space for a pointer inside<br />

the ‘.DBT’ file. The length <strong>of</strong> a Memo field is automatically<br />

10 and is referred to the memo pointer.<br />

After the function "NEW .DBF" is selected, a table for the field specifications appears.<br />

+--------------------------------+<br />

| Database file structure |<br />

| |<br />

| Field Name Type Length Decimal |<br />

|--------------------------------|<br />

| | | 0| 0 |<br />

| | | | |<br />

| | | | |<br />

| | | | |<br />

| | | | |<br />

| | | | |<br />

| | | | |<br />

To navigate and to edit the table use the following keys:<br />

[ Up ]/[ Down ]/[ Left ][ Right ] move the cursor one position (up, down, left or right);<br />

[ PgUp ] move to previous screen page;<br />

[ PgDn ] move to next screen page;<br />

[ Ctrl+PgUp ] move to top <strong>of</strong> table;<br />

[ Ctrl+PgDn ] move to bottom <strong>of</strong> table;<br />

[ Ctrl+Home ] move to first column;<br />

[ Ctrl+End ] move to last column;<br />

[ Ctrl+Enter ] append a new empty line;<br />

[ Ctrl+F1 ]<br />

delete (cut) the current line and save a copy into the<br />

"clipboard";<br />

[ Ctrl+F2 ] copy current line into the table "clipboard";<br />

insert (paste) the content <strong>of</strong> the "clipboard" in the cur-<br />

[ Ctrl+F3 ]<br />

rent position;<br />

[ Enter ] start editing in the current position;<br />

[ Esc ] terminate;<br />

[ x ] any other key will be written in the current position.<br />

When the editing is terminated, press [ Esc ] and a dialog box will ask for the file name<br />

and the RDD.<br />

xBase files (.DBF) are not all equal, this way, when a new ‘.DBF’ file si created, the<br />

RDD (Replaceable Database Driver) is asked. The normal RDD is DBFNTX, the one<br />

used by Clipper.


178 volume VIII Argomenti avanzati e accessori<br />

Modify .DBF structure<br />

The modification <strong>of</strong> a ‘.DBF’ file structure is a delicate matter if it contains data.<br />

In fact, it is a data transfer from a source ‘.DBF’ file to a destination ‘.DBF’ file with<br />

a different structure. This way, the destination ‘.DBF’ will be updated only for the<br />

fields with the same name <strong>of</strong> the source one. The position may be different, but names<br />

cannot be changed (not so easily).<br />

Mistakes may be dangerous, so, before doing it, it is recommended a backup copy <strong>of</strong><br />

the original ‘.DBF’ file.<br />

Open .DBF<br />

When a ‘.DBF’ file is opened, it becomes a alias, a logical file, placed inside a work<br />

area. The same ‘.DBF’ file may be opened inside different areas with different alias<br />

names.<br />

The required information to open the file are:<br />

File .NTX<br />

FILENAME<br />

ALIAS<br />

RDD<br />

SHARED<br />

READ ONLY<br />

Contains a pop-up menu for physical indexes operations.<br />

the physical file name.<br />

the alias name. If not assigned, it becomes automatically<br />

the same <strong>of</strong> FILENAME without extention.<br />

the Replaceable Database Driver to use to access to this<br />

file.<br />

a logical value: TRUE means that the file will be accessible<br />

to other users, FALSE means use exclusive.<br />

a logical value: TRUE means that the file will be only<br />

readable and no modification will be allowed, FALSE<br />

means that no restriction on editing will be made.<br />

New .NTX / new tag<br />

If the active area is used we have an active alias. In this case a index may be created.<br />

The index is a way to see the active alias ordered without changing the physical<br />

position <strong>of</strong> records.<br />

There are two words to understand: ORDER and INDEX-BAG. The index bag is the<br />

file that contains the information on the record ordering, the order is the rule followed<br />

to order the records. A index bag may contains one or more orders depending on the<br />

Replaceable Database Driver in use.<br />

Typical ‘.NTX’ file are index bag containing only one order.<br />

Depending on the RDD in use the following field may be filled.<br />

INDEX FILENAME<br />

this is the name <strong>of</strong> the index bag.<br />

the expression that defines the rule for the record or-<br />

KEY EXPRESSION<br />

dering.<br />

this is the name to give to the order (tag) when the RDD<br />

permits to have a index bag containing more than one<br />

ORDER NAME<br />

order. In the other case, the index bag name correspond<br />

to the order name.<br />

FOR EXPRESSION<br />

a FOR condition to filter records before indexing.<br />

Open index<br />

If a index file already exists, it can be associated to the active alias simply opening it.<br />

Take note that the system is not able to verify if the index belong the active alias<br />

and if it is not so a error will result.


nanoBase 1997 user manual 179<br />

Alias<br />

Order<br />

INDEX NAME<br />

is the name <strong>of</strong> the index bag file to open.<br />

Contains a pop-up menu for logical databases (alias) operations.<br />

Select<br />

Only one may be the active alias and with this function the active alias may be changed<br />

choosing from the list <strong>of</strong> used areas.<br />

Selecting the area number zero, no alias is active.<br />

Display structure<br />

With this function the active alias structure may be viewed.<br />

Close active alias<br />

Selecting this function the active alias is closed. That is: the ‘.DBF’ file and eventual<br />

indexes are closed.<br />

Close all aliases<br />

With this function all Aliases are closed.<br />

Contains a pop-up menu for logical indexes (orders).<br />

Relation<br />

Order list rebuild<br />

This function rebuild the indexes opened and associated to the active alias.<br />

Order set focus<br />

This function permits to change the active order selecting form the ones opened and<br />

associated to the active alias.<br />

Order list clear<br />

This function closes all orders associated to the active alias.<br />

Contains a pop-up menu for relations (links with other Aliases).<br />

Set relation<br />

This function permits to establish a relation between a alias and a Child alias showing<br />

as a result a unique database.<br />

CHILD is the alias name to connect to the active alias.<br />

is the relation expression that specify the rule for the relation.<br />

The value <strong>of</strong> this expression is the key to access<br />

EXPRESSION<br />

the Child alias: if this Child alias is accessed without<br />

index, it must be the record number, if this Child alias<br />

is accessed via index, it must be a valid index key.<br />

Clear relation<br />

This function eliminates any relation that originate form the active alias.<br />

RDD default<br />

Contains a pop-up menu for Replaceable Database Driver defaults.<br />

Show actual RDD default<br />

It simply shows the actual Replaceable Database Driver.


180 volume VIII Argomenti avanzati e accessori<br />

Set default RDD<br />

Select a new default Replaceable Database Driver.<br />

368.6.2 Menu Edit<br />

The menu Edit contains functions to access data from the active alias (the actual area).<br />

View<br />

This function permits you to view the active alias with eventual relations as a table.<br />

No edit is allowed.<br />

To navigate the table use the following keys.<br />

[ Enter ] start field editing.<br />

[ PgUp ] show previous screen page.<br />

[ PgDn ] show next screen page.<br />

[ Ctrl+PgUp ] show top <strong>of</strong> alias.<br />

[ Ctrl+PgDn ] show bottom <strong>of</strong> file.<br />

[ Ctrl+Home ] show the first column.<br />

[ Ctrl+End ] show last column.<br />

Edit/browse<br />

This function permits you to edit the active alias with eventual relations as a table.<br />

To navigate and edit the table use the following keys.<br />

[ Enter ] start field editing.<br />

[ PgUp ] show previous screen page.<br />

[ PgDn ] show next screen page.<br />

[ Ctrl+PgUp ] show top <strong>of</strong> alias.<br />

[ Ctrl+PgDn ] show bottom <strong>of</strong> file.<br />

[ Ctrl+Home ] show the first column.<br />

[ Ctrl+End ] show last column.<br />

[ Ctrl+Enter ] append a new empty record.<br />

[ Ctrl+F2 ] copy the current record.<br />

[ Ctrl+F3 ] append and paste a record.<br />

[ Ctrl+F4 ]<br />

paste a previously copied record, overwriting the content<br />

<strong>of</strong> the current one.<br />

[ Ctrl+Y ] delete or recall the current record.<br />

[ Ctrl+Del ] delete or recall the current record.<br />

When a memo field is edited:<br />

Replace<br />

[ Esc ] cancel and close the memo window.<br />

[ Ctrl+Y ] line delete.<br />

[ Ctrl+W ] save and close the memo window.<br />

The content <strong>of</strong> a Field <strong>of</strong> the active alias may be replaced with an expression.<br />

The required data is:


nanoBase 1997 user manual 181<br />

Recall<br />

Delete<br />

Pack<br />

FIELD TO REPLACE<br />

NEW VALUE EXPRESSION<br />

WHILE EXPRESSION<br />

FOR EXPRESSION<br />

the Field name to be replaced.<br />

the expression that obtain the new value for the selected<br />

Field.<br />

the WHILE condition expression: the replacement continue<br />

until this expression results True. The constant ‘.T.’<br />

is ever True and is the default.<br />

the FOR condition expression: the replacement is made<br />

for all records that satisfy the condition. The constant<br />

‘.T.’ is ever True and is the default.<br />

The records signed for deletion (deleted but still there), may be recalled (undeleted).<br />

The required data is:<br />

WHILE EXPRESSION<br />

FOR EXPRESSION<br />

the WHILE condition expression: the record recall continue<br />

until this expression results True. The constant ‘.T.’<br />

is ever True and is the default.<br />

the FOR condition expression: the record recall is made<br />

for all records that satisfy the condition. The constant<br />

‘.T.’ is ever True and is the default.<br />

Deletes (sign for deletion) a group <strong>of</strong> record depending on the required conditions.<br />

The required data is:<br />

WHILE EXPRESSION<br />

FOR EXPRESSION<br />

the WHILE condition expression: the record deletion continue<br />

until this expression results True. The constant ‘.T.’<br />

is ever True and is the default.<br />

the FOR condition expression: the record deletion is made<br />

for all records that satisfy the condition. The constant<br />

‘.T.’ is ever True and is the default.<br />

This function eliminates definitely records previously deleted (signed for deletion).<br />

It may work only if the active alias was opened in exclusive mode.<br />

368.6.3 Menu Report<br />

The menu Report contains functions for data report (print). In particular, label files ‘.LBL’ and<br />

report file ‘.RPT’ may be created and used for printing. There is also another way to print, with<br />

the RPT() system that is available inside the nB internal editor DOC().<br />

DBGOTOP()<br />

Moves the record pointer for the active alias at the first logical record.<br />

New label<br />

With this function can be created a standard label file (.LBL under the dBaseIII standard).<br />

Labels may be printed in more than one column and can contain 16 lines maximum.<br />

The label data is the following.


182 volume VIII Argomenti avanzati e accessori<br />

REMARK<br />

a label remark that will not be printed.<br />

HEIGHT the label vertical dimension.<br />

WIDTH the label horizontal dimension.<br />

MARGIN<br />

the left margin in characters.<br />

LINES<br />

the vertical spacing between labels.<br />

SPACES<br />

the horizontal spacing between labels in characters.<br />

ACROSS the number <strong>of</strong> label columns.<br />

LINE 1 The first line inside labels.<br />

LINE n The n-th line inside labels.<br />

LINE 16 The 16th line inside labels.<br />

The number <strong>of</strong> lines inside the labels depend on the HEIGHT and the maximum value is<br />

16.<br />

The label lines can contain constant string and/or string expressions.<br />

See the example below.<br />

Modify label<br />

Margin<br />

<br />

XXXXXXX|XXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX<br />

XXXXXXX|XXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX<br />

XXXX Height XXXX XXXXXXXXXXXXXXXX<br />

XXXXXXX|XXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX<br />

XXXXXXX|XXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX<br />

|<br />

| Lines <br />

| Spaces<br />

XX Line 1 XXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX<br />

XX Line 2 XXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX<br />

XX Line 3 XXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX<br />

XX Line 4 XXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX<br />

XX Line 5 XXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX<br />

XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX<br />

XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX<br />

XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX<br />

XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX<br />

XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX<br />

| | |<br />

| | |<br />

| | |<br />

+---------------- Across ---------------+<br />

This function permits you to modify a label file.<br />

Label form<br />

This function permits you to print labels with the data provided by the active alias: one<br />

label each record.<br />

The following data is required.<br />

LABEL FILENAME the label filename.<br />

WHILE<br />

the WHILE condition: the label printing goes on as long<br />

as this condition remain True.<br />

FOR<br />

the FOR condition: only the records from the active alias<br />

that satisfy the condition are used for the label print.


nanoBase 1997 user manual 183<br />

New report<br />

This function permits you to create a standard report form file (.FRM under the dBaseIII<br />

standard).<br />

The informations to create a ‘.FRM’ file are divided into two parts: the head and groups; the<br />

columns.<br />

The first part: head and groups, requires the folliwing informations:<br />

PAGE WIDTH<br />

the page width in characters.<br />

LINES PER PAGE<br />

the usable lines per per page.<br />

LEFT MARGIN<br />

the left margin in characters.<br />

DOUBLE SPACED?<br />

double spaced print, yes or no.<br />

PAGE EJECT BEFORE PRINT? form feed before print, yes or no.<br />

PAGE EJECT AFTER PRINT? form feed after print, yes or no.<br />

PLAIN PAGE?<br />

plain page, yes or no.<br />

PAGE HEADER<br />

the page header, max 4 lines (the separation between one<br />

line and the other is obtained writing a semicolon, ";").<br />

GROUP HEADER<br />

the group title.<br />

GROUP EXPRESSION<br />

the group expression (when it changes, the group changes)<br />

SUMMARY REPORT ONLY? only totals and no columns, yes or no.<br />

PAGE EJECT AFTER GROUP? form feed when the group changes, yes or no.<br />

SUB GROUP HEADER<br />

sub group title.<br />

SUB GROUP EXPRESSION the sub group expression.<br />

The second part: columns, requires the following informations structured in table form:<br />

COLUMN HEADER<br />

column head description (it can contain 4 lines separated<br />

with a semicolon).<br />

CONTENT<br />

the column expression.<br />

WIDTH the column width.<br />

DEC.<br />

the decimal length for numeric columns.<br />

TOTALS<br />

totals to be calculated, yes or no (usefull only for numeric<br />

columns).<br />

To navigate and to edit the table use the following keys:<br />

[ Up ]/[ Down ]/[ Left ][ Right ] move the cursor one position (up, down, left or right);<br />

[ PgUp ] move to previous screen page;<br />

[ PgDn ] move to next screen page;<br />

[ Ctrl+PgUp ] move to top <strong>of</strong> table;<br />

[ Ctrl+PgDn ] move to bottom <strong>of</strong> table;<br />

[ Ctrl+Home ] move to first column;<br />

[ Ctrl+End ] move to last column;<br />

[ Ctrl+Enter ] append a new empty line;<br />

[ Ctrl+F1 ]<br />

delete (cut) the current line and save a copy into the "clipboard";<br />

[ Ctrl+F2 ] copy current line into the table "clipboard";<br />

[ Ctrl+F3 ]<br />

insert (paste) the content <strong>of</strong> the "clipboard" in the current<br />

position;<br />

[ Enter ] start editing in the current position;<br />

[ Esc ] terminate;<br />

[ x ] any other key will be written in the current position.<br />

When the editing is terminated, press [ Esc ] and a dialog box will ask for the name to give<br />

to the report form file.<br />

Modify report<br />

This function permits you to modify a standard report form file (.FRM under the dBaseIII


184 volume VIII Argomenti avanzati e accessori<br />

standard).<br />

Report form<br />

This function permits you to print a report form with the data provided by the active alias.<br />

The following data is required.<br />

REPORT FORM FILENAME the label filename.<br />

WHILE<br />

the WHILE condition: the form printing goes on as long<br />

as this condition remain True.<br />

FOR<br />

the FOR condition: only the records from the active alias<br />

that satisfy the condition are used for the report form print.<br />

Create/modify/print text<br />

This function activates the text editor.<br />

368.6.4 Menu HTF<br />

The menu Htf helps on creating and accessing the "Help Text Files". This name, help text file, is<br />

just the name given to it.<br />

A text (Ascii) file prepared like this manual may be transformed into a "Help Text File" that is a<br />

simple text with pointers.<br />

Open help text file<br />

This function permits to open a Help Text File and browse it. The Help Text File name is<br />

required.<br />

New help text file<br />

This function permits to create a new "Help Text File" that is a help file under the nB style.<br />

The source is an Ascii file where three kind <strong>of</strong> information are available: Normal text,<br />

<strong>Index</strong>es and pointers.<br />

<strong>Index</strong>es and Pointers are word or phrases delimited with user defined delimiters; indexes<br />

are placed inside the text to indicate an argument, pointers are placed inside the text to<br />

indicate a reference to indexes.<br />

Inside this manual, indexes are delimited with ## and ##, so the titles are here indexes;<br />

pointers are delimited with < and >.<br />

Only one index per line is allowed, only one pointer per line is allowed.<br />

The Delimiters used do identify indexes and pointers are user defined; the _start_ identifier<br />

symbol can be equal to the _end_ identifier symbol. The symbols used for indexes cannot<br />

be used for the pointers.<br />

So, the informations required are:<br />

SOURCE TEXT FILENAME the filename <strong>of</strong> the text source file.<br />

DESTINATION FILENAME<br />

the filename <strong>of</strong> the destination Help Text File (suggested<br />

‘.HLP’ extention).<br />

INDEX START CODE<br />

the index start symbol; suggested ##.<br />

INDEX END CODE<br />

the index end symbol; suggested ##.<br />

POINTER START CODE<br />

the pointer start symbol; suggested .


nanoBase 1997 user manual 185<br />

New HTML file<br />

This function permits to create a new HTML file form a text file formatted to obtain a HTF<br />

file.<br />

The informations required are:<br />

SOURCE TEXT FILENAME the filename <strong>of</strong> the text source file.<br />

DESTINATION FILENAME<br />

the filename <strong>of</strong> the destination Help Text File (suggested<br />

‘.HLP’ extention).<br />

INDEX START CODE<br />

the index start symbol; suggested ##.<br />

INDEX END CODE<br />

the index end symbol; suggested ##.<br />

POINTER START CODE<br />

the pointer start symbol; suggested .<br />

HTML TITLE the title for the html page.<br />

368.6.5 Menu Macro<br />

The menu Macro helps on creating macros (programs) with a macro recorder, a macro "compiler"<br />

and a macro executor.<br />

Start recording<br />

This function simply starts or pause the macro recording. The menu items that end with<br />

"&", may be recorded by this macro recorder.<br />

Save recording<br />

A recorded macro may be saved into a ASCII file that may be later modified or simply used<br />

as it is. The filename is requested.<br />

Erase recording<br />

While recording or when the macro recorder is paused, it is possible to erase all previous<br />

recording with this function.<br />

Edit recording<br />

While recording or when the macro recorder is paused, it is possible to edit all previous<br />

recording, for example adding more comments or simply to see what the recorder does.<br />

Macro compilation<br />

A macro file (a program) contained inside a ASCII file, may be compiled into a different<br />

file format to speed up execution. The source filename and the destination filename are<br />

requested.<br />

Load + execute macro<br />

A macro file (a program) in ASCII form or compiled, may be executed.<br />

A macro file may require some parameters.<br />

This function asks for the macro filename to start and the possible parameter to pass to it.


186 volume VIII Argomenti avanzati e accessori<br />

368.6.6 Menu Info<br />

The menu Info is the information menu.<br />

ABOUT<br />

a brief copyright notice.<br />

starts the browse <strong>of</strong> ‘NB.HLP’ , the nB Help Text File manual<br />

MANUAL BROWSE<br />

if it is present in the current directory or it is found in the<br />

PATH (the Dos SET PATH).<br />

[F1] HELP [F1] reminder.<br />

[F3] ALIAS INFO<br />

[F3] reminder. It shows all the available information on the<br />

active alias.<br />

[F5] SET OUTPUT TO [F5] reminder. It defines the output peripheral or file.<br />

368.6.7 Menu Doc<br />

This menu actually appears only inside the DOC() function, the nB text editor.<br />

New<br />

Open<br />

Save<br />

Save as<br />

It starts the editing <strong>of</strong> a new empty text.<br />

It opens for editing a new textfile.<br />

It saves the text file under editing.<br />

It saves the text file under editing asking for a new name.<br />

Set output to<br />

It permits to change the default output peripheral: the default is the screen.<br />

Print as it is<br />

It prints on the output peripheral the content <strong>of</strong> the text as it is.<br />

Print with RPT() once<br />

It prints on the output peripheral the content <strong>of</strong> the text only once replacing possible text<br />

variables.<br />

Print with RPT() std<br />

It prints on the output peripheral the content <strong>of</strong> the text repeating this print for every record<br />

contained inside the archive alias.<br />

Exit DOC()<br />

Terminates the use <strong>of</strong> DOC() the text/document editing/print function.


nanoBase 1997 user manual 187<br />

368.7 The text editor DOC()<br />

The function Doc() activates a simple text editor usefull to build some simple reports.<br />

Inside this function a menu is available and is activated pressing [ Alt+M ] or [ F10 ]. The Doc()<br />

menu is part <strong>of</strong> the nB menu system.<br />

DOC() may handle text files <strong>of</strong> a teorical maximum <strong>of</strong> 64K.<br />

DOC() may be particularly useful to create formatted text with variables identified by CHR(174)<br />

and CHR(175) delimiters: when an active alias exists, [ F2 ] gives a list <strong>of</strong> insertable fields.<br />

[ Esc ] Exit DOC().<br />

[ F1 ] Call the help.<br />

[ F2 ] Field list.<br />

[ up ] / [ Ctrl+E ] Line up.<br />

[ down ] / [ Ctrl+X ] Line down.<br />

[ left ] / [ Ctrl+S ] Character left.<br />

[ right ] / [ Ctrl+D ] Character right.<br />

[ Ctrl+right ] / [ Ctrl+A ] Word left.<br />

[ Ctrl+left ] / [ Ctrl+F ] Word right.<br />

[ Home ] Line start.<br />

[ End ] Line end.<br />

[ Ctrl+Home ] Top window.<br />

[ Ctrl+End ] Bottom window.<br />

[ PgUp ] Previous window.<br />

[ PgDn ] Next window.<br />

[ Ctrl+PgUp ] Document start.<br />

[ Ctrl+PgDn ] End document.<br />

[ Del ] Delete character (right).<br />

[ Backspace ] Delete character Left.<br />

[ Tab ] Insert tab.<br />

[ Ins ] Toggle insert/overwrite.<br />

[ Enter ] Next line.<br />

[ Ctrl+Y ] Delete line.<br />

[ Ctrl+T ] Delete word right.<br />

[ F10 ] / [ Alt+M ] DOC() menu.<br />

368.8 The help text file<br />

nB provides a basic hypertext system to build simple help files. A source text file with "indexes"<br />

and "pointers" to indexes is translated into a "help text file" (a ‘.DBF’ file); then, this file is<br />

browsed by nB.<br />

The source file can have a maximum line width <strong>of</strong> 80 characters; each line can terminate with<br />

CR or CR+LF.<br />

"<strong>Index</strong>es" are string delimited by index delimiters (default "##"); "pointers" are string delimited<br />

by pointer delimiters (default "") and refers to indexes.<br />

Inside a text, indexes must be unique; pointers can be repeated anywhere. A text can contain a<br />

maximum <strong>of</strong> 4000 indexes.<br />

Inside this manual, titles are delimited with "##" as they are indexes; strings delimited with "" identify a reference to a title with the same string.


188 volume VIII Argomenti avanzati e accessori<br />

To browse a previously created Help Text File, use the following keys:<br />

[ Esc ] Exit.<br />

[ UpArrow ] Move cursor up.<br />

[ DownArrow ] Move cursor down.<br />

[ PgUp ] Move cursor PageUp.<br />

[ PgDn ] Move cursor Pagedown.<br />

[ Ctrl+PgUp ] Move cursor Top.<br />

[ Ctrl+PgDn ] Move cursor Bottom.<br />

[ Enter ] Select a reference (pointer).<br />

[ ] Go to next selected reference (pointer).<br />

[ Shift+F3 ] Search for a new pattern.<br />

[ F3 ] Repeat previous search.<br />

368.9 Macro<br />

nB can execute (run) macro files. There may be three kind <strong>of</strong> macro files: ASCII (usually with<br />

.& extention); "compiled" (usually with .NB extention); EXE files (compiled with Clipper and<br />

linked).<br />

"Compiled" macro files are executed faster then the ASCII source files.<br />

EXE macro files are the fastest.<br />

368.9.1 Macro statements<br />

The statements recognised from nB are very similar to Clipper, with some restrictions.<br />

Note that: the FOR statement is not included; there is no function declaration; procedure calls<br />

cannot transfer variables; only public variables are allowed.<br />

PROCEDURE<br />

Procedures are the basic building blocks <strong>of</strong> a nB macro.<br />

Procedures are visible only inside the current macro file.<br />

The procedure structure is as follows:<br />

PROCEDURE procedure_name<br />

statements...<br />

[RETURN]<br />

statements...<br />

ENDPROCEDURE<br />

A procedure definition begins with a PROCEDURE declaration followed with the<br />

procedure_name and ends with ENDPROCEDURE.<br />

Inside the PROCEDURE - ENDPROCEDURE declaration are placed the executable<br />

statements which are executed when the procedure is called.<br />

Inside the PROCEDURE - ENDPROCEDURE declaration, the RETURN statement may<br />

appear. In this case, encountering this RETURN statement, the procedure execution is immediately<br />

terminated and control is passed to the statement following the calling one.<br />

The procedure definition do not permit to receive parameters from the calling statement.


nanoBase 1997 user manual 189<br />

DO PROCEDURE<br />

There is only one way to call a procedure:<br />

DO PROCEDURE procedure_name<br />

When the statement DO PROCEDURE is encountered, the control is passed to the begin <strong>of</strong><br />

the called PROCEDURE. After the PROCEDURE execution, the control is returned to the<br />

statement following DO PROCEDURE.<br />

The procedure call do not permit to send parameters to the procedure.<br />

BEGIN SEQUENCE<br />

The BEGIN SEQUENCE - END structure permits to define a sequence <strong>of</strong> operation that<br />

may be broken.<br />

Inside nB, this control structure is useful only because there is the possibility to break the<br />

execution and pass control over the end <strong>of</strong> it.<br />

This way, encountering BREAK means: "go to end".<br />

BEGIN SEQUENCE<br />

statements...<br />

[BREAK]<br />

statements...<br />

END<br />

Inside nB, error exception handling is not supported.<br />

DO CASE<br />

This is a control structure where only the statements following a True CASE condition are<br />

executed.<br />

When the DO CASE statement is encountered, the following CASE statements are tested.<br />

The first time that a condition returns True, the CASE’s statements are executed and then<br />

control is passed over the END case.<br />

That is: only one CASE is taken into consideration.<br />

If no condition is True, the statements following OTHERWISE are executed.<br />

DO CASE<br />

CASE lCondition1<br />

statements...<br />

[CASE lCondition2]<br />

statements...<br />

[OTHERWISE]<br />

statements...<br />

END<br />

WHILE<br />

The structure WHILE - END defines a loop based on a condition: the loop is repeated until<br />

the condition is True.<br />

The loop execution may be broken with the EXIT statement: it transfer control after the<br />

END while.<br />

The LOOP statement may be use to repeat the loop: it transfer the control to the beginning<br />

<strong>of</strong> the loop.<br />

WHILE lCondition<br />

statements...<br />

[EXIT]<br />

statements...<br />

[LOOP]<br />

statements...<br />

END


190 volume VIII Argomenti avanzati e accessori<br />

IF<br />

The IF - END control structure executes a section <strong>of</strong> code if a specified condition is True.<br />

The structure can also specify alternative code to execute if the condition is False.<br />

IF lCondition1<br />

statements...<br />

[ELSE]<br />

statements...<br />

END<br />

368.9.2 Variable declaration<br />

Inside nB, variables are created using a specific function:<br />

MEMPUBLIC( "cVarName" )<br />

For example,<br />

MEMPUBLIC( "Name" )<br />

creates the variable Name.<br />

The scope <strong>of</strong> the created variable is global and there is no way to restrict the visibility <strong>of</strong> it.<br />

When a variable is no more needed or desired, it can be released:<br />

MEMRELEASE( "cVarName" )<br />

The variable declaration do not defines the variable type. Every variable may receive any kind <strong>of</strong><br />

data; that is that the type depends on the type <strong>of</strong> data contained.<br />

368.9.3 Macro structure<br />

A nB macro must be organised as follow. There may be two situations: Macros with procedures<br />

and macros without procedures.<br />

Macro with procedures:<br />

PROCEDURE procedure_name_1<br />

statements...<br />

[RETURN]<br />

statements...<br />

ENDPROCEDURE<br />

PROCEDURE procedure_name_2<br />

statements...<br />

[RETURN]<br />

statements...<br />

ENDPROCEDURE<br />

...<br />

...<br />

DO PROCEDURE procedure_name_n<br />

Macro without procedures:<br />

statements...<br />

statements...<br />

statements...<br />

statements...<br />

statements...<br />

nB Macros may be compiled with Clipper. To do so, the first structure example must be changed<br />

as follows:


nanoBase 1997 user manual 191<br />

#INCLUDE MACRO.CH<br />

DO PROCEDURE procedure_name_nth<br />

...<br />

PROCEDURE procedure_name_1<br />

statements...<br />

[RETURN]<br />

statements...<br />

ENDPROCEDURE<br />

PROCEDURE procedure_name_2<br />

statements...<br />

[RETURN]<br />

statements...<br />

ENDPROCEDURE<br />

...<br />

...<br />

To compile a macro with Clipper, the macro file name can be changed into ‘MACRO.PRG’ and<br />

RTLINK MACRO.RMK [Enter]<br />

should be started.<br />

368.9.4 Macro comments<br />

A nB Macro source file can contain comments. only the "//" comment is recognised! This way:<br />

* and /*...*/ will generate errors!<br />

ATTENTION: to simplify the macro interpretation, lines such as this:<br />

qqout( "You can’t do that // you can’t do that!" )<br />

will generate an error as the interpreter will read only:<br />

qqout( "You can’t do that<br />

Sorry!<br />

368.9.5 Macro long lines split<br />

Inside a nB macro, long lines may be splitted using ";" (semicolon). Please note that: lines can<br />

only be splitted and not joined; a resulting command line cannot be longer then 254 characters.<br />

368.9.6 The macro recorder<br />

Inside the functions ASSIST() and DOC() is available the Macro recorder menu.<br />

When a macro recording is started, a "&" appears on the left side <strong>of</strong> the status bar. It it blinks, the<br />

recording is active, if it is stable, the recording is paused.<br />

The macro recording is not exactly a step-by-step recording <strong>of</strong> all action taken, but a translation<br />

(as good as possible) <strong>of</strong> what you have done.


192 volume VIII Argomenti avanzati e accessori<br />

The macro recorder is able to record only the menu functions that terminates with the "&" symbol<br />

and all what is inserted at the dot command line.<br />

The macro recording can be viewed and edited during the recording. The macro recording can<br />

be saved into a text file (a macro file).<br />

368.10 Data types<br />

The data types supported in the nB macro language are the same as Clipper:<br />

Array<br />

Character<br />

Code Block<br />

Numeric<br />

Date<br />

Logical<br />

Memo<br />

NIL<br />

368.10.1 Character<br />

The character data type identifies character strings <strong>of</strong> a fixed length. The character set corresponds<br />

to: CHR(32) through CHR(255) and the null character, CHR(0).<br />

Valid character strings consist <strong>of</strong> zero or more characters with a theoretical maximum <strong>of</strong> 65535<br />

characters. The real maximum dimension depends on the available memory.<br />

Character string constants are formed by enclosing a valid string <strong>of</strong> characters within a designed<br />

pair <strong>of</strong> delimiters. There are three possible delimiter pairs:<br />

two single quotes like ‘’string_constant’’;<br />

two double quotes like ‘"string_constant"’;<br />

left and right square brackets like ‘[string_constant]’.<br />

These three different kind <strong>of</strong> delimiters are available to resolve some possible problems:<br />

I don’t want it -> "I don’t want it"<br />

She said, "I love hin" -> ’She said, "I love hin"’<br />

He said, "I don’t want it" -> [He said, "I don’t want it"]<br />

The following table shows all operations available inside nB for character data types. These<br />

operations act on one or more character expressions and the result is not necessarily a character<br />

data type.<br />

+ Concatenate.<br />

- Concatenate without intervening spaces.<br />

== Compare for exact equity.<br />

!=, , # Compare for inequity.<br />

< Compare for sorts before


nanoBase 1997 user manual 193<br />

Compare for sorts after.<br />

>= Compare for sorts after or same as.<br />

:= In line assign.<br />

$ Test for substring existence.<br />

ALLTRIM() Remove leading and trailing spaces.<br />

ASC() Convert to numeric ASCII code equivalent.<br />

AT() Locate substring position.<br />

CTOD() Convert to date.<br />

DESCEND() Convert to complemented form.<br />

EMPTY() Test for null or blank string.<br />

ISALPHA() Test for initial letter.<br />

ISDIGIT() Test for initial digit.<br />

ISLOWER() Test for initial lowercase letter.<br />

ISUPPER() Test for initial uppercase letter.<br />

LEFT() Extract substring form the left.<br />

LEN() Compute string length in characters.<br />

LOWER() Convert letters to lowercase.<br />

LTRIM() Remove leading spaces.<br />

PADC() Pad with leading and trailing spaces.<br />

PADL() Pad with leading spaces.<br />

PADR() Pad with trailing spaces.<br />

RAT() Locate substring position starting from the right.<br />

RIGHT() Extract substring form the right.<br />

RTRIM() Remove trailing spaces.<br />

SOUNDEX() Convert to soundex equivalent.<br />

SPACE() Create a blank string <strong>of</strong> a defined length.<br />

STRTRAN() Search and replace substring.<br />

STUFF() Replace substring.<br />

SUBSTR() Extract substring.<br />

TRANSFORM() Convert to formatted string.<br />

UPPER() Convert letters to uppercase<br />

VAL() Convert to numeric.<br />

VALTYPE() Evaluates data type directly.<br />

368.10.2 Memo<br />

The memo data type is used to represent variable length character data that can only exist in the<br />

form <strong>of</strong> a database field.<br />

Memo fields are not stored inside the main database file (.DBF) but inside a separate file (.DBT).<br />

A memo field can contain up to 65535 characters, that is the same maximum dimension <strong>of</strong> character<br />

fields. In fact, originally xBases, couldn’t have character string longer than 254 characters.<br />

As here memo fields are very similar to long character strings, you may forget that there is a<br />

difference.<br />

All the operations that may be applied to character strings, may be used with memo fields; the<br />

following functions may be use especially for memo fields or long character strings.<br />

HARDCR() Replace s<strong>of</strong>t with hard carriage returns.<br />

MEMOEDIT() Edit contents.<br />

MEMOLINE() Extract a line <strong>of</strong> a text.


194 volume VIII Argomenti avanzati e accessori<br />

MEMOREAD() Read form a disk text file.<br />

MEMOTRAN() Replace s<strong>of</strong>t and hard carriage returns.<br />

MEMOWRIT() Write to disk text file.<br />

MLCOUNT() Count lines.<br />

MLPOS() Compute position.<br />

368.10.3 Date<br />

The date data type is used to represent calendar dates.<br />

Supported dates are from 0100.01.01 to 2999.12.31 and null or blank date.<br />

The appearance <strong>of</strong> a date is controlled from SETVERB("DATEFORMAT"). The default is<br />

"dd/mm/yyyy" and it may easily changed for example with SETVERB("DATEFORMAT",<br />

"MM/DD/YYYY") to the US standard.<br />

There is no way to represent date constants; these must be replaced with the CTOD() function.<br />

For example if the date 11/11/1995 is to be written, the right way is:<br />

CTOD( "11/11/1995" )<br />

The character string "11/11/1995" must respect the date format defined as before explained.<br />

The function CTOD() will accept only valid dates, and null dates:<br />

CTOD( "" )<br />

A null date is ever less than any other valid date.<br />

The following table shows all operations available inside nB for date data types. These operations<br />

act on one or more date expressions and the result is not necessarily a character data type.<br />

+ Add a number <strong>of</strong> days to a date.<br />

- Subtract days to a date.<br />

== Compare for equity.<br />

!=, , # Compare for inequity.<br />

< Compare for earlier<br />

Compare for later.<br />

>= Compare for later or same as.<br />

:= In line assign.<br />

CDOW() Compute day <strong>of</strong> week name.<br />

CMONTH() Compute month name.<br />

DAY() Extract day number.<br />

DESCEND() Convert to complemented form.<br />

DOW() Compute day <strong>of</strong> week.<br />

DTOC()<br />

Convert to character string with the format defined with<br />

SETVERB( "DATEFORMAT" ).<br />

DOTOS() Convert to character string in sorting format (YYYYMMDD).<br />

EMPTY() Test for null date.<br />

MONTH() Extract month number.<br />

VALTYPE() Evaluates data type directly.<br />

YEAR() Extract entire year number, including century.


nanoBase 1997 user manual 195<br />

368.10.4 Numeric<br />

The numeric data type identifies real number. The theoretical range is form 10^-308 to 10^308<br />

but the numeric precision is guaranteed up to 16 significant digits, and formatting a numeric<br />

value for display is guaranteed up to a length <strong>of</strong> 32 (30 digits, a sign, and a decimal point). That<br />

is: numbers longer than 32 bytes may be displayed as asterisks, and digits other then most 16<br />

significant ones are displayed as zeroes.<br />

Numeric constants are written without delimiters. The following are valid constant numbers:<br />

12345<br />

12345.678<br />

-156<br />

+1256.789<br />

-.789<br />

If a numeric constant is delimited like character strings, it becomes a character string.<br />

The following table shows all operations available inside nB for numeric data types. These operations<br />

act on one or more numeric expressions and the result is not necessarily a numeric data<br />

type.<br />

+ Add or Unary Positive.<br />

- Subtract or Unary Negative.<br />

*<br />

Multiply.<br />

/ Divide.<br />

% Modulus.<br />

^, ** Exponentiate.<br />

== Compare for equity.<br />

!=, , # Compare for inequity.<br />

< Compare for less than.<br />

>= Compare for less than or equal.<br />

> Compare for greater than.<br />

>= Compare for greater than or equal.<br />

:= In line assign.<br />

ABS() Compute absolute value.<br />

CHR() Convert to ASCII character equivalent.<br />

DESCEND() Convert to complemented form.<br />

EMPTY() Test for zero.<br />

EXP() Exponentiate with e as the base.<br />

INT() Convert to integer.<br />

LOG() Compute natural logarithm.<br />

MAX() Compute maximum.<br />

MIN() Compute minimum.<br />

ROUND() Round up or down()<br />

SQRT() Compute square root.<br />

STR() Convert to character.<br />

TRANSFORM() Convert to formatted string.<br />

VALTYPE() Evaluates data type directly.<br />

Number appearence may be affected by SETVERB("FIXED") and consequently by<br />

SETVERB("DECIMALS"). If SETVERB("FIXED") is True, numbers are displayed with a fixed<br />

decimal position. The number <strong>of</strong> decimal positions is defined by SETVERB("DECIMALS"). For


196 volume VIII Argomenti avanzati e accessori<br />

that reason, the default is SETVERB("FIXED", .F.) and SETVERB("DECIMALS", 2), that is,<br />

no fixed decimal position, but if they will be activated, the default is two decimal digits.<br />

368.10.5 Logical<br />

The logical data type identifies Boolean values.<br />

Logical constants are:<br />

‘.T.’ True.<br />

‘.F.’ False.<br />

When editing a logical field, inputs may be:<br />

y, Y, t, T for True<br />

n, N, f, F for False<br />

The following table shows all operations available inside nB for logical data types. These operations<br />

act on one or more logical expressions and the result is not necessarily a logical data<br />

type.<br />

.AND. And.<br />

.OR. Or.<br />

.NOT. or !<br />

Negate.<br />

== Compare for equity.<br />

!=, , or # Compare for inequity.<br />

Comparing two logical values, False (‘.F.’) is always less than True (‘.T.’).<br />

368.10.6 NIL<br />

NIL is not properly a data type, it represent the value <strong>of</strong> an uninitialised variable.<br />

Inside nB (like what it happens inside Clipper), variables are not declared with the data type that<br />

they will contain. This means that a variable can contain any kind <strong>of</strong> data. In fact, nB variables<br />

are pointer to data and a pointer to "nothing" is NIL.<br />

NIL may be used as constant for assignment or comparing purpose:<br />

NIL<br />

Fields (database fields) cannot contain NIL.<br />

The following table shows all operations available inside nB for the NIL data type. Except for<br />

these operations, attempting to operate on a NIL results in a runtime error.<br />

== Compare for equity.<br />

!=, , # Compare for inequity.<br />

< Compare for less than.<br />

Compare for greater than.<br />

>= Compare for greater than or equal.<br />

:= In line assign.<br />

EMPTY() Test for NIL.<br />

VALTYPE() Evaluates data type directly.


nanoBase 1997 user manual 197<br />

For the purpose <strong>of</strong> comparison, NIL is the only value that is equal to NIL. All other values are<br />

greater than NIL.<br />

Variables are created inside nB with MEMPUBLIC(). This function creates variables which will<br />

be automatically initialised to NIL.<br />

368.10.7 Array<br />

The array data type identifies a collection <strong>of</strong> related data items that share the same name. Each<br />

value in an array is referred to as an element.<br />

Array elements can be <strong>of</strong> any data type except memo (memo is available only inside database<br />

fields). For example the first element can be a character string, the second a number, the third a<br />

date and so on. Arrays can contain other arrays and code blocks as elements.<br />

The variable containing the array does not contains the entire array, but the reference to it.<br />

When the NIL type was described, it was cleared that variables doesn’t contains real data,<br />

but pointer to data. But this happens in a transparent way, that is that when the a variable is<br />

assigned to another (for example A := B) the variable receiving the assignment will receive<br />

a pointer to a new copy <strong>of</strong> the source data. This is not the same with arrays: assigning to a<br />

variable an array, will assign to that variable a pointer to the same source array and not to a<br />

new copy <strong>of</strong> it.<br />

If arrays are to be duplicated, the ACLONE() function is to be used.<br />

An array constant may be expressed using curly brackets {}. See the examples below.<br />

A := { "first_element", "second_element", "third_element" }<br />

With this example, the variable A contain the reference to an array with three element containing<br />

character string.<br />

A[1] == "first_element"<br />

A[2] == "second_element"<br />

A[3] == "third_element"<br />

Arrays may contain also no element: empty array and may be expressed as:<br />

{}<br />

The array element is identified by a number enclosed with square brackets, following the variable<br />

name containing the reference to the array. The first array element is one.<br />

If an array contains arrays, we obtain a multidimensional array. For example:<br />

A := { { 1, 2 }, { 3, 4 }, { 5, 6 } }<br />

is equivalent to the following table.<br />

1 2<br />

3 4<br />

5 6


198 volume VIII Argomenti avanzati e accessori<br />

With this example, the variable A contain the reference to a bidimensional array containing numbers.<br />

A[1,1] or A[1][1] contains 1<br />

A[1,2] or A[1][2] contains 2<br />

A[2,1] or A[2][1] contains 3<br />

and so on.<br />

As arrays may contain mixed data, it is the user who have to handle correctly the element numbers.<br />

For example:<br />

A := { "hello", { 3, 4 }, 1234 }<br />

A[1] == "hello"<br />

A[2] == reference to { 3, 4 }<br />

A[3] == 1234<br />

A[2,1] or A[2][1] contains 3<br />

A[2,2] or A[2][2] contains 4<br />

A[1,1] is an error!<br />

The following table shows all operations available inside nB for arrays.<br />

:= In line assign.<br />

AADD() Add dynamically an element to an array.<br />

ACLONE() Create a copy <strong>of</strong> an array.<br />

ACOPY() Copy element by element an array to another.<br />

ADEL() Delete one element inside an array.<br />

AFILL() Fill all array elements with a value.<br />

AINS() Insert an element inside an array.<br />

ARRAY() Creates an array <strong>of</strong> empty elements.<br />

ASCAN() Scan the array elements.<br />

ASIZE() Resize an array.<br />

ASORT() Sort the array elements.<br />

EMPTY() Test for no elements.<br />

VALTYPE() Evaluates data type directly.<br />

368.10.8 Code block<br />

The code block data type identifies a small piece <strong>of</strong> executable program code.<br />

A code block is something like a little user defined function where only a sequence <strong>of</strong> functions<br />

or assignments may appear: no loops, no IF ELSE END.<br />

A code block may receive argument and return a value after execution, just like a function.<br />

The syntax is:<br />

{ | [argument_list] | exp_list }<br />

That is: the argument_list is optional; the exp_list may contain one or more expressions separated<br />

with a comma.


nanoBase 1997 user manual 199<br />

For example, calling the following code block will give the string "hello world" as result.<br />

{ || "hello world" }<br />

The following code block require a numeric argument an returns the number passed as argument<br />

incremented:<br />

{ | n | n+1 }<br />

The following code block requires two numeric arguments and returns the sum <strong>of</strong> the two square<br />

radix:<br />

{ | nFirst, nSecond | SQRT(nFirst) + SQRT(nSecond) }<br />

But code blocks may contains more expressions and the result <strong>of</strong> the execution <strong>of</strong> the code block<br />

is the result <strong>of</strong> the last expression.<br />

The following code block executes in sequence some functions and give ever "hello world" as a<br />

result.<br />

{ | a, b | functionOne(a), functionTwo(b), "hello world" }<br />

To start the execution <strong>of</strong> a code block a function is used: EVAL()<br />

For example, a code block is assigned to a variable and then executed.<br />

B := { || "hello world" }<br />

EVAL( B ) == "hello world"<br />

Another example with a parameter.<br />

B := { | n | n+1 }<br />

EVAL( B, 1 ) == 2<br />

Another example with two parameters.<br />

B := { | nFirst, nSecond | SQRT(nFirst) + SQRT(nSecond) }<br />

EVAL( B, 2, 4 ) == 20<br />

And so on.<br />

The following table shows some operations available inside nB for code blocks: many functions<br />

use code blocks as argument.<br />

:= In line assign.<br />

AEVAL() Evaluate (execute) a code block for each element in an array.<br />

BCOMPILE() Convert (compile) a character string into a code block.<br />

DBEVAL()<br />

Evaluate (execute) a code block for each record in the active<br />

alias.<br />

EVAL() Evaluate a code block once.<br />

VALTYPE() Evaluates data type directly.


200 volume VIII Argomenti avanzati e accessori<br />

368.11 Operators<br />

Here is a list with a brief description <strong>of</strong> the operators available inside nB.<br />

cString1 $ cString2<br />

Substring comparison.<br />

If cString1 is contained inside cString2 the result is true (‘.T.’).<br />

nNumber1 % nNumber2<br />

Modulus.<br />

The result is the remainder <strong>of</strong> nNumber1 divided by nNuber2.<br />

()<br />

Function or grouping indicator.<br />

nNumber1 * nNumber2<br />

Multiplication.<br />

nNumber1 ** nNumber2<br />

nNumber1 ^ nNumber2<br />

Exponentiation.<br />

nNumber1 + nNumber2<br />

dDate + nNumber<br />

Addition, unary positive.<br />

cString1 + cString2<br />

String concatenation.<br />

The result is a string beginning with the content <strong>of</strong> cString1 and following with the content <strong>of</strong><br />

cString2.<br />

nNumber1 - nNumber2<br />

dDate1 - dDate2<br />

dDate - nNumber<br />

Subtraction, unary negative.<br />

cString1 - cString2<br />

String concatenation.<br />

The result is a string containing cString1 after trimming trailing blanks and cString2.<br />

idAlias->idField<br />

FIELD->idVar<br />

MEMVAR->idVar<br />

Alias assignment.<br />

The alias operator implicitly SELECTs the idAlias before evaluating idField. When the evaluation<br />

is complete, the original work area is SELECTed again.<br />

lCondition1 .AND. lCondition2<br />

Logical AND.<br />

.NOT. lCondition


nanoBase 1997 user manual 201<br />

Logical NOT.<br />

lCondition1 .OR. lCondition2<br />

Logical OR.<br />

nNumber1 / nNumber2<br />

Division.<br />

object:message[(argument list)]<br />

Send.<br />

idVar := exp<br />

Inline assign.<br />

exp1 exp2<br />

Greater than.<br />

exp1 >= exp2<br />

Greater than or equal.<br />

@idVar<br />

Pass-by-reference.<br />

[]<br />

aArray[nSubscript, ...]<br />

aArray[nSubscript1][nSubscript2] ...<br />

Array element indicator.<br />

368.12 Delimiters<br />

Here is the delimiter list recognised from nB.<br />

{ exp_list }<br />

Literal array delimiters.<br />

{ |param_list| exp_list }<br />

Code block delimiters.<br />

"cString"<br />

’cString’<br />

[cString]


202 volume VIII Argomenti avanzati e accessori<br />

String delimiters.<br />

368.13 Code blocks<br />

A code block is a sequence <strong>of</strong> function, assignments and constant like the following:<br />

sqrt(10)<br />

nResult := 10 * n<strong>Index</strong><br />

Suppose that the above sequence <strong>of</strong> operations has a meaning for you. We want to create a box<br />

containing this sequence <strong>of</strong> operation. This box is contained inside a variable:<br />

bBlackBox := { || sqrt(10), nResult := 10 * n<strong>Index</strong> }<br />

Note the comma used as separator.<br />

Now bBlackBox contains the small sequence seen before. To execute this sequence, the function<br />

EVAL() is used:<br />

EVAL(bBlackBox)<br />

The execution <strong>of</strong> the code block gives a result: the value <strong>of</strong> the last operation contained inside<br />

the code block. In this case it is the result <strong>of</strong> 10*n<strong>Index</strong>. For that reason, if the execution <strong>of</strong> the<br />

code block must give a fixed result, it can terminate with a constant.<br />

A code block may receive parameters working like a function. Try to imagine that we need to do<br />

the following.<br />

function multiply( nVar1, nVar2 )<br />

return nVar * nVar2<br />

endfunction<br />

A code block that does the same is:<br />

bMultiply := { | nVar1, nVar2 | nVar1 * nVar2 }<br />

To evaluate it, for example trying to multiply 10 * 5:<br />

nResult := EVAL( bMultiply, 10, 5 )<br />

and nResult will contain 50.<br />

368.14 Standard functions<br />

With nB all Clipper standard functions may be used. Here follows a short description.<br />

368.14.1 AADD()<br />

Array add<br />

AADD(aTarget, expValue) ⇒ Value<br />

aTarget is the array to add a new element to.<br />

expValue is the value assigned to the new element.<br />

It increases the actual length <strong>of</strong> the target array by one. The newly created array element is


nanoBase 1997 user manual 203<br />

assigned the value specified by expValue.<br />

368.14.2 ABS()<br />

Absolute<br />

ABS(nExp) ⇒ nPositive<br />

nExp is the numeric expression to evaluate.<br />

ABS() returns a number representing the absolute value <strong>of</strong> its argument.<br />

368.14.3 ACLONE()<br />

Array clone<br />

ACLONE(aSource) ⇒ aDuplicate<br />

aSource<br />

ACLONE() returns a duplicate <strong>of</strong> aSource.<br />

368.14.4 ACOPY()<br />

Array copy<br />

is the array to duplicate.<br />

ACOPY(aSource, aTarget,<br />

[nStart], [nCount], [nTargetPos]) ⇒ aTarget<br />

aSource<br />

is the array to copy elements from.<br />

aTarget is the array to copy elements to.<br />

is the starting element position in the aSource array. If not<br />

nStart<br />

specified, the default value is one.<br />

is the number <strong>of</strong> elements to copy from the aSource array<br />

beginning at the nStart position. If nCount is not specified,<br />

nCount<br />

all elements in aSource beginning with the starting element<br />

are copied.<br />

is the starting element position in the aTarget array to receive<br />

nTargetPos<br />

elements from aSource. If not specified, the default value is<br />

one.<br />

ACOPY() is an array function that copies elements from the aSource array to the aTarget array.<br />

The aTarget array must already exist and be large enough to hold the copied elements.


204 volume VIII Argomenti avanzati e accessori<br />

368.14.5 ADEL()<br />

Array delete<br />

ADEL(aTarget, nPosition) ⇒ aTarget<br />

aTarget is the array to delete an element from.<br />

nPosition<br />

is the position <strong>of</strong> the target array element to delete.<br />

ADEL() is an array function that deletes an element from an array. The contents <strong>of</strong> the specified<br />

array element is lost, and all elements from that position to the end <strong>of</strong> the array are shifted up one<br />

element. The last element in the array becomes NIL.<br />

368.14.6 AEVAL()<br />

Array evaluation<br />

AEVAL(aArray, bBlock,<br />

[nStart], [nCount]) ⇒ aArray<br />

aArray is the array to be evaluated.<br />

bBlock is a code block to execute for each element encountered.<br />

nStart<br />

is the starting element. If not specified, the default is element<br />

one.<br />

nCount<br />

is the number <strong>of</strong> elements to process from nStart. If not specified,<br />

the default is all elements to the end <strong>of</strong> the array.<br />

AEVAL() is an array function that evaluates a code block once for each element <strong>of</strong> an array,<br />

passing the element value and the element index as block parameters. The return value <strong>of</strong> the<br />

block is ignored. All elements in aArray are processed unless either the nStart or the nCount<br />

argument is specified.<br />

368.14.7 AFILL()<br />

Array fill<br />

AFILL(aTarget, expValue,<br />

[nStart], [nCount]) ⇒ aTarget<br />

aTarget is the array to fill.<br />

expValue<br />

is the value to place in each array element. It can be an expression<br />

<strong>of</strong> any valid data type.<br />

nStart<br />

is the position <strong>of</strong> the first element to fill. If this argument is<br />

omitted, the default value is one.<br />

is the number <strong>of</strong> elements to fill starting with element nStart.<br />

nCount<br />

If this argument is omitted, elements are filled from the starting<br />

element position to the end <strong>of</strong> the array.<br />

AFILL() is an array function that fills the specified array with a single value <strong>of</strong> any data type<br />

(including an array, code block, or NIL) by assigning expValue to each array element in the<br />

specified range.


nanoBase 1997 user manual 205<br />

368.14.8 AINS()<br />

Array insert<br />

AINS(aTarget, nPosition) ⇒ aTarget<br />

aTarget is the array into which a new element will be inserted.<br />

nPosition<br />

is the position at which the new element will be inserted.<br />

AINS() is an array function that inserts a new element into a specified array. The newly inserted<br />

element is NIL data type until a new value is assigned to it. After the insertion, the last element<br />

in the array is discarded, and all elements after the new element are shifted down one position.<br />

368.14.9 ALERT()<br />

ALERT( cMessage, [aOptions] ) ⇒ nChoice<br />

is the message text displayed, centered, in the alert box. If the<br />

cMessage<br />

message contains one or more semicolons, the text after the<br />

semicolons is centered on succeeding lines in the dialog box.<br />

aOptions defines a list <strong>of</strong> up to 4 possible responses to the dialog box.<br />

ALERT() returns a numeric value indicating which option was chosen. If the Esc key is pressed,<br />

the value returned is zero. The ALERT() function creates a simple modal dialog. The user can<br />

respond by moving a highlight bar and pressing the Return or SpaceBar keys, or by pressing<br />

the key corresponding to the first letter <strong>of</strong> the option. If aOptions is not supplied, a single "Ok"<br />

option is presented.<br />

368.14.10 ALIAS()<br />

ALIAS([nWorkArea]) ⇒ cAlias<br />

nWorkArea<br />

is any work area number.<br />

ALIAS() returns the alias <strong>of</strong> the specified work area as a character string. If nWorkArea is not<br />

specified, the alias <strong>of</strong> the current work area is returned. If there is no database file in USE for the<br />

specified work area, ALIAS() returns a null string ("").<br />

368.14.11 ALLTRIM()<br />

ALLTRIM(cString) ⇒ cTrimmedString<br />

cString is the character expression to trim.<br />

ALLTRIM() returns a character string with leading and trailing spaces removed.


206 volume VIII Argomenti avanzati e accessori<br />

368.14.12 ARRAY()<br />

ARRAY(nElements [, nElements...]) ⇒ aArray<br />

nElements<br />

is the number <strong>of</strong> elements in the specified dimension.<br />

ARRAY() is an array function that returns an uninitialized array with the specified number <strong>of</strong><br />

elements and dimensions.<br />

368.14.13 ASC()<br />

ASCII<br />

ASC(cExp) ⇒ nCode<br />

cExp is the character expression to convert to a number.<br />

ASC() returns an integer numeric value in the range <strong>of</strong> zero to 255 , representing the ASCII value<br />

<strong>of</strong> cExp.<br />

368.14.14 ASCAN()<br />

Array scan<br />

ASCAN(aTarget, expSearch,<br />

[nStart], [nCount]) ⇒ nStoppedAt<br />

aTarget is the array to scan.<br />

is either a simple value to scan for, or a code block. If<br />

expSearch<br />

expSearch is a simple value it can be character, date, logical,<br />

or numeric type.<br />

is the starting element <strong>of</strong> the scan. If this argument is not spec-<br />

nStart<br />

ified, the default starting position is one.<br />

is the number <strong>of</strong> elements to scan from the starting position.<br />

nCount<br />

If this argument is not specified, all elements from the starting<br />

element to the end <strong>of</strong> the array are scanned.<br />

ASCAN() returns a numeric value representing the array position <strong>of</strong> the last element scanned.<br />

If expSearch is a simple value, ASCAN() returns the position <strong>of</strong> the first matching element, or<br />

zero if a match is not found. If expSearch is a code block, ASCAN() returns the position <strong>of</strong> the<br />

element where the block returned true (‘.T.’).<br />

368.14.15 ASIZE()<br />

Array size<br />

ASIZE(aTarget, nLength) ⇒ aTarget<br />

aTarget is the array to grow or shrink.<br />

nLength is the new size <strong>of</strong> the array.


nanoBase 1997 user manual 207<br />

ASIZE() is an array function that changes the actual length <strong>of</strong> the aTarget array. The array is<br />

shortened or lengthened to match the specified length. If the array is shortened, elements at the<br />

end <strong>of</strong> the array are lost. If the array is lengthened, new elements are added to the end <strong>of</strong> the array<br />

and assigned NIL.<br />

368.14.16 ASORT()<br />

Array sort<br />

ASORT(aTarget, [nStart],<br />

[nCount], [bOrder]) ⇒ aTarget<br />

aTarget is the array to sort.<br />

nStart<br />

is the first element <strong>of</strong> the sort. If not specified, the default<br />

starting position is one.<br />

nCount<br />

is the number <strong>of</strong> elements to sort. If not specified, all elements<br />

in the array beginning with the starting element are sorted.<br />

bOrder<br />

is an optional code block used to determine sorting order. If<br />

not specified, the default order is ascending.<br />

ASORT() is an array function that sorts all or part <strong>of</strong> an array containing elements <strong>of</strong> a single data<br />

type. Data types that can be sorted include character, date, logical, and numeric. If the bOrder<br />

argument is not specified, the default order is ascending. Each time the block is evaluated, two<br />

elements from the target array are passed as block parameters. The block must return true (‘.T.’)<br />

if the elements are in sorted order.<br />

368.14.17 AT()<br />

AT(cSearch, cTarget) ⇒ nPosition<br />

cSearch<br />

is the character substring for which to search.<br />

cTarget is the character string to search.<br />

AT() returns the position <strong>of</strong> the first instance <strong>of</strong> cSearch within cTarget as an integer numeric<br />

value. If cSearch is not found, AT() returns zero.<br />

AT() is a character function used to determine the position <strong>of</strong> the first occurrence <strong>of</strong> a character<br />

substring within another string.<br />

368.14.18 ATAIL()<br />

Array TAIL<br />

ATAIL(aArray) ⇒ Element<br />

aArray is the array.<br />

ATAIL() is an array function that returns the highest numbered element <strong>of</strong> an array. It can be used<br />

in applications as shorthand for aArray[LEN(aArray)] when you need to obtain the last element<br />

<strong>of</strong> an array.


208 volume VIII Argomenti avanzati e accessori<br />

368.14.19 BIN2I()<br />

Binary to integer<br />

BIN2I(cSignedInt) ⇒ nNumber<br />

cSignedInt<br />

is a character string in the form <strong>of</strong> a 16-bit signed integer<br />

number--least significant byte first.<br />

BIN2I() returns an integer obtained converting the first two byte contained inside cSignedInt.<br />

368.14.20 BIN2L()<br />

Binary to long<br />

BIN2L(cSignedInt) ⇒ nNumber<br />

cSignedInt<br />

is a character string in the form <strong>of</strong> a 32-bit signed integer<br />

number--least significant byte first.<br />

BIN2L() returns an integer obtained from the first tour characters contained in cSignedInt.<br />

368.14.21 BIN2W()<br />

Binary to word<br />

BIN2W(cUnsignedInt) ⇒ nNumber<br />

cUnsignedInt<br />

is a character string in the form <strong>of</strong> a 16-bit unsigned integer<br />

number--least significant byte first.<br />

BIN2W() returns an integer obtained from the first two characters contained in cSignedInt.<br />

368.14.22 BOF()<br />

Begin <strong>of</strong> file<br />

BOF() ⇒ lBoundary<br />

BOF() returns true (‘.T.’) after an attempt to SKIP backward beyond the first logical record in<br />

a database file; otherwise, it returns false (‘.F.’). If there is no database file open in the current<br />

work area, BOF() returns false (‘.F.’). If the current database file contains no records, BOF()<br />

returns true (‘.T.’).


nanoBase 1997 user manual 209<br />

368.14.23 CDOW()<br />

Character day <strong>of</strong> week<br />

CDOW(dExp) ⇒ cDayName<br />

dExp is the date value to convert.<br />

CDOW() returns the name <strong>of</strong> the day <strong>of</strong> the week as a character string. The first letter is uppercase<br />

and the rest <strong>of</strong> the string is lowercase. For a null date value, CDOW() returns a null string ("").<br />

368.14.24 CHR()<br />

Character<br />

CHR(nCode) ⇒ cChar<br />

nCode<br />

is an ASCII code in the range <strong>of</strong> zero to 255.<br />

CHR() returns a single character value whose ASCII code is specified by nCode.<br />

368.14.25 CMONTH()<br />

Character month<br />

CMONTH(dDate) ⇒ cMonth<br />

dDate is the date value to convert.<br />

CMONTH() returns the name <strong>of</strong> the month as a character string from a date value with the first<br />

letter uppercase and the rest <strong>of</strong> the string lowercase. For a null date value, CMONTH() returns a<br />

null string ("").<br />

368.14.26 COL()<br />

Column<br />

COL() ⇒ nCol<br />

COL() is a screen function that returns the current column position <strong>of</strong> the cursor. The value <strong>of</strong><br />

COL() changes whenever the cursor position changes on the screen.<br />

368.14.27 COLORSELECT()<br />

COLORSELECT(nColor<strong>Index</strong>) ⇒ NIL<br />

nColor<strong>Index</strong><br />

is a number corresponding to the ordinal positions in the current<br />

list <strong>of</strong> color attributes, as set by SETCOLOR().<br />

COLORSELECT() activates the specified color pair from the current list <strong>of</strong> color attributes (es-


210 volume VIII Argomenti avanzati e accessori<br />

tablished by SETCOLOR()).<br />

368.14.28 CTOD()<br />

Character to date<br />

CTOD(cDate) ⇒ dDate<br />

cDate<br />

is a character string consisting <strong>of</strong> numbers representing the<br />

month, day, and year separated by any character other than a<br />

number. The month, day, and year digits must be specified in<br />

accordance with the SET DATE format. If the century digits<br />

are not specified, the century is determined by the rules <strong>of</strong><br />

SET EPOCH.<br />

CTOD() returns a date value. If cDate is not a valid date, CTOD() returns an empty date.<br />

368.14.29 CURDIR()<br />

Current directory<br />

CURDIR([cDrivespec]) ⇒ cDirectory<br />

cDrivespec<br />

specifies the letter <strong>of</strong> the disk drive to query. If not specified,<br />

the default is the current DOS drive.<br />

CURDIR() returns the current DOS directory <strong>of</strong> the drive specified by cDrivespec as a character<br />

string without either leading or trailing backslash (\) characters.<br />

368.14.30 DATE()<br />

DATE() ⇒ dSystemDate<br />

DATE() returns the system date as a date value.<br />

368.14.31 DAY()<br />

DAY(dDate) ⇒ nDay<br />

dDate is a date value to convert.<br />

DAY() returns the day number from dDate.


nanoBase 1997 user manual 211<br />

368.14.32 DBAPPEND()<br />

DBAPPEND([lReleaseRecLocks]) ⇒ NIL<br />

lReleaseRecLocks<br />

DBAPPEND() adds a new empty record to the active alias.<br />

368.14.33 DBCLEARFILTER()<br />

DBCLEARFILTER() ⇒ NIL<br />

is a logical data type that if true (‘.T.’), clears all<br />

pending record locks, then appends the next record. If<br />

lReleaseRecLocks is false (‘.F.’), all pending record locks<br />

are maintained and the new record is added to the end <strong>of</strong><br />

the Lock List. The default value <strong>of</strong> lReleaseRecLocks is true<br />

(‘.T.’).<br />

DBCLEARFILTER() clears the logical filter condition, if any, for the current work area.<br />

368.14.34 DBCLEARINDEX()<br />

DBCLEARINDEX() ⇒ NIL<br />

DBCLEARINDEX() closes any active indexes for the active alias.<br />

368.14.35 DBCLEARRELATION()<br />

DBCLEARRELATION() ⇒ NIL<br />

DBCLEARRELATION() clears any active relations for the active alias.<br />

368.14.36 DBCLOSEALL()<br />

DBCLOSEALL() ⇒ NIL<br />

DBCLOSEALL() releases all occupied work areas from use. It is equivalent to calling DB-<br />

CLOSEAREA() on every occupied work area.<br />

Attention: DBCLOSEALL() cannot be used inside a "compiled" macro as this will stop the<br />

macro execution. In substitution, DBCLOSE() should be used.<br />

368.14.37 DBCLOSEAREA()<br />

DBCLOSEAREA() ⇒ NIL<br />

DBCLOSEAREA() releases the current work area from use.


212 volume VIII Argomenti avanzati e accessori<br />

368.14.38 DBCOMMIT()<br />

DBCOMMIT() ⇒ NIL<br />

DBCOMMIT() causes all updates to the current work area to be written to disk. All updated<br />

database and index buffers are written to DOS and a DOS COMMIT request is issued for the<br />

database (.dbf) file and any index files associated with the work area. Inside a network environment,<br />

DBCOMMIT() makes database updates visible to other processes. To insure data integrity,<br />

issue DBCOMMIT() before an UNLOCK operation.<br />

368.14.39 DBCOMMITALL()<br />

DBCOMMITALL() ⇒ NIL<br />

DBCOMMITALL() causes all pending updates to all work areas to be written to disk. It is equivalent<br />

to calling DBCOMMIT() for every occupied work area.<br />

368.14.40 DBCREATE()<br />

DBCREATE(cDatabase, aStruct, [cDriver]) ⇒ NIL<br />

cDatabase<br />

aStruct<br />

cDriver<br />

is the name <strong>of</strong> the new database file, with an optional drive and<br />

directory, specified as a character string. If specified without<br />

an extension (.dbf) is assumed.<br />

is an array that contains the structure <strong>of</strong> cDatabase as a series<br />

<strong>of</strong> subarrays, one per field. Each subarray contains the definition<br />

<strong>of</strong> each field’s attributes and has the following structure:<br />

aStruct[n][1] == cName<br />

aStruct[n][2] == cType<br />

aStruct[n][3] == nLength<br />

aStruct[n][4] == nDecimals<br />

specifies the replaceable database driver (RDD) to use to process<br />

the current work area. cDriver is name <strong>of</strong> the RDD specified<br />

as a character expression.<br />

DBCREATE() is a database function that creates a database file from an array containing the<br />

structure <strong>of</strong> the file.<br />

368.14.41 DBCREATEINDEX()<br />

DBCREATEINDEX(c<strong>Index</strong>Name, cKeyExpr, bKeyExpr, [lUnique])<br />

⇒ NIL<br />

c<strong>Index</strong>Name<br />

cKeyExpr<br />

bKeyExpr<br />

lUnique<br />

is a character value that specifies the filename <strong>of</strong> the index file<br />

(order bag) to be created.<br />

is a character value that expresses the index key expression in<br />

textual form.<br />

is a code block that expresses the index key expression in executable<br />

form.<br />

is an optional logical value that specifies whether a unique<br />

index is to be created. If lUnique is omitted, the current global<br />

_SET_UNIQUE setting is used.


nanoBase 1997 user manual 213<br />

DBCREATEINDEX() creates an index for the active alias. If the alias has active indexes, they<br />

are closed.<br />

368.14.42 DBDELETE()<br />

DBDELETE() ⇒ NIL<br />

DBDELETE() marks the current record as deleted (*). Records marked for deletion can be filtered<br />

using SET DELETED or removed from the file using the PACK command.<br />

368.14.43 DBEVAL()<br />

DB evaluate<br />

DBEVAL(bBlock,<br />

[bForCondition],<br />

[bWhileCondition],<br />

[nNextRecords],<br />

[nRecord],<br />

[lRest]) ⇒ NIL<br />

bBlock<br />

bForCondition<br />

bWhileCondition<br />

nNextRecords<br />

nRecord<br />

lRest<br />

is a code block to execute for each record processed.<br />

the FOR condition expressed as code block.<br />

the WHILE condition expressed as code block.<br />

is an optional number that specifies the number <strong>of</strong> records to<br />

process starting with the current record. It is the same as the<br />

NEXT clause.<br />

is an optional record number to process. If this argument is<br />

specified, bBlock will be evaluated for the specified record.<br />

This argument is the same as the RECORD clause.<br />

is an optional logical value that determines whether the scope<br />

<strong>of</strong> DBEVAL() is all records, or, starting with the current<br />

record, all records to the end <strong>of</strong> file.<br />

DBEVAL() is a database function that evaluates a single block for each record within the active<br />

alias.<br />

368.14.44 DBFILTER()<br />

DBFILTER() ⇒ cFilter<br />

BFILTER() returns the filter condition defined in the current work area as a character string. If<br />

no FILTER has been SET, DBFILTER() returns a null string ("").<br />

368.14.45 DBGOBOTTOM()<br />

DBGOBOTTOM() ⇒ NIL<br />

DBGOBOTTOM() moves to last logical record in the active alias.


214 volume VIII Argomenti avanzati e accessori<br />

368.14.46 DBGOTO()<br />

DBGOTO(nRecordNumber) ⇒ NIL<br />

nRecordNumber<br />

is a numeric value that specifies the record number <strong>of</strong> the desired<br />

record.<br />

DBGOTO() moves to the record whose record number is equal to nRecordNumber. If no such<br />

record exists, the work area is positioned to LASTREC() + 1 and both EOF() and BOF() return<br />

true (‘.T.’).<br />

368.14.47 DBGOTOP()<br />

DBGOTOP() ⇒ NIL<br />

DBGOTOP() moves to the first logical record in the current work area.<br />

368.14.48 DBRECALL()<br />

DBRECALL() ⇒ NIL<br />

DBRECALL() causes the current record to be reinstated if it is marked for deletion.<br />

368.14.49 DBREINDEX()<br />

DBREINDEX() ⇒ NIL<br />

DBREINDEX() rebuilds all active indexes associated with the active alias.<br />

368.14.50 DBRELATION()<br />

DBRELATION(nRelation) ⇒ cLinkExp<br />

nRelation<br />

is the position <strong>of</strong> the desired relation in the list <strong>of</strong> active alias<br />

relations.<br />

DBRELATION() returns a character string containing the linking expression <strong>of</strong> the relation specified<br />

by nRelation. If there is no RELATION SET for nRelation, DBRELATION() returns a null<br />

string ("").<br />

368.14.51 DBRLOCK()<br />

DB record lock<br />

DBRLOCK([nRecNo]) ⇒ lSuccess<br />

nRecNo<br />

is the record number to be locked. The default is the current<br />

record.<br />

DBRLOCK() is a database function that locks the record identified by nRecNo or the current


nanoBase 1997 user manual 215<br />

record.<br />

368.14.52 DBRLOCKLIST()<br />

DBRLOCKLIST() ⇒ aRecordLocks<br />

DBRLOCKLIST() returns a one-dimensional array <strong>of</strong> the locked records in the active alias.<br />

368.14.53 DBRSELECT()<br />

DB relation select<br />

DBRSELECT(nRelation) ⇒ nWorkArea<br />

nRelation<br />

is the position <strong>of</strong> the desired relation in the list <strong>of</strong> current work<br />

area relations.<br />

DBRSELECT() returns the work area number <strong>of</strong> the relation specified by nRelation as an integer<br />

numeric value. If there is no RELATION SET for nRelation, DBRSELECT() returns zero.<br />

368.14.54 DBRUNLOCK()<br />

DB relation unlock<br />

DBRUNLOCK([nRecNo]) ⇒ NIL<br />

nRecNo<br />

is the record number to be unlocked. The default is all previously<br />

locked records.<br />

DBRUNLOCK() is a database function that unlocks the record identified by nRecNo or all<br />

locked records.<br />

368.14.55 DBSEEK()<br />

DBSEEK(expKey, [lS<strong>of</strong>tSeek]) ⇒ lFound<br />

expKey<br />

lS<strong>of</strong>tSeek<br />

is a value <strong>of</strong> any type that specifies the key value associated<br />

with the desired record.<br />

is an optional logical value that specifies whether a s<strong>of</strong>t seek<br />

is to be performed. This determines how the work area is positioned<br />

if the specified key value is not found. If lS<strong>of</strong>tSeek is<br />

omitted, the current global _SET_SOFTSEEK setting is used.<br />

DBSEEK() returns true (‘.T.’) if the specified key value was found; otherwise, it returns false<br />

(‘.F.’).


216 volume VIII Argomenti avanzati e accessori<br />

368.14.56 DBSELECTAREA()<br />

DBSELECTAREA(nArea | cAlias) ⇒ NIL<br />

nArea<br />

cAlias<br />

is a numeric value between zero and 250, inclusive, that specifies<br />

the work area being selected.<br />

is a character value that specifies the alias <strong>of</strong> a currently occupied<br />

work area being selected.<br />

DBSELECTAREA() causes the specified work area to become the current work area. All subsequent<br />

database operations will apply to this work area unless another work area is explicitly<br />

specified for an operation.<br />

368.14.57 DBSETDRIVER()<br />

DBSETDRIVER([cDriver]) ⇒ cCurrentDriver<br />

cDriver<br />

DBSETDRIVER() returns the name <strong>of</strong> the current default driver.<br />

368.14.58 DBSETFILTER()<br />

DBSETFILTER(bCondition, [cCondition]) ⇒ NIL<br />

bCondition<br />

cCondition<br />

is an optional character value that specifies the name <strong>of</strong> the<br />

database driver that should be used to activate and manage<br />

new work areas when no driver is explicitly specified.<br />

is a code block that expresses the filter condition in executable<br />

form.<br />

is a character value that expresses the filter condition in textual<br />

form. If cCondition is omitted, the DBSETFILTER()<br />

function will return an empty string for the work area.<br />

DBSETFILTER() sets a logical filter condition for the current work area. When a filter is set,<br />

records which do not meet the filter condition are not logically visible. That is, database operations<br />

which act on logical records will not consider these records. The filter expression supplied<br />

to DBSETFILTER() evaluates to true (‘.T.’) if the current record meets the filter condition;<br />

otherwise, it should evaluate to false (‘.F.’).<br />

368.14.59 DBSETINDEX()<br />

DBSETINDEX(cOrderBagName) ⇒ NIL<br />

cOrderBagName<br />

is a character value that specifies the filename <strong>of</strong> the index file<br />

(index bag) to be opened.<br />

DBSETINDEX() is a database function that adds the contents <strong>of</strong> an Order Bag into the Order List<br />

<strong>of</strong> the current work area. Any Orders already associated with the work area continue to be active.


nanoBase 1997 user manual 217<br />

If the newly opened Order Bag is the only Order associated with the work area, it becomes the<br />

controlling Order; otherwise, the controlling Order remains unchanged. If the Order Bag contains<br />

more than one Order, and there are no other Orders associated with the work area, the first Order<br />

in the new Order Bag becomes the controlling Order.<br />

368.14.60 DBSETORDER()<br />

DBSETORDER(nOrderNum) ⇒ NIL<br />

nOrderNum<br />

is a numeric value that specifies which <strong>of</strong> the active indexes is<br />

to be the controlling index.<br />

DBSETORDER() controls which <strong>of</strong> the active alias’ active indexes is the controlling index.<br />

368.14.61 DBSETRELATION()<br />

DBSETRELATION(nArea | cAlias, bExpr, [cExpr]) ⇒ NIL<br />

nArea<br />

cAlias<br />

bExpr<br />

cExpr<br />

is a numeric value that specifies the work area number <strong>of</strong> the<br />

child work area.<br />

is a character value that specifies the alias <strong>of</strong> the child work<br />

area.<br />

is a code block that expresses the relational expression in executable<br />

form.<br />

is an optional character value that expresses the relational expression<br />

in textual form. If cExpr is omitted, the DBRELA-<br />

TION() function returns an empty string for the relation.<br />

DBSETRELATION() relates the work area specified by nArea or cAlias (the child work area),<br />

to the current work area (the parent work area). Any existing relations remain active.<br />

368.14.62 DBSKIP()<br />

DBSKIP([nRecords]) ⇒ NIL<br />

nRecords<br />

is the number <strong>of</strong> logical records to move, relative to the current<br />

record. A positive value means to skip forward, and a<br />

negative value means to skip backward. If nRecords is omitted,<br />

a value <strong>of</strong> 1 is assumed.<br />

DBSKIP() moves either forward or backward relative to the current record. Attempting to skip<br />

forward beyond the last record positions the work area to LASTREC() + 1 and EOF() returns<br />

true (‘.T.’). Attempting to skip backward beyond the first record positions the work area to the<br />

first record and BOF() returns true (‘.T.’).


218 volume VIII Argomenti avanzati e accessori<br />

368.14.63 DBSTRUCT()<br />

DBSTRUCT() ⇒ aStruct<br />

DBSTRUCT() returns the structure <strong>of</strong> the current database file in an array whose length is equal<br />

to the number <strong>of</strong> fields in the database file. Each element <strong>of</strong> the array is a subarray containing<br />

information for one field. The subarrays have the following format:<br />

aStruct[n][1] == cName<br />

aStruct[n][2] == cType<br />

aStruct[n][3] == nLength<br />

aStruct[n][4] == nDecimals<br />

If there is no database file in USE in the current work area, DBSTRUCT() returns an empty array<br />

({}).<br />

368.14.64 DBUNLOCK()<br />

DBUNLOCK() ⇒ NIL<br />

DBUNLOCK() releases any record or file locks obtained by the current process for the current<br />

work area. DBUNLOCK() is only meaningful on a shared database in a network environment.<br />

368.14.65 DBUNLOCKALL()<br />

DBUNLOCKALL() ⇒ NIL<br />

DBUNLOCKALL() releases any record or file locks obtained by the current process for any work<br />

area. DBUNLOCKALL() is only meaningful on a shared database in a network environment.<br />

368.14.66 DBUSEAREA()<br />

DBUSEAREA( [lNewArea], [cDriver], cName, [xcAlias],<br />

[lShared], [lReadonly]) ⇒ NIL<br />

lNewArea<br />

cDriver<br />

cName<br />

xcAlias<br />

lShared<br />

is an optional logical value. A value <strong>of</strong> true (‘.T.’) selects the<br />

lowest numbered unoccupied work area as the current work<br />

area before the use operation. If lNewArea is false (‘.F.’)<br />

or omitted, the current work area is used; if the work area is<br />

occupied, it is closed first.<br />

is an optional character value. If present, it specifies the name<br />

<strong>of</strong> the database driver which will service the work area. If<br />

cDriver is omitted, the current default driver is used.<br />

specifies the name <strong>of</strong> the database (.dbf) file to be opened.<br />

is an optional character value. If present, it specifies the alias<br />

to be associated with the work area. The alias must constitute<br />

a valid identifier. A valid xcAlias may be any legal identifier<br />

(i.e., it must begin with an alphabetic character and may contain<br />

numeric or alphabetic characters and the underscore). If<br />

xcAlias is omitted, a default alias is constructed from cName.<br />

is an optional logical value. If present, it specifies whether the<br />

database (.dbf) file should be accessible to other processes<br />

on a network. A value <strong>of</strong> true (‘.T.’) specifies that other processes<br />

should be allowed access; a value <strong>of</strong> false (‘.F.’) specifies<br />

that the current process is to have exclusive access. If<br />

lShared is omitted, the current global _SET_EXCLUSIVE<br />

setting determines whether shared access is allowed.


nanoBase 1997 user manual 219<br />

lReadonly<br />

DBUSEAREA() opens the specified database (.DBF).<br />

368.14.67 DBDELETE()<br />

DELETED() ⇒ lDeleted<br />

is an optional logical value that specifies whether updates to<br />

the work area are prohibited. A value <strong>of</strong> true (‘.T.’) prohibits<br />

updates; a value <strong>of</strong> false (‘.F.’) permits updates. A value<br />

<strong>of</strong> true (‘.T.’) also permits read-only access to the specified<br />

database (.dbf) file. If lReadonly is omitted, the default value<br />

is false (‘.F.’).<br />

DELETED() returns true (‘.T.’) if the current record is marked for deletion; otherwise, it returns<br />

false (‘.F.’). If there is no database file in USE in the current work area, DELETED() returns<br />

false (‘.F.’).<br />

368.14.68 DESCEND()<br />

DESCEND(exp) ⇒ ValueInverted<br />

exp<br />

is any valid expression <strong>of</strong> character, date, logical, or numeric<br />

type.<br />

DESCEND() returns an inverted expression <strong>of</strong> the same data type as the exp, except for dates<br />

which return a numeric value. A DESCEND() <strong>of</strong> CHR(0) always returns CHR(0).<br />

368.14.69 DEVOUT()<br />

Device output<br />

DEVOUT(exp, [cColorString]) ⇒ NIL<br />

exp is the value to display.<br />

cColorString is an optional argument that defines the display color <strong>of</strong> exp.<br />

DEVOUT() is a full-screen display function that writes the value <strong>of</strong> a single expression to the<br />

current device at the current cursor or printhead position.<br />

368.14.70 DEVOUTPICT()<br />

Device output picture<br />

DEVOUTPICT(exp, cPicture, [cColorString]) ⇒ NIL<br />

exp is the value to display.<br />

cPicture<br />

defines the formatting control for the display <strong>of</strong> exp.<br />

cColorString is an optional argument that defines the display color <strong>of</strong> exp.


220 volume VIII Argomenti avanzati e accessori<br />

DEVOUTPICT() is a full-screen display function that writes the value <strong>of</strong> a single expression to<br />

the current device at the current cursor or printhead position.<br />

368.14.71 DEVPOS()<br />

Device position<br />

DEVPOS(nRow, nCol) ⇒ NIL<br />

nRow, nCol<br />

are the new row and column positions <strong>of</strong> the cursor or printhead.<br />

DEVPOS() is an environment function that moves the screen or printhead depending on the<br />

current DEVICE.<br />

368.14.72 DIRECTORY()<br />

DIRECTORY(cDirSpec, [cAttributes]) ⇒ aDirectory<br />

cDirSpec<br />

cAttributes<br />

identifies the drive, directory and file specification for the directory<br />

search. Wildcards are allowed in the file specification.<br />

If cDirSpec is omitted, the default value is *.*.<br />

specifies inclusion <strong>of</strong> files with special attributes in the returned<br />

information. cAttributes is a string containing one or<br />

more <strong>of</strong> the following characters:<br />

H Include hidden files<br />

S Include system files<br />

D Include directories<br />

V Search for the DOS volume label only<br />

Normal files are always included in the search, unless you<br />

specify V.<br />

DIRECTORY() returns an array <strong>of</strong> subarrays, with each subarray containing information about<br />

each file matching cDirSpec. The subarray has the following structure:<br />

aDirectory[n][1] == cName<br />

aDirectory[n][2] == cSize<br />

aDirectory[n][3] == dDate<br />

aDirectory[n][4] == cTime<br />

aDirectory[n][5] == cAttributes<br />

If no files are found matching cDirSpec or if cDirSpec is an illegal path or file specification,<br />

DIRECTORY() returns an empty ({}) array.<br />

368.14.73 DISKSPACE()<br />

DISKSPACE([nDrive]) ⇒ nBytes<br />

nDrive<br />

is the number <strong>of</strong> the drive to query, where one is drive A, two<br />

is B, three is C, etc. The default is the current DOS drive if<br />

nDrive is omitted or specified as zero.


nanoBase 1997 user manual 221<br />

DISKSPACE() returns the number <strong>of</strong> bytes <strong>of</strong> empty space on the specified disk drive as an<br />

integer numeric value.<br />

368.14.74 DISPBOX()<br />

Display box<br />

DISPBOX(nTop, nLeft, nBottom, nRight,<br />

[cnBoxString], [cColorString]) ⇒ NIL<br />

nTop, nLeft, nBottom, nRight define the coordinates <strong>of</strong> the box.<br />

is a numeric or character expression that defines the border<br />

characters <strong>of</strong> the box. If specified as a numeric expression, a<br />

value <strong>of</strong> 1 displays a single-line box and a value <strong>of</strong> 2 displays<br />

cnBoxString<br />

a double-line box. All other numeric values display a singleline<br />

box. If cnBoxString is a character expression, it specifies<br />

the characters to be used in drawing the box. This is a string<br />

<strong>of</strong> eight border characters and a fill character.<br />

cColorString defines the display color <strong>of</strong> the box that is drawn.<br />

DISPBOX() is a screen function that draws a box at the specified display coordinates in the<br />

specified color.<br />

368.14.75 DISPOUT()<br />

Display out<br />

DISPOUT(exp, [cColorString]) ⇒ NIL<br />

exp is the value to display.<br />

cColorString is an optional argument that defines the display color <strong>of</strong> exp.<br />

cColorString is a character expression containing the standard color setting.<br />

DISPOUT() is a simple output function that writes the value <strong>of</strong> a single expression to the display<br />

at the current cursor position. This function ignores the SET DEVICE setting; output always goes<br />

to the screen.<br />

368.14.76 DOW()<br />

Day <strong>of</strong> week<br />

DOW(dDate) ⇒ nDay<br />

dDate is a date value to convert.<br />

DOW() returns the day <strong>of</strong> the week as a number between zero and seven. The first day <strong>of</strong> the<br />

week is one (Sunday) and the last day is seven (Saturday). If dDate is empty, DOW() returns<br />

zero.


222 volume VIII Argomenti avanzati e accessori<br />

368.14.77 DTOC()<br />

Date to character<br />

DTOC(dDate) ⇒ cDate<br />

dDate is the date value to convert.<br />

DTOC() returns a character string representation <strong>of</strong> a date value. The return value is formatted in<br />

the current date format. A null date returns a string <strong>of</strong> spaces equal in length to the current date<br />

format.<br />

368.14.78 DTOS()<br />

Date to sort<br />

DTOS(dDate) ⇒ cDate<br />

dDate is the date value to convert.<br />

DTOS() returns a character string eight characters long in the form, yyyymmdd. When dDate is<br />

a null date (CTOD("")), DTOS() returns a string <strong>of</strong> eight spaces.<br />

368.14.79 EMPTY()<br />

EMPTY(exp) ⇒ lEmpty<br />

exp is an expression <strong>of</strong> any data type.<br />

EMPTY() returns true (‘.T.’) if the expression results in an empty value; otherwise, it returns<br />

false (‘.F.’):<br />

Array {}<br />

Character/Memo<br />

Spaces, tabs, CR/LF, or ""<br />

Numeric 0<br />

Date CTOD("")<br />

Logical ‘.F.’<br />

NIL NIL<br />

368.14.80 EOF()<br />

End <strong>of</strong> file<br />

EOF() ⇒ lBoundary<br />

EOF() returns true (‘.T.’) when an attempt is made to move the record pointer beyond the last<br />

logical record in a database file; otherwise, it returns false (‘.F.’). If there is no database file<br />

open in the current work area, EOF() returns false (‘.F.’). If the current database file contains<br />

no records, EOF() returns true (‘.T.’).


nanoBase 1997 user manual 223<br />

368.14.81 EVAL()<br />

Code block evaluation<br />

EVAL(bBlock, [BlockArg_list]) ⇒ LastBlockValue<br />

bBlock is the code block to evaluate.<br />

BlockArg_list<br />

is a list <strong>of</strong> arguments to send to the code block before it is<br />

evaluated.<br />

To execute or evaluate a code block, call EVAL() with the block value and any parameters.<br />

The parameters are supplied to the block when it is executed. Code blocks may be a series <strong>of</strong><br />

expressions separated by commas. When a code block is evaluated, the returned value is the<br />

value <strong>of</strong> the last expression in the block.<br />

368.14.82 EXP()<br />

Exponent<br />

EXP(nExponent) ⇒ nAntilogarithm<br />

nExponent<br />

is the natural logarithm for which a numeric value is to be<br />

calculated.<br />

EXP() returns a numeric value that is equivalent to the value e raised to the specified power.<br />

368.14.83 FCLOSE()<br />

File close<br />

FCLOSE(nHandle) ⇒ lError<br />

nHandle<br />

is the file handle obtained previously from FOPEN() or<br />

FCREATE().<br />

FCLOSE() is a low-level file function that closes binary files and forces the associated DOS<br />

buffers to be written to disk. If the operation fails, FCLOSE() returns false (‘.F.’). FERROR()<br />

can then be used to determine the reason for the failure.<br />

368.14.84 FCOUNT()<br />

Field count<br />

FCOUNT() ⇒ nFields<br />

FCOUNT() returns the number <strong>of</strong> fields in the database file in the active alias as an integer<br />

numeric value. If there is no database file open, FCOUNT() returns zero.


224 volume VIII Argomenti avanzati e accessori<br />

368.14.85 FCREATE()<br />

Field create<br />

FCREATE(cFile, [nAttribute]) ⇒ nHandle<br />

cFile<br />

nAttribute<br />

is the name <strong>of</strong> the file to create. If the file already exists, its<br />

length is truncated to zero without warning.<br />

is the binary file attribute, the default value is zero.<br />

nAttribute = 0 Normal (default)<br />

nAttribute = 1 Read-only<br />

nAttribute = 2 Hidden<br />

nAttribute = 4 System<br />

FCREATE() returns the DOS file handle number <strong>of</strong> the new binary file in the range <strong>of</strong> zero to<br />

65,535. If an error occurs, FCREATE() returns -1 and FERROR() is set to indicate an error code.<br />

368.14.86 FERASE()<br />

File erase<br />

FERASE(cFile) ⇒ nSuccess<br />

cFile<br />

is the name (with or without path) <strong>of</strong> the file to be deleted<br />

from disk.<br />

FERASE() is a file function that deletes a specified file from disk. FERASE() returns -1 if the<br />

operation fails and zero if it succeeds.<br />

368.14.87 FERROR()<br />

File error<br />

FERROR() ⇒ nErrorCode<br />

FERROR() returns the DOS error from the last file operation as an integer numeric value. If there<br />

is no error, FERROR() returns zero.<br />

nErrorCode value<br />

Meaning<br />

0 Successful<br />

2 File not found<br />

3 Path not found<br />

4<br />

Too many files open<br />

5 Access denied<br />

6 Invalid handle<br />

8<br />

Insufficient memory<br />

15<br />

Invalid drive specified<br />

19 Attempted to write to a write-protected disk<br />

21<br />

Drive not ready<br />

23 Data CRC error<br />

29 Write fault<br />

30 Read fault<br />

32<br />

Sharing violation<br />

33 Lock Violation


nanoBase 1997 user manual 225<br />

FERROR() is a low-level file function that indicates a DOS error after a file function is used.<br />

368.14.88 FIELDBLOCK()<br />

FIELDBLOCK(cFieldName) ⇒ bFieldBlock<br />

cFieldName<br />

is the name <strong>of</strong> the field to which the set-get block will refer.<br />

FIELDBLOCK() returns a code block that, when evaluated, sets (assigns) or gets (retrieves) the<br />

value <strong>of</strong> the given field. If cFieldName does not exist in the current work area, FIELDBLOCK()<br />

returns NIL.<br />

368.14.89 FIELDGET()<br />

FIELDGET(nField) ⇒ ValueField<br />

nField<br />

is the ordinal position <strong>of</strong> the field in the record structure for<br />

the current work area.<br />

FIELDGET() returns the value <strong>of</strong> the specified field. If nField does not correspond to the position<br />

<strong>of</strong> any field in the current database file, FIELDGET() returns NIL.<br />

368.14.90 FIELDNAME()<br />

FIELDNAME(nPosition) ⇒ cFieldName<br />

nPosition<br />

is the position <strong>of</strong> a field in the database file structure.<br />

FIELDNAME() returns the name <strong>of</strong> the specified field as a character string. If nPosition does<br />

not correspond to an existing field in the current database file or if no database file is open in the<br />

current work area, FIELDNAME() returns a null string ("").<br />

368.14.91 FIELDPOS()<br />

Field position<br />

FIELDPOS(cFieldName) ⇒ nFieldPos<br />

cFieldName<br />

is the name <strong>of</strong> a field in the current or specified work area.<br />

FIELDPOS() returns the position <strong>of</strong> the specified field within the list <strong>of</strong> fields associated with<br />

the current or specified work area. If the current work area has no field with the specified name,<br />

FIELDPOS() returns zero.


226 volume VIII Argomenti avanzati e accessori<br />

368.14.92 FIELDPUT()<br />

FIELDPUT(nField, expAssign) ⇒ ValueAssigned<br />

nField<br />

expAssign<br />

is the ordinal position <strong>of</strong> the field in the current database file.<br />

is the value to assign to the given field. The data type <strong>of</strong> this<br />

expression must match the data type <strong>of</strong> the designated field<br />

variable.<br />

FIELDPUT() is a database function that assigns expAssign to the field at ordinal position nField<br />

in the current work area. This function allows you to set the value <strong>of</strong> a field using its position<br />

within the database file structure rather than its field name.<br />

368.14.93 FIELDWBLOCK()<br />

Field work area block<br />

FIELDWBLOCK(cFieldName, nWorkArea) ⇒ bFieldWBlock<br />

cFieldName<br />

nWorkArea<br />

is the name <strong>of</strong> the field specified as a character string.<br />

is the work area number where the field resides specified as a<br />

numeric value.<br />

FIELDWBLOCK() returns a code block that, when evaluated, sets (assigns) or gets (retrieves)<br />

the value <strong>of</strong> cFieldName in the work area designated by nWorkArea. If cFieldName does not<br />

exist in the specified work area, FIELDWBLOCK() returns NIL.<br />

368.14.94 FILE()<br />

FILE(cFilespec) ⇒ lExists<br />

cFilespec<br />

is in the current default directory and path. It is a standard file<br />

specification that can include the wildcard characters * and ?<br />

as well as a drive and path reference.<br />

FILE() returns true (‘.T.’) if there is a match for any file matching the cFilespec pattern; otherwise,<br />

it returns false (‘.F.’).<br />

368.14.95 FLOCK()<br />

File lock<br />

FLOCK() ⇒ lSuccess<br />

FLOCK() tries to lock the active alias and returns true (‘.T.’) if it succeeds; otherwise, it returns<br />

false (‘.F.’).


nanoBase 1997 user manual 227<br />

368.14.96 FOPEN()<br />

File open<br />

FOPEN(cFile, [nMode]) ⇒ nHandle<br />

cFile<br />

nMode<br />

is the name <strong>of</strong> the file to open including the path if there is<br />

one.<br />

is the requested DOS open mode indicating how the opened<br />

file is to be accessed. The open mode is composed <strong>of</strong> the sum<br />

<strong>of</strong> two elements: the Open mode and the Sharing mode.<br />

Open mode:<br />

0 Open for reading (default)<br />

1 Open for writing<br />

2 Open for reading or writing<br />

Sharing mode:<br />

0 Compatibility mode (default)<br />

16 Exclusive use<br />

32 Prevent others from writing<br />

48 Prevent others from reading<br />

64 Allow others to read or write<br />

FOPEN() returns the file handle <strong>of</strong> the opened file in the range <strong>of</strong> zero to 65,535. If an error<br />

occurs, FOPEN() returns -1.<br />

368.14.97 FOUND()<br />

FOUND() ⇒ lSuccess<br />

FOUND() returns true (‘.T.’) if the last search command was successful; otherwise, it returns<br />

false (‘.F.’).<br />

368.14.98 FREAD()<br />

File read<br />

FREAD(nHandle, @cBufferVar, nBytes) ⇒ nBytes<br />

is the file handle obtained from FOPEN(), FCREATE(), or<br />

nHandle<br />

predefined by DOS.<br />

is the name <strong>of</strong> an existing and initialized character variable<br />

used to store data read from the specified file. The length<br />

cBufferVar<br />

<strong>of</strong> this variable must be greater than or equal to nBytes.<br />

cBufferVar must be passed by reference and, therefore, must<br />

be prefaced by the pass-by-reference operator (@).<br />

nBytes is the number <strong>of</strong> bytes to read into the buffer.<br />

FREAD() tries to read nBytes <strong>of</strong> the binary file nHandle inside cBufferVar. It returns the number<br />

<strong>of</strong> bytes successfully read as an integer numeric value. A return value less than nBytes or<br />

zero indicates end <strong>of</strong> file or some other read error.


228 volume VIII Argomenti avanzati e accessori<br />

368.14.99 FREADSTR()<br />

File read string<br />

FREADSTR(nHandle, nBytes) ⇒ cString<br />

nHandle<br />

nBytes<br />

is the file handle obtained from FOPEN(), FCREATE(), or<br />

predefined by DOS.<br />

is the number <strong>of</strong> bytes to read, beginning at the current DOS<br />

file pointer position.<br />

FREADSTR() returns a character string up to 65,535 (64K) bytes. A null return value ("") indicates<br />

an error or end <strong>of</strong> file. FREADSTR() is a low-level file function that reads characters from<br />

an open binary file beginning with the current DOS file pointer position. Characters are read up<br />

to nBytes or until a null character (CHR(0)) is encountered. All characters are read including<br />

control characters except for CHR(0). The file pointer is then moved forward nBytes. If nBytes<br />

is greater than the number <strong>of</strong> bytes from the pointer position to the end <strong>of</strong> the file, the file pointer<br />

is positioned to the last byte in the file.<br />

368.14.100 FRENAME()<br />

File rename<br />

FRENAME(cOldFile, cNewFile) ⇒ nSuccess<br />

cOldFile<br />

cNewFile<br />

is the name <strong>of</strong> the file to rename, including the file extension.<br />

A drive letter and/or path name may also be included as part<br />

<strong>of</strong> the filename.<br />

is the new name <strong>of</strong> the file, including the file extension. A<br />

drive letter and/or path name may also be included as part <strong>of</strong><br />

the name.<br />

FRENAME() returns -1 if the operation fails and zero if it succeeds.<br />

368.14.101 FSEEK()<br />

File seek<br />

FSEEK(nHandle, nOffset, [nOrigin]) ⇒ nPosition<br />

is the file handle obtained from FOPEN(), FCREATE(), or<br />

nHandle<br />

predefined by DOS.<br />

is the number <strong>of</strong> bytes to move the file pointer from the position<br />

defined by nOrigin. It can be a positive or negative<br />

nOffset<br />

number. A positive number moves the pointer forward, and<br />

a negative number moves the pointer backward in the file.<br />

defines the starting location <strong>of</strong> the file pointer before FSEEK()<br />

is executed. The default value is zero, representing the begin-<br />

nOrigin<br />

ning <strong>of</strong> file. If nOrigin is the end <strong>of</strong> file, nOffset must be zero<br />

or negative.<br />

nOrigin == 0 Seek from beginning <strong>of</strong> file<br />

nOrigin == 1 Seek from the current pointer position<br />

nOrigin == 2 Seek from end <strong>of</strong> file


nanoBase 1997 user manual 229<br />

FSEEK() returns the new position <strong>of</strong> the file pointer relative to the beginning <strong>of</strong> file (position<br />

0) as an integer numeric value. This value is without regard to the original position <strong>of</strong> the file<br />

pointer. FSEEK() is a low-level file function that moves the file pointer forward or backward<br />

in an open binary file without actually reading the contents <strong>of</strong> the specified file. The beginning<br />

position and <strong>of</strong>fset are specified as function arguments, and the new file position is returned.<br />

368.14.102 FWRITE()<br />

File write<br />

FWRITE(nHandle, cBuffer, [nBytes]) ⇒ nBytesWritten<br />

nHandle<br />

is the file handle obtained from FOPEN(), FCREATE(), or<br />

predefined by DOS.<br />

cBuffer is the character string to write to the specified file.<br />

nBytes<br />

indicates the number <strong>of</strong> bytes to write beginning at the current<br />

file pointer position. If omitted, the entire content <strong>of</strong> cBuffer<br />

is written.<br />

FWRITE() returns the number <strong>of</strong> bytes written as an integer numeric value. If the value returned<br />

is equal to nBytes, the operation was successful. If the return value is less than nBytes or zero,<br />

either the disk is full or another error has occurred.<br />

368.14.103 GETENV()<br />

Get environment<br />

GETENV(cEnvironmentVariable) ⇒ cString<br />

cEnvironmentVariable<br />

is the name <strong>of</strong> the DOS environment variable. When specifying<br />

this argument, you can use any combination <strong>of</strong> upper and<br />

lowercase letters; GETENV() is not case- sensitive.<br />

GETENV() returns the contents <strong>of</strong> the specified DOS environment variable as a character string.<br />

If the variable cannot be found, GETENV() returns a null string ("").<br />

368.14.104 HARDCR()<br />

Hard carriage return<br />

HARDCR(cString) ⇒ cConvertedString<br />

cString is the character string or memo field to convert.<br />

HARDCR() is a memo function that replaces all s<strong>of</strong>t carriage returns (CHR(141)) with hard carriage<br />

returns (CHR(13)). It is used to display long character strings and memo fields containing<br />

s<strong>of</strong>t carriage returns with console commands.


230 volume VIII Argomenti avanzati e accessori<br />

368.14.105 HEADER()<br />

HEADER() ⇒ nBytes<br />

HEADER() returns the number <strong>of</strong> bytes in the header <strong>of</strong> the current database file as an integer<br />

numeric value. If no database file is in use, HEADER() returns a zero (0).<br />

368.14.106 I2BIN()<br />

Integer to binary<br />

I2BIN(nInteger) ⇒ cBinaryInteger<br />

nInteger<br />

is an integer numeric value to convert. Decimal digits are truncated.<br />

I2BIN() returns a two-byte character string containing a 16-bit binary integer.<br />

368.14.107 IF()<br />

[I]IF(lCondition, expTrue, expFalse) ⇒ Value<br />

lCondition<br />

expTrue<br />

expFalse<br />

is a logical expression to be evaluated.<br />

is the value, a condition-expression, <strong>of</strong> any data type, returned<br />

if lCondition is true (‘.T.’).<br />

is the value, <strong>of</strong> any date type, returned if lCondition is false<br />

(‘.F.’). This argument need not be the same data type as<br />

expTrue.<br />

IF() returns the evaluation <strong>of</strong> expTrue if lCondition evaluates to true (‘.T.’) and expFalse if it<br />

evaluates to false (‘.F.’).<br />

368.14.108 INDEXEXT()<br />

<strong>Index</strong> extention<br />

INDEXEXT() ⇒ cExtension<br />

INDEXEXT() returns the default index file extension by determining which database driver is<br />

currently linked.<br />

368.14.109 INDEXKEY()<br />

INDEXKEY(nOrder) ⇒ cKeyExp<br />

nOrder<br />

is the ordinal position <strong>of</strong> the index in the list <strong>of</strong> index files<br />

opened by the last USE...INDEX or SET INDEX TO command<br />

for the current work area. A zero value specifies the<br />

controlling index, without regard to its actual position in the<br />

list.


nanoBase 1997 user manual 231<br />

INDEXKEY() returns the key expression <strong>of</strong> the specified index as a character string. If there is<br />

no corresponding index or if no database file is open, INDEXKEY() returns a null string ("").<br />

368.14.110 INDEXORD()<br />

<strong>Index</strong> order<br />

INDEXORD() ⇒ nOrder<br />

INDEXORD() returns an integer numeric value. The value returned is equal to the position <strong>of</strong> the<br />

controlling index in the list <strong>of</strong> open indexes for the current work area. A value <strong>of</strong> zero indicates<br />

that there is no controlling index and records are being accessed in natural order. If no database<br />

file is open, INDEXORD() will also return a zero.<br />

368.14.111 INKEY()<br />

Input key<br />

INKEY([nSeconds]) ⇒ nInkeyCode<br />

nSeconds<br />

specifies the number <strong>of</strong> seconds INKEY() waits for a keypress.<br />

You can specify the value in increments as small as<br />

one-tenth <strong>of</strong> a second. Specifying zero halts the program until<br />

a key is pressed. If nSeconds is omitted, INKEY() does not<br />

wait for a keypress.<br />

INKEY() returns an integer numeric value from -39 to 386, identifying the key extracted from<br />

the keyboard buffer. If the keyboard buffer is empty, INKEY() returns zero. INKEY() returns<br />

values for all ASCII characters, function, Alt+function, Ctrl+function, Alt+letter, and Ctrl+letter<br />

key combinations.<br />

nInkeyCode value Key or key combination<br />

5 [ Up arrow ], [ Ctrl ]+E<br />

24 [ Down arrow ], [ Ctrl ]+X<br />

19 [ Left arrow ], [ Ctrl ]+S<br />

4 [ Right arrow ], [ Ctrl ]+D<br />

1 [ Home ], [ Ctrl ]+A<br />

6 [ End ], [ Ctrl ]+F<br />

18 [ PgUp ], [ Ctrl ]+R<br />

3 [ PgDn ], [ Ctrl ]+C<br />

397<br />

[ Ctrl ]+[ Up arrow ]<br />

401<br />

[ Ctrl ]+[ Down arrow ]<br />

26 [ Ctrl ]+[ Left arrow ], [ Ctrl ]+Z<br />

2 [ Ctrl ]+[ Right arrow ], [ Ctrl ]+B<br />

29<br />

[ Ctrl ]+[ Home ]<br />

23 [ Ctrl ]+[ End ], [ Ctrl ]+W<br />

31<br />

[ Ctrl ]+[ PgUp ], [ Ctrl ]+Hyphen<br />

30 [ Ctrl ]+[ PgDn ], [ Ctrl ]+^<br />

408<br />

[ Alt ]+[ Up arrow ]<br />

416<br />

[ Alt ]+[ Down arrow ]<br />

411<br />

[ Alt ]+[ Left arrow ]<br />

413<br />

[ Alt ]+[ Right arrow ]<br />

407<br />

[ Alt ]+[ Home ]


232 volume VIII Argomenti avanzati e accessori<br />

nInkeyCode value Key or key combination<br />

415<br />

[ Alt ]+[ End ]<br />

409<br />

[ Alt ]+[ PgUp ]<br />

417<br />

[ Alt ]+[ PgDn ]<br />

13 [ Enter ], [ Ctrl ]+M<br />

32<br />

Space bar<br />

27 Esc<br />

10<br />

[ Ctrl ]+[ Enter ]<br />

379 [ Ctrl ]+Print Screen<br />

309 [ Ctrl ]+?<br />

284<br />

[ Alt ]+[ Enter ]<br />

387<br />

[ Alt ]+Equals<br />

257 [ Alt ]+Esc<br />

422<br />

Keypad [ Alt ]+[ Enter ]<br />

399 Keypad [ Ctrl ]+5<br />

405<br />

Keypad [ Ctrl ]+/<br />

406<br />

Keypad [ Ctrl ]+*<br />

398 Keypad [ Ctrl ]+-<br />

400<br />

Keypad [ Ctrl ]++<br />

5<br />

Keypad [ Alt ]+5<br />

420<br />

Keypad [ Alt ]+/<br />

311<br />

Keypad [ Alt ]+*<br />

330<br />

Keypad [ Alt ]+-<br />

334<br />

Keypad [ Alt ]++<br />

22 [ Ins ], [ Ctrl ]+V<br />

7 [ Del ], [ Ctrl ]+G<br />

8 [ Backspace ], [ Ctrl ]+H<br />

9 [ Tab ], [ Ctrl ]+I<br />

271<br />

[ Shift ]+[ Tab ]<br />

402<br />

[ Ctrl ]+[ Ins ]<br />

403<br />

[ Ctrl ]+[ Del ]<br />

127<br />

[ Ctrl ]+[ Backspace ]<br />

404<br />

[ Ctrl ]+[ Tab ]<br />

418<br />

[ Alt ]+[ Ins ]<br />

419<br />

[ Alt ]+[ Del ]<br />

270<br />

[ Alt ]+[ Backspace ]<br />

421<br />

[ Alt ]+[ Tab ]<br />

1 [ Ctrl ]+A, [ Home ]<br />

2 [ Ctrl ]+B, [ Ctrl ]+[ Right arrow ]<br />

3 [ Ctrl ]+C, [ PgDn ], [ Ctrl ]+[ ScrollLock ]<br />

4 [ Ctrl ]+D, [ Right arrow ]<br />

5 [ Ctrl ]+E, [ Up arrow ]<br />

6 [ Ctrl ]+F, [ End ]<br />

7 [ Ctrl ]+G, [ Del ]<br />

8 [ Ctrl ]+H, [ Backspace ]<br />

9 [ Ctrl ]+I, [ Tab ]<br />

10 [ Ctrl ]+J<br />

11 [ Ctrl ]+K<br />

12 [ Ctrl ]+L<br />

13 [ Ctrl ]+M, Return<br />

14 [ Ctrl ]+N<br />

15 [ Ctrl ]+O<br />

16 [ Ctrl ]+P<br />

17 [ Ctrl ]+Q


nanoBase 1997 user manual 233<br />

nInkeyCode value Key or key combination<br />

18 [ Ctrl ]+R, [ PgUp ]<br />

19 [ Ctrl ]+S, [ Left arrow ]<br />

20 [ Ctrl ]+T<br />

21 [ Ctrl ]+U<br />

22 [ Ctrl ]+V, [ Ins ]<br />

23 [ Ctrl ]+W, [ Ctrl ]+[ End ]<br />

24 [ Ctrl ]+X, [ Down arrow ]<br />

25 [ Ctrl ]+Y<br />

26 [ Ctrl ]+Z, [ Ctrl ]+[ Left arrow ]<br />

286 [ Alt ]+A<br />

304 [ Alt ]+B<br />

302 [ Alt ]+C<br />

288 [ Alt ]+D<br />

274 [ Alt ]+E<br />

289 [ Alt ]+F<br />

290 [ Alt ]+G<br />

291 [ Alt ]+H<br />

279 [ Alt ]+I<br />

292 [ Alt ]+J<br />

293 [ Alt ]+K<br />

294 [ Alt ]+L<br />

306 [ Alt ]+M<br />

305 [ Alt ]+N<br />

280 [ Alt ]+O<br />

281 [ Alt ]+P<br />

272 [ Alt ]+Q<br />

275 [ Alt ]+R<br />

287 [ Alt ]+S<br />

276 [ Alt ]+T<br />

278 [ Alt ]+U<br />

303 [ Alt ]+V<br />

273 [ Alt ]+W<br />

301 [ Alt ]+X<br />

277 [ Alt ]+Y<br />

300 [ Alt ]+Z<br />

376 [ Alt ]+1<br />

377 [ Alt ]+2<br />

378 [ Alt ]+3<br />

379 [ Alt ]+4<br />

380 [ Alt ]+5<br />

381 [ Alt ]+6<br />

382 [ Alt ]+7<br />

383 [ Alt ]+8<br />

384 [ Alt ]+9<br />

385 [ Alt ]+0<br />

28 F1, [ Ctrl ]+[ Backslash ]<br />

-1 F2<br />

-2 F3<br />

-3 F4<br />

-4 F5<br />

-5 F6<br />

-6 F7<br />

-7 F8


234 volume VIII Argomenti avanzati e accessori<br />

nInkeyCode value Key or key combination<br />

-8 F9<br />

-9 F10<br />

-40 F11<br />

-41 F12<br />

-20 [ Ctrl ]+F1<br />

-21 [ Ctrl ]+F2<br />

-22 [ Ctrl ]+F4<br />

-23 [ Ctrl ]+F3<br />

-24 [ Ctrl ]+F5<br />

-25 [ Ctrl ]+F6<br />

-26 [ Ctrl ]+F7<br />

-27 [ Ctrl ]+F8<br />

-28 [ Ctrl ]+F9<br />

-29 [ Ctrl ]+F10<br />

-44 [ Ctrl ]+F11<br />

-45 [ Ctrl ]+F12<br />

-30 [ Alt ]+F1<br />

-31 [ Alt ]+F2<br />

-32 [ Alt ]+F3<br />

-33 [ Alt ]+F4<br />

-34 [ Alt ]+F5<br />

-35 [ Alt ]+F6<br />

-36 [ Alt ]+F7<br />

-37 [ Alt ]+F8<br />

-38 [ Alt ]+F9<br />

-39 [ Alt ]+F10<br />

-46 [ Alt ]+F11<br />

-47 [ Alt ]+F12<br />

-10 [ Shift ]+F1<br />

-11 [ Shift ]+F2<br />

-12 [ Shift ]+F3<br />

-13 [ Shift ]+F4<br />

-14 [ Shift ]+F5<br />

-15 [ Shift ]+F6<br />

-16 [ Shift ]+F7<br />

-17 [ Shift ]+F8<br />

-18 [ Shift ]+F9<br />

-19 [ Shift ]+F10<br />

-42 [ Shift ]+F11<br />

-43 [ Shift ]+F12<br />

368.14.112 INT()<br />

Integer<br />

INT(nExp) ⇒ nInteger<br />

nExp is a numeric expression to convert to an integer.<br />

INT() is a numeric function that converts a numeric value to an integer by truncating all digits<br />

to the right <strong>of</strong> the decimal point. INT() is useful in operations where the decimal portion <strong>of</strong> a


nanoBase 1997 user manual 235<br />

number is not needed.<br />

368.14.113 ISALPHA()<br />

ISALPHA(cString) ⇒ lBoolean<br />

cString is the character string to examine.<br />

ISALPHA() returns true (‘.T.’) if the first character in cString is alphabetic; otherwise, it returns<br />

false (‘.F.’).<br />

368.14.114 ISCOLOR()<br />

ISCOLOR() | ISCOLOUR() ⇒ lBoolean<br />

ISCOLOR() returns true (‘.T.’) if there is a color graphics card installed; otherwise, it returns<br />

false (‘.F.’).<br />

368.14.115 ISDIGIT()<br />

ISDIGIT(cString) ⇒ lBoolean<br />

cString is the character string to examine.<br />

ISDIGIT() returns true (‘.T.’) if the first character <strong>of</strong> the character string is a digit between zero<br />

and nine; otherwise, it returns false (‘.F.’).<br />

368.14.116 ISLOWER()<br />

ISLOWER(cString) ⇒ lBoolean<br />

cString is the character string to examine.<br />

ISLOWER() returns true (‘.T.’) if the first character <strong>of</strong> the character string is a lowercase letter;<br />

otherwise, it returns false (‘.F.’).<br />

368.14.117 ISPRINTER()<br />

ISPRINTER() ⇒ lReady<br />

ISPRINTER() returns true (‘.T.’) if ‘LPT1:’ is ready; otherwise, it returns false (‘.F.’).


236 volume VIII Argomenti avanzati e accessori<br />

368.14.118 ISUPPER()<br />

ISUPPER(cString) ⇒ lBoolean<br />

cString is the character string to examine.<br />

ISUPPER() returns true (‘.T.’) if the first character is an uppercase letter; otherwise, it returns<br />

false (‘.F.’).<br />

368.14.119 L2BIN()<br />

Long to binary<br />

L2BIN(nExp) ⇒ cBinaryInteger<br />

nExp is the numeric value to convert. Decimal digits are truncated.<br />

L2BIN() returns a four-byte character string formatted as a 32- bit binary integer.<br />

368.14.120 LASTKEY()<br />

LASTKEY() ⇒ nInkeyCode<br />

LASTKEY() is a keyboard function that reports the INKEY() value <strong>of</strong> the last key fetched from<br />

the keyboard buffer by the INKEY() function, or a wait state. LASTKEY() retains its current<br />

value until another key is fetched from the keyboard buffer.<br />

368.14.121 LASTREC()<br />

Last record<br />

LASTREC() ⇒ nRecords<br />

LASTREC() returns the number <strong>of</strong> physical records in the active alias as an integer numeric<br />

value.<br />

368.14.122 LEFT()<br />

LEFT(cString, nCount) ⇒ cSubString<br />

cString is a character string from which to extract characters.<br />

nCount is the number <strong>of</strong> characters to extract.<br />

LEFT() returns the leftmost nCount characters <strong>of</strong> cString as a character string. If nCount is<br />

negative or zero, LEFT() returns a null string (""). If nCount is larger than the length <strong>of</strong> the<br />

character string, LEFT() returns the entire string.


nanoBase 1997 user manual 237<br />

368.14.123 LEN()<br />

Length<br />

LEN(cString | aTarget) ⇒ nCount<br />

cString is the character string to count.<br />

aTarget is the array to count.<br />

LEN() returns the length <strong>of</strong> a character string or the number <strong>of</strong> elements in an array as an integer<br />

numeric value.<br />

368.14.124 LOG()<br />

LOG(nExp) ⇒ nNaturalLog<br />

nExp<br />

is a numeric value greater than zero to convert to its natural<br />

logarithm.<br />

LOG() returns the natural logarithm as a numeric value. If nExp is less than or equal to zero,<br />

LOG() returns a numeric overflow (displayed as a row <strong>of</strong> asterisks).<br />

368.14.125 LOWER()<br />

LOWER(cString) ⇒ cLowerString<br />

cString is a character string to convert to lowercase.<br />

LOWER() returns a copy <strong>of</strong> cString with all alphabetic characters converted to lowercase.<br />

368.14.126 LTRIM()<br />

Left trim<br />

LTRIM(cString) ⇒ cTrimString<br />

cString is the character string to copy without leading spaces.<br />

LTRIM() returns a copy <strong>of</strong> cString with the leading spaces removed.<br />

368.14.127 LUPDATE()<br />

Last update<br />

LUPDATE() ⇒ dModification<br />

LUPDATE() returns the date <strong>of</strong> last change to the open database file in the current work area.


238 volume VIII Argomenti avanzati e accessori<br />

368.14.128 MAX()<br />

MAX(nExp1, nExp2) ⇒ nLarger<br />

MAX(dExp1, dExp2) ⇒ dLarger<br />

nExp1, nExp2 are the numeric values to compare.<br />

dExp1, dExp2 are the date values to compare.<br />

MAX() returns the larger <strong>of</strong> the two arguments. The value returned is the same type as the arguments.<br />

368.14.129 MAXCOL()<br />

Max column<br />

MAXCOL() ⇒ nColumn<br />

MAXCOL() returns the column number <strong>of</strong> the rightmost visible column for display purposes.<br />

368.14.130 MAXROW()<br />

MAXROW() ⇒ nRow<br />

MAXROW() returns the row number <strong>of</strong> the bottommost visible row for display purposes.<br />

368.14.131 MEMOEDIT()<br />

MEMOEDIT([cString],<br />

[nTop], [nLeft],<br />

[nBottom], [nRight],<br />

[lEditMode],<br />

[cUserFunction],<br />

[nLineLength],<br />

[nTabSize],<br />

[nTextBufferRow],<br />

[nTextBufferColumn],<br />

[nWindowRow],<br />

[nWindowColumn]) ⇒ cTextBuffer<br />

cString<br />

nTop, nLeft, nBottom, nRight<br />

lEditMode<br />

cUserFunction<br />

nLineLength<br />

is the character string or memo field to copy to the MEM-<br />

OEDIT() text buffer.<br />

are window coordinates. The default coordinates are 0, 0,<br />

MAXROW(), and MAXCOL().<br />

determines whether the text buffer can be edited or merely<br />

displayed. If not specified, the default value is true (‘.T.’).<br />

is the name <strong>of</strong> a user-defined function that executes when the<br />

user presses a key not recognized by MEMOEDIT() and when<br />

no keys are pending in the keyboard buffer.<br />

determines the length <strong>of</strong> lines displayed in the MEMOEDIT()<br />

window. If a line is greater than nLineLength, it is word<br />

wrapped to the next line in the MEMOEDIT() window. The<br />

default line length is (nRight - nLeft).


nanoBase 1997 user manual 239<br />

nTabSize<br />

nTextBufferRow,<br />

nTextBufferColumn<br />

nWindowRow, nWindowColumn<br />

determines the size <strong>of</strong> a tab character to insert when the user<br />

presses Tab. The default is four.<br />

define the display position <strong>of</strong> the cursor within the text buffer<br />

when MEMOEDIT() is invoked. nTextBufferRow begins<br />

with one and nTextBufferColumn begins with zero. Default<br />

is the beginning <strong>of</strong> MEMOEDIT() window.<br />

define the initial position <strong>of</strong> the cursor within the MEM-<br />

OEDIT() window. Row and column positions begin with zero.<br />

If these arguments are not specified, the initial window position<br />

is row zero and the current cursor column position.<br />

MEMOEDIT() is a user interface and general purpose text editing function that edits memo fields<br />

and long character strings. Editing occurs within a specified window region placed anywhere on<br />

the screen.<br />

[ Uparrow ]/[ Ctrl ]+E Move up one line<br />

[ Dnarrow ]/[ Ctrl ]+X Move down one line<br />

[ Leftarrow ]/[ Ctrl ]+S Move left one character<br />

[ Rightarrow ]/[ Ctrl ]+D Move right one character<br />

[ Ctrl ]-[ Leftarrow ]/[ Ctrl ]+A Move left one word<br />

[ Ctrl ]-[ Rightarrow ]/[ Ctrl ]+F Move right one word<br />

[ Home ] Move to beginning <strong>of</strong> current line<br />

[ End ] Move to end <strong>of</strong> current line<br />

[ Ctrl ]+[ Home ] Move to beginning <strong>of</strong> current window<br />

[ Ctrl ]+[ End ] Move to end <strong>of</strong> current window<br />

[ PgUp ] Move to previous edit window<br />

[ PgDn ] Move to next edit window<br />

[ Ctrl ]+[ PgUp ] Move to beginning <strong>of</strong> memo<br />

[ Ctrl ]+[ PgDn ] Move to end <strong>of</strong> memo<br />

[ Return ] Move to beginning <strong>of</strong> next line<br />

[ Delete ] Delete character at cursor<br />

[ Backspace ] Delete character to left <strong>of</strong> cursor<br />

[ Tab ] Insert tab character or spaces<br />

Printable characters Insert character<br />

[ Ctrl ]+Y Delete the current line<br />

[ Ctrl ]+T Delete word right<br />

[ Ctrl ]+B Reform paragraph<br />

[ Ctrl ]+V/[ Ins ] Toggle insert mode<br />

[ Ctrl ]+W Finish editing with save<br />

[ Esc ] Abort edit and return original<br />

368.14.132 MEMOLINE()<br />

MEMOLINE(cString,<br />

[nLineLength],<br />

[nLineNumber],<br />

[nTabSize],<br />

[lWrap]) ⇒ cLine<br />

cString<br />

nLineLength<br />

is the memo field or character string from which to extract a<br />

line <strong>of</strong> text.<br />

specifies the number <strong>of</strong> characters per line and can be between<br />

four and 254 . If not specified, the default line length is 79.


240 volume VIII Argomenti avanzati e accessori<br />

nLineNumber<br />

is the line number to extract. If not specified, the default value<br />

is one.<br />

nTabSize defines the tab size. If not specified, the default value is four.<br />

lWrap<br />

toggles word wrap on and <strong>of</strong>f. Specifying true (‘.T.’) toggles<br />

word wrap on; false (‘.F.’) toggles it <strong>of</strong>f. If not specified, the<br />

default value is true (‘.T.’).<br />

MEMOLINE() returns the line <strong>of</strong> text specified by nLineNumber in cString as a character string.<br />

If the line has fewer characters than the indicated length, the return value is padded with blanks.<br />

If the line number is greater than the total number <strong>of</strong> lines in cString, MEMOLINE() returns a<br />

null string (""). If lWrap is true (‘.T.’) and the indicated line length breaks the line in the middle<br />

<strong>of</strong> a word, that word is not included as part <strong>of</strong> the return value but shows up at the beginning <strong>of</strong><br />

the next line extracted with MEMOLINE(). If lWrap is false (‘.F.’), MEMOLINE() returns only<br />

the number <strong>of</strong> characters specified by the line length. The next line extracted by MEMOLINE()<br />

begins with the character following the next hard carriage return, and all intervening characters<br />

are not processed.<br />

368.14.133 MEMOREAD()<br />

MEMOREAD(cFile) ⇒ cString<br />

cFile<br />

is the name <strong>of</strong> the file to read from disk. It must include an<br />

extension if there is one, and can optionally include a path.<br />

MEMOREAD() returns the contents <strong>of</strong> a text file as a character string.<br />

368.14.134 MEMORY()<br />

MEMORY(nExp) ⇒ nKbytes<br />

nExp<br />

is a numeric value that determines the type <strong>of</strong> value MEM-<br />

ORY() returns.<br />

MEMORY() returns an integer numeric value representing the amount <strong>of</strong> memory available.<br />

MEMORY(0) Estimated total space available for character values<br />

MEMORY(1) Largest contiguous block available for character values<br />

MEMORY(2) Area available for RUN commands<br />

368.14.135 MEMOTRAN()<br />

Memo translate<br />

MEMOTRAN(cString,<br />

[cReplaceHardCR],<br />

[cReplaceS<strong>of</strong>tCR]) ⇒ cNewString<br />

cString is the character string or memo field to search.<br />

cReplaceHardCR<br />

is the character to replace a hard carriage return/linefeed pair<br />

with. If not specified, the default value is a semicolon (;).


nanoBase 1997 user manual 241<br />

cReplaceS<strong>of</strong>tCR<br />

is the character to replace a s<strong>of</strong>t carriage return/linefeed pair<br />

with. If not specified, the default value is a space.<br />

MEMOTRAN() returns a copy <strong>of</strong> cString with the specified carriage return/linefeed pairs replaced.<br />

368.14.136 MEMOWRIT()<br />

Memo write<br />

MEMOWRIT(cFile, cString) ⇒ lSuccess<br />

cFile<br />

is the name <strong>of</strong> the target disk file including the file extension<br />

and optional path and drive designator.<br />

cString is the character string or memo field to write to cFile.<br />

MEMOWRIT() is a memo function that writes a character string or memo field to a disk file.<br />

If a path is not specified, MEMOWRIT() writes cFile to the current DOS directory and not the<br />

current DEFAULT directory. If cFile already exists, it is overwritten. MEMOWRIT() returns true<br />

(‘.T.’) if the writing operation is successful; otherwise, it returns false (‘.F.’).<br />

368.14.137 MEMVARBLOCK()<br />

MEMVARBLOCK(cMemvarName) ⇒ bMemvarBlock<br />

cMemvarName<br />

is the name <strong>of</strong> the variable referred to by the set-get block,<br />

specified as a character string.<br />

MEMVARBLOCK() returns a code block that when evaluated sets (assigns) or gets (retrieves)<br />

the value <strong>of</strong> the given memory variable. If cMemvarName does not exist, MEMVARBLOCK()<br />

returns NIL.<br />

368.14.138 MIN()<br />

MIN(nExp1, nExp2) ⇒ nSmaller<br />

MIN(dExp1, dExp2) ⇒ dSmaller<br />

nExp1, nExp2 are the numeric values to compare.<br />

dExp1, dExp2 are the date values to compare.<br />

MIN() returns the smaller <strong>of</strong> the two arguments. The value returned is the same data type as the<br />

arguments.


242 volume VIII Argomenti avanzati e accessori<br />

368.14.139 MLCOUNT()<br />

Memo line count<br />

MLCOUNT(cString, [nLineLength],<br />

[nTabSize], [lWrap]) ⇒ nLines<br />

cString is the character string or memo field to count.<br />

nLineLength<br />

specifies the number <strong>of</strong> characters per line and can range from<br />

four to 254 . If not specified, the default line length is 79.<br />

nTabSize defines the tab size. If not specified, the default value is four.<br />

toggles word wrap on and <strong>of</strong>f. Specifying true (‘.T.’) toggles<br />

lWrap<br />

word wrap on; false (‘.F.’) toggles it <strong>of</strong>f. If not specified, the<br />

default value is true (‘.T.’).<br />

MLCOUNT() returns the number <strong>of</strong> lines in cString depending on the nLineLength, the<br />

nTabSize, and whether word wrapping is on or <strong>of</strong>f.<br />

368.14.140 MLCTOPOS()<br />

Memo line column to position<br />

MLCTOPOS(cText, nWidth, nLine,<br />

nCol, [nTabSize], [lWrap]) ⇒ nPosition<br />

cText<br />

nWidth<br />

nLine<br />

nCol<br />

nTabSize<br />

lWrap<br />

is the text string to scan.<br />

is the line length formatting width.<br />

is the line number counting from 1.<br />

is the column number counting from 0.<br />

is the number <strong>of</strong> columns between tab stops. If not specified,<br />

the default is 4.<br />

is the word wrap flag. If not specified, the default is true<br />

(‘.T.’).<br />

MLCTOPOS() returns the byte position within cText counting from 1.<br />

368.14.141 MLPOS()<br />

Memo line position<br />

MLPOS(cString, nLineLength,<br />

nLine, [nTabSize], [lWrap]) ⇒ nPosition<br />

cString is a character string or memo field.<br />

nLineLength specifies the number <strong>of</strong> characters per line.<br />

nLine<br />

specifies the line number.<br />

nTabSize defines the tab size. The default is four.<br />

toggles word wrap on and <strong>of</strong>f. Specifying true (‘.T.’) toggles<br />

lWrap<br />

word wrap on, and false (‘.F.’) toggles it <strong>of</strong>f. The default is<br />

true (‘.T.’).<br />

MLPOS() returns the character position <strong>of</strong> nLine in cString as an integer numeric value. If<br />

nLine is greater than the number <strong>of</strong> lines in cString, MLPOS() returns the length <strong>of</strong> cString.


nanoBase 1997 user manual 243<br />

368.14.142 MONTH()<br />

MONTH(dDate) ⇒ nMonth<br />

dDate is the date value to convert.<br />

MONTH() returns an integer numeric value in the range <strong>of</strong> zero to 12. Specifying a null date<br />

(CTOD("")) returns zero.<br />

368.14.143 MPOSTOLC()<br />

Memo position to line column<br />

MPOSTOLC(cText, nWidth, nPos,<br />

[nTabSize], [lWrap]) ⇒ aLineColumn<br />

cText<br />

nWidth<br />

nPos<br />

nTabSize<br />

lWrap<br />

is a text string.<br />

is the length <strong>of</strong> the formatted line.<br />

is the byte position within text counting from one.<br />

is the number <strong>of</strong> columns between tab stops. If not specified,<br />

the default is four.<br />

is the word wrap flag. If not specified, the default is true<br />

(‘.T.’).<br />

MPOSTOLC() returns an array containing the line and the column values for the specified byte<br />

position, nPos. MPOSTOLC() is a memo function that determines the formatted line and column<br />

corresponding to a particular byte position within cText. Note that the line number returned is<br />

one-relative, the column number is zero-relative. This is compatible with MEMOEDIT(). nPos<br />

is one-relative, compatible with AT(), RAT(), and other string functions.<br />

368.14.144 NETERR()<br />

Net error<br />

NETERR([lNewError]) ⇒ lError<br />

lNewError<br />

if specified sets the value returned by NETERR() to the specified<br />

status. lNewError can be either true (‘.T.’) or false<br />

(‘.F.’). Setting NETERR() to a specified value allows the<br />

runtime error handler to control the way certain file errors are<br />

handled.<br />

NETERR() returns true (‘.T.’) if a USE or APPEND BLANK fails. The initial value <strong>of</strong><br />

NETERR() is false (‘.F.’). If the current process is not running under a network operating system,<br />

NETERR() always returns false (‘.F.’).


244 volume VIII Argomenti avanzati e accessori<br />

368.14.145 NETNAME()<br />

NETNAME() ⇒ cWorkstationName<br />

NETNAME() returns the workstation identification as a character string up to 15 characters in<br />

length. If the workstation identification was never set or the application is not operating under<br />

the IBM PC Network, it returns a null string ("").<br />

368.14.146 NEXTKEY()<br />

NEXTKEY() ⇒ nInkeyCode<br />

NEXTKEY() returns an integer numeric value ranging from -39 to 386. If the keyboard buffer<br />

is empty, NEXTKEY() returns zero. If SET TYPEAHEAD is zero, NEXTKEY() always returns<br />

zero. NEXTKEY() is like the INKEY() function, but differs in one fundamental respect.<br />

INKEY() removes the pending key from the keyboard buffer and updates LASTKEY() with the<br />

value <strong>of</strong> the key. NEXTKEY(), by contrast, reads, but does not remove the key from the keyboard<br />

buffer and does not update LASTKEY().<br />

368.14.147 NOSNOW()<br />

NOSNOW(lToggle) ⇒ NIL<br />

lToggle<br />

NOSNOW() is used to suppress snow on old CGA monitors.<br />

368.14.148 ORDBAGEXT()<br />

ORDBAGEXT() ⇒ cBagExt<br />

is a logical value that toggles the current state <strong>of</strong> snow suppression.<br />

A value <strong>of</strong> true (‘.T.’) enables the snow suppression<br />

on, while a value <strong>of</strong> false (‘.F.’) disables snow suppression.<br />

ORDBAGEXT() returns a character expression that is the default Order Bag extension <strong>of</strong> the<br />

current work area. cBagExt is determined by the RDD active in the current work area.<br />

368.14.149 ORDBAGNAME()<br />

ORDBAGNAME(nOrder | cOrderName) ⇒ cOrderBagName<br />

nOrder<br />

cOrderName<br />

is an integer that identifies the position in the Order List <strong>of</strong> the<br />

target Order whose Order Bag name is sought.<br />

is a character string that represents the name <strong>of</strong> the target Order<br />

whose Order Bag name is sought.<br />

ORDBAGNAME() returns a character string, the Order Bag name <strong>of</strong> the specific Order.


nanoBase 1997 user manual 245<br />

368.14.150 ORDCREATE()<br />

ORDCREATE(cOrderBagName,[cOrderName], cExpKey, [bExpKey],<br />

[lUnique]) ⇒ NIL<br />

cOrderBagName is the name <strong>of</strong> a disk file containing one or more Orders.<br />

cOrderName is the name <strong>of</strong> the Order to be created.<br />

is an expression that returns the key value to place in the Order<br />

cExpKey<br />

for each record in the current work area. The maximum length<br />

<strong>of</strong> the index key expression is determined by the database<br />

driver.<br />

bExpKey<br />

is a code block that evaluates to a key value that is placed in<br />

the Order for each record in the current work area.<br />

lUnique<br />

specifies whether a unique Order is to be created. Default is<br />

the current global _SET_UNIQUE setting.<br />

ORDCREATE() is an Order management function that creates an Order in the current work area.<br />

It works like DBCREATEINDEX() except that it lets you create Orders in RDDs that recognize<br />

multiple Order Bags.<br />

368.14.151 ORDDESTROY()<br />

ORDDESTROY(cOrderName [, cOrderBagName ]) ⇒ NIL<br />

cOrderName<br />

is the name <strong>of</strong> the Order to be removed from the current or<br />

specified work area.<br />

cOrderBagName is the name <strong>of</strong> a disk file containing one or more Orders.<br />

ORDDESTROY() is an Order management function that removes a specified Order from<br />

multiple-Order Bags. ORDDESTROY() is not supported for DBFNDX and DBFNTX.<br />

368.14.152 ORDFOR()<br />

ORDFOR(cOrderName | nOrder [, cOrderBagName]) ⇒ cForExp<br />

cOrderName<br />

is the name <strong>of</strong> the target Order, whose cForExp is sought.<br />

nOrder<br />

is an integer that identifies the position in the Order List <strong>of</strong> the<br />

target Order whose cForExp is sought.<br />

cOrderBagName is the name <strong>of</strong> an Order Bag containing one or more Orders.<br />

ORDFOR() returns a character expression, cForExp, that represents the FOR condition <strong>of</strong> the<br />

specified Order. If the Order was not created using the FOR clause the return value will be an<br />

empty string (""). If the database driver does not support the FOR condition, it may either return<br />

an empty string ("") or raise an "unsupported function" error, depending on the driver.


246 volume VIII Argomenti avanzati e accessori<br />

368.14.153 ORDKEY()<br />

ORDKEY(cOrderName | nOrder [, cOrderBagName]) ⇒ cExpKey<br />

cOrderName<br />

is the name <strong>of</strong> an Order, a logical ordering <strong>of</strong> a database.<br />

nOrder<br />

is an integer that identifies the position in the Order List <strong>of</strong> the<br />

target Order whose cExpKey is sought.<br />

cOrderBagName is the name <strong>of</strong> a disk file containing one or more Orders.<br />

ORDKEY() is an Order management function that returns a character expression, cExpKey, that<br />

represents the key expression <strong>of</strong> the specified Order.<br />

368.14.154 ORDLISTADD()<br />

ORDLISTADD(cOrderBagName [, cOrderName]) ⇒ NIL<br />

cOrderBagName is the name <strong>of</strong> a disk file containing one or more Orders.<br />

the name <strong>of</strong> the specific Order from the Order Bag to be added<br />

to the Order List <strong>of</strong> the current work area. If you do not spec-<br />

cOrderName<br />

ify cOrderName, all orders in the Order Bag are added to the<br />

Order List <strong>of</strong> the current work area.<br />

ORDLISTADD() is an Order management function that adds the contents <strong>of</strong> an Order Bag , or<br />

a single Order in an Order Bag, to the Order List. Any Orders already associated with the work<br />

area continue to be active. If the newly opened Order Bag contains the only Order associated<br />

with the work area, it becomes the controlling Order; otherwise, the controlling Order remains<br />

unchanged.<br />

368.14.155 ORDLISTCLEAR()<br />

ORDLISTCLEAR() ⇒ NIL<br />

ORDLISTCLEAR() is an Order management function that removes all Orders from the Order<br />

List for the current work area.<br />

368.14.156 ORDLISTREBUILD()<br />

ORDLISTREBUILD() ⇒ NIL<br />

ORDLISTREBUILD() is an Order management function that rebuilds all the orders in the current<br />

Order List.<br />

368.14.157 ORDNAME()<br />

ORDNAME(nOrder[,cOrderBagName]) ⇒ cOrderName<br />

nOrder<br />

is an integer that identifies the position in the Order List <strong>of</strong> the<br />

target Order whose database name is sought.<br />

cOrderBagName is the name <strong>of</strong> a disk file containing one or more Orders.


nanoBase 1997 user manual 247<br />

ORDNAME() returns the name <strong>of</strong> the specified Order in the current Order List or the specified<br />

Order Bag if opened in the Current Order list.<br />

368.14.158 ORDNUMBER()<br />

ORDNUMBER(cOrderName [, cOrderBagName]) ⇒ nOrderNo<br />

cOrderName<br />

the name <strong>of</strong> the specific Order whose position in the Order<br />

List is sought.<br />

cOrderBagName is the name <strong>of</strong> a disk file containing one or more Orders.<br />

ORDNUMBER() returns nOrderNo, an integer that represents the position <strong>of</strong> the specified Order<br />

in the Order List.<br />

368.14.159 ORDSETFOCUS()<br />

ORDSETFOCUS([cOrderName | nOrder] [,cOrderBagName])<br />

⇒ cPrevOrderNameInFocus<br />

cOrderName<br />

is the name <strong>of</strong> the selected Order, a logical ordering <strong>of</strong> a<br />

database.<br />

nOrder<br />

is a number representing the position in the Order List <strong>of</strong> the<br />

selected Order.<br />

cOrderBagName is the name <strong>of</strong> a disk file containing one or more Orders.<br />

ORDSETFOCUS() is an Order management function that returns the Order Name <strong>of</strong> the previous<br />

controlling Order and optionally sets the focus to an new Order.<br />

368.14.160 OS()<br />

OS() ⇒ cOsName<br />

OS() returns the operating system name as a character string.<br />

368.14.161 OUTERR()<br />

Output error<br />

OUTERR(exp_list) ⇒ NIL<br />

exp_list<br />

is a list <strong>of</strong> values to display and can consist <strong>of</strong> any combination<br />

<strong>of</strong> data types including memo.<br />

OUTERR() is identical to OUTSTD() except that it writes to the standard error device rather<br />

than the standard output device. Output sent to the standard error device bypasses the console<br />

and output devices as well as any DOS redirection. It is typically used to log error messages in a<br />

manner that will not interfere with the standard screen or printer output.


248 volume VIII Argomenti avanzati e accessori<br />

368.14.162 OUTSTD()<br />

Output standard<br />

OUTSTD(exp_list) ⇒ NIL<br />

exp_list<br />

is a list <strong>of</strong> values to display and can consist <strong>of</strong> any combination<br />

<strong>of</strong> data types including memo.<br />

OUTSTD() is a simple output function similar to QOUT(), except that it writes to the STDOUT<br />

device (instead <strong>of</strong> to the console output stream).<br />

368.14.163 PAD?()<br />

PADL(exp, nLength, [cFillChar]) ⇒ cPaddedString<br />

PADC(exp, nLength, [cFillChar]) ⇒ cPaddedString<br />

PADR(exp, nLength, [cFillChar]) ⇒ cPaddedString<br />

exp<br />

is a character, numeric, or date value to pad with a fill character.<br />

nLength is the length <strong>of</strong> the character string to return.<br />

cFillChar<br />

is the character to pad exp with. If not specified, the default is<br />

a space character.<br />

PADC(), PADL(), and PADR() are character functions that pad character, date, and numeric<br />

values with a fill character to create a new character string <strong>of</strong> a specified length. PADC() centers<br />

exp within nLength adding fill characters to the left and right sides; PADL() adds fill characters<br />

on the left side; and PADR() adds fill characters on the right side.<br />

368.14.164 PCOL()<br />

Printed column<br />

PCOL() ⇒ nColumn<br />

PCOL() returns an integer numeric value representing the last printed column position, plus one.<br />

The beginning column position is zero.<br />

368.14.165 PROW()<br />

Printed row<br />

PROW() ⇒ nRow<br />

PROW() returns an integer numeric value that represents the number <strong>of</strong> the current line sent to<br />

the printer. The beginning row position is zero.


nanoBase 1997 user manual 249<br />

368.14.166 QOUT()<br />

QOUT([exp_list]) ⇒ NIL<br />

QQOUT([exp_list]) ⇒ NIL<br />

exp_list<br />

is a comma-separated list <strong>of</strong> expressions (<strong>of</strong> any data type<br />

other than array or block) to display to the console. If no argument<br />

is specified and QOUT() is specified, a carriage return/linefeed<br />

pair is displayed. If QQOUT() is specified without<br />

arguments, nothing displays.<br />

QOUT() and QQOUT() are console functions. They display the results <strong>of</strong> one or more expressions<br />

to the console. QOUT() outputs carriage return and linefeed characters before displaying<br />

the results <strong>of</strong> exp_list. QQOUT() displays the results <strong>of</strong> exp_list at the current ROW() and COL()<br />

position. When QOUT() and QQOUT() display to the console, ROW() and COL() are updated.<br />

368.14.167 RAT()<br />

Right at<br />

RAT(cSearch, cTarget) ⇒ nPosition<br />

cSearch<br />

is the character string to locate.<br />

cTarget is the character string to search.<br />

RAT() returns the position <strong>of</strong> cSearch within cTarget as an integer numeric value, starting the<br />

search from the right. If cSearch is not found, RAT() returns zero.<br />

368.14.168 RDDLIST()<br />

RDDLIST([nRDDType]) ⇒ aRDDList<br />

nRDDType<br />

is an integer that represents the type <strong>of</strong> the RDD you wish to<br />

list.<br />

nRDDType = 1 Full RDD implementation<br />

nRDDType = 2 Import/Export only driver.<br />

RDDLIST() returns a one-dimensional array <strong>of</strong> the RDD names registered with the application<br />

as nRDDType.<br />

368.14.169 RDDNAME()<br />

RDDNAME() ⇒ cRDDName<br />

RDDNAME() returns a character string, cRDDName, the registered name <strong>of</strong> the active RDD in<br />

the current or specified work area.


250 volume VIII Argomenti avanzati e accessori<br />

368.14.170 RDDSETDEFAULT()<br />

RDDSETDEFAULT([cNewDefaultRDD]) ⇒ cPreviousDefaultRDD<br />

cNewDefaultRDD<br />

is a character string, the name <strong>of</strong> the RDD that is to be made<br />

the new default RDD in the application.<br />

RDDSETDEFAULT() is an RDD function that sets or returns the name <strong>of</strong> the previous default<br />

RDD driver and, optionally, sets the current driver to the new RDD driver specified by<br />

cNewDefaultRDD.<br />

368.14.171 READINSERT()<br />

READINSERT([lToggle]) ⇒ lCurrentMode<br />

lToggle<br />

toggles the insert mode on or <strong>of</strong>f. True (‘.T.’) turns insert on,<br />

while false (‘.F.’) turns insert <strong>of</strong>f. The default is false (‘.F.’)<br />

or the last user-selected mode in READ or MEMOEDIT().<br />

READINSERT() returns the current insert mode state as a logical value.<br />

368.14.172 READMODAL()<br />

READMODAL(aGetList) ⇒ NIL<br />

aGetList<br />

is an array containing a list <strong>of</strong> Get objects to edit.<br />

READMODAL() is like the READ command, but takes a GetList array as an argument and does<br />

not reinitialize the GetList array when it terminates. The GET system is implemented using a<br />

public array called GetList. Each time an @...GET command executes, it creates a Get object and<br />

adds to the currently visible GetList array. The standard READ command is preprocessed into a<br />

call to READMODAL() using the GetList array as its argument.<br />

368.14.173 READVAR()<br />

READVAR() ⇒ cVarName<br />

READVAR() returns the name <strong>of</strong> the variable associated with the current Get object or the variable<br />

being assigned by the current MENU TO command as an uppercase character string.<br />

368.14.174 RECNO()<br />

Record number<br />

RECNO() ⇒ nRecord<br />

RECNO() returns the current record number as an integer numeric value. If the work area contains<br />

a database file with zero records, RECNO() returns one, BOF() and EOF() both return<br />

true (‘.T.’), and LASTREC() returns zero. If the record pointer is moved past the last record,


nanoBase 1997 user manual 251<br />

RECNO() returns LASTREC() + 1 and EOF() returns true (‘.T.’). If an attempt is made to<br />

move before the first record, RECNO() returns the record number <strong>of</strong> the first logical record in the<br />

database file and BOF() returns true (‘.T.’). If no database file is open, RECNO() will return a<br />

zero.<br />

368.14.175 RECSIZE()<br />

Record size<br />

RECSIZE() ⇒ nBytes<br />

RECSIZE() returns, as a numeric value, the record length, in bytes, <strong>of</strong> the database file open in<br />

the current work area. RECSIZE() returns zero if no database file is open.<br />

368.14.176 REPLICATE()<br />

REPLICATE(cString, nCount) ⇒ cRepeatedString<br />

cString is the character string to repeat.<br />

nCount<br />

is the number <strong>of</strong> times to repeat cString.<br />

REPLICATE() returns a character string. Specifying a zero as the nCount argument returns a<br />

null string ("").<br />

368.14.177 RESTSCREEN()<br />

Restore screen<br />

RESTSCREEN([nTop], [nLeft],<br />

[nBottom], [nRight], cScreen) ⇒ NIL<br />

nTop, nLeft, nBottom, nRight<br />

cScreen<br />

define the coordinates <strong>of</strong> the screen information contained in<br />

cScreen. If the cScreen was saved without coordinates to preserve<br />

the entire screen, no screen coordinates are necessary<br />

with RESTSCREEN().<br />

is a character string containing the saved screen region.<br />

RESTSCREEN() is a screen function that redisplays a screen region saved with SAVE-<br />

SCREEN(). The target screen location may be the same as or different than the original location<br />

when the screen region was saved.<br />

368.14.178 RIGHT()<br />

RIGHT(cString, nCount) ⇒ cSubString<br />

cString is the character string from which to extract characters.<br />

nCount is the number <strong>of</strong> characters to extract.<br />

RIGHT() returns the rightmost nCount characters <strong>of</strong> cString. If nCount is zero, RIGHT() returns<br />

a null string (""). If nCount is negative or larger than the length <strong>of</strong> the character string,


252 volume VIII Argomenti avanzati e accessori<br />

RIGHT() returns cString.<br />

368.14.179 RLOCK()<br />

Record lock<br />

RLOCK() ⇒ lSuccess<br />

RLOCK() is a network function that locks the current record, preventing other users from updating<br />

the record until the lock is released. RLOCK() provides a shared lock, allowing other users<br />

read-only access to the locked record while allowing only the current user to modify it. A record<br />

lock remains until another record is locked, an UNLOCK is executed, the current database file is<br />

closed, or an FLOCK() is obtained on the current database file.<br />

368.14.180 ROUND()<br />

ROUND(nNumber, nDecimals) ⇒ nRounded<br />

nNumber is the numeric value to round.<br />

nDecimals<br />

defines the number <strong>of</strong> decimal places to retain. Specifying a<br />

negative nDecimals value rounds whole number digits.<br />

ROUND() is a numeric function that rounds nNumber to the number <strong>of</strong> places specified by<br />

nDecimals. Specifying a zero or negative value for nDecimals allows rounding <strong>of</strong> whole numbers.<br />

A negative nDecimals indicates the number <strong>of</strong> digits to the left <strong>of</strong> the decimal point to<br />

round. Digits between five to nine, inclusive, are rounded up. Digits below five are rounded<br />

down.<br />

368.14.181 ROW()<br />

ROW() ⇒ nRow<br />

ROW() returns the cursor row position as an integer numeric value. The range <strong>of</strong> the return value<br />

is zero to MAXROW().<br />

368.14.182 RTRIM()<br />

Right trim<br />

[R]TRIM(cString) ⇒ cTrimString<br />

cString is the character string to copy without trailing spaces.<br />

RTRIM() returns a copy <strong>of</strong> cString with the trailing spaces removed. If cString is a null string<br />

("") or all spaces, RTRIM() returns a null string ("").


nanoBase 1997 user manual 253<br />

368.14.183 SAVESCREEN()<br />

SAVESCREEN([nTop], [nLeft],<br />

[nBottom], [nRight]) ⇒ cScreen<br />

nTop, nLeft, nBottom, nRight<br />

define the coordinates <strong>of</strong> the screen region to save. Default is<br />

the entire screen.<br />

SAVESCREEN() returns the specified screen region as a character string.<br />

368.14.184 SCROLL()<br />

SCROLL([nTop], [nLeft],<br />

[nBottom], [nRight], [nVert] [nHoriz]) ⇒ NIL<br />

nTop, nLeft, nBottom, nRight define the scroll region coordinates.<br />

nVert<br />

nHoriz<br />

defines the number <strong>of</strong> rows to scroll, vertically. A positive<br />

value scrolls up the specified number <strong>of</strong> rows. A negative<br />

value scrolls down the specified number <strong>of</strong> rows. A value <strong>of</strong><br />

zero disables vertical scrolling. If nVert is not specified, zero<br />

is assumed.<br />

defines the number <strong>of</strong> rows to scroll horizontally. A positive<br />

value scrolls left the specified number <strong>of</strong> columns. A negative<br />

value scrolls right the specified number <strong>of</strong> columns. A value<br />

<strong>of</strong> zero disables horizontal scrolling. If nHoriz is not specified,<br />

zero is assumed. If you supply neither nVert or nHoriz<br />

parameters to SCROLL(), the area specified by the first four<br />

parameters will be blanked.<br />

SCROLL() is a screen function that scrolls a screen region up or down a specified number <strong>of</strong><br />

rows. When a screen scrolls up, the first line <strong>of</strong> the region is erased, all other lines are moved<br />

up, and a blank line is displayed in the current standard color on the bottom line <strong>of</strong> the specified<br />

region. If the region scrolls down, the operation is reversed. If the screen region is scrolled more<br />

than one line, this process is repeated.<br />

368.14.185 SECONDS()<br />

SECONDS() ⇒ nSeconds<br />

SECONDS() returns the system time as a numeric value in the form seconds.hundredths. The numeric<br />

value returned is the number <strong>of</strong> seconds elapsed since midnight, and is based on a twentyfour<br />

hour clock in a range from zero to 86399.<br />

368.14.186 SELECT()<br />

SELECT([cAlias]) ⇒ nWorkArea<br />

cAlias<br />

is the target work area alias name.


254 volume VIII Argomenti avanzati e accessori<br />

SELECT() returns the work area <strong>of</strong> the specified alias as a integer numeric value.<br />

368.14.187 SET()<br />

SET(nSpecifier, [expNewSetting], [lOpenMode])<br />

⇒ CurrentSetting<br />

nSpecifier<br />

expNewSetting<br />

lOpenMode<br />

SET() returns the current value <strong>of</strong> the specified setting.<br />

is a numeric value that identifies the setting to be inspected or<br />

changed.<br />

is an optional argument that specifies a new value for<br />

the nSpecifier. The type <strong>of</strong> expNewSetting depends on<br />

nSpecifier.<br />

is a logical value that indicates whether or not files are opened<br />

for some settings. A value <strong>of</strong> false (‘.F.’) means the file<br />

should be truncated. A value <strong>of</strong> true (‘.T.’) means the file<br />

should be opened in append mode. In either case, if the file<br />

does not exist, it is created. If this argument is not specified,<br />

the default is append mode.<br />

Inside nB, the function SET() is not so easy to use as inside the Clipper environment. This<br />

because nB cannot support manifest constants and a numeric specifier nSpecifier is not easy to<br />

manage. Instead <strong>of</strong> SET() you can use SETVERB().<br />

368.14.188 SETBLINK()<br />

SETBLINK([lToggle]) ⇒ lCurrentSetting<br />

lToggle<br />

SETBLINK() returns the current setting as a logical value.<br />

368.14.189 SETCANCEL()<br />

SETCANCEL([lToggle]) ⇒ lCurrentSetting<br />

lToggle<br />

SETCANCEL() returns the current setting as a logical value.<br />

changes the meaning <strong>of</strong> the asterisk (*) character when it<br />

is encountered in a SETCOLOR() string. Specifying true<br />

(‘.T.’) sets character blinking on and false (‘.F.’) sets background<br />

intensity. The default is true (‘.T.’).<br />

changes the availability <strong>of</strong> Alt-C and Ctrl-Break as termination<br />

keys. Specifying true (‘.T.’) allows either <strong>of</strong> these keys<br />

to terminate an application and false (‘.F.’) disables both<br />

keys. The default is true (‘.T.’).


nanoBase 1997 user manual 255<br />

368.14.190 SETCOLOR()<br />

SETCOLOR([cColorString]) ⇒ cColorString<br />

cColorString<br />

368.14.191 SETCURSOR()<br />

SETCURSOR([nCursorShape]) ⇒ nCurrentSetting<br />

nCursorShape<br />

is a character string containing a list <strong>of</strong> color attribute settings<br />

for subsequent screen painting.<br />

is a number indicating the shape <strong>of</strong> the cursor.<br />

nCursorShape == 0 None<br />

nCursorShape == 1 Underline<br />

nCursorShape == 2 Lower half block<br />

nCursorShape == 3 Full block<br />

nCursorShape == 4 Upper half block<br />

SETCURSOR() returns the current cursor shape as a numeric value.<br />

368.14.192 SETKEY()<br />

SETKEY(nInkeyCode, [bAction]) ⇒ bCurrentAction<br />

nInkeyCode is the INKEY() value <strong>of</strong> the key to be associated or queried.<br />

bAction<br />

specifies a code block that is automatically executed whenever<br />

the specified key is pressed during a wait state.<br />

SETKEY() returns the action block currently associated with the specified key, or NIL if the<br />

specified key is not currently associated with a block.<br />

368.14.193 SETMODE()<br />

SETMODE(nRows, nCols) ⇒ lSuccess<br />

nRows<br />

nCols<br />

is the number <strong>of</strong> rows in the desired display mode.<br />

is the number <strong>of</strong> columns in the desired display mode.<br />

SETMODE() is an environment function that attempts to change the mode <strong>of</strong> the display hardware<br />

to match the number <strong>of</strong> rows and columns specified. The change in screen size is reflected<br />

in the values returned by MAXROW() and MAXCOL().


256 volume VIII Argomenti avanzati e accessori<br />

368.14.194 SETPOS()<br />

Set position<br />

SETPOS(nRow, nCol) ⇒ NIL<br />

nRow, nCol<br />

define the new screen position <strong>of</strong> the cursor. These values may<br />

range from 0, 0 to MAXROW(), MAXCOL().<br />

SETPOS() is an environment function that moves the cursor to a new position on the screen.<br />

After the cursor is positioned, ROW() and COL() are updated accordingly.<br />

368.14.195 SETPRC()<br />

Set printer row column<br />

SETPRC(nRow, nCol) ⇒ NIL<br />

nRow is the new PROW() value.<br />

nCol is the new PCOL() value.<br />

SETPRC() is a printer function that sends control codes to the printer without changing the<br />

tracking <strong>of</strong> the printhead position.<br />

368.14.196 SOUNDEX()<br />

SOUNDEX(cString) ⇒ cSoundexString<br />

cString is the character string to convert.<br />

SOUNDEX() returns a four-digit character string in the form A999.<br />

368.14.197 SPACE()<br />

SPACE(nCount) ⇒ cSpaces<br />

nCount<br />

is the number <strong>of</strong> spaces to return.<br />

SPACE() returns a character string. If nCount is zero, SPACE() returns a null string ("").<br />

368.14.198 SQRT()<br />

SQRT(nNumber) ⇒ nRoot<br />

nNumber<br />

is a positive number to take the square root <strong>of</strong>.<br />

SQRT() returns a numeric value calculated to double precision. The number <strong>of</strong> decimal places<br />

displayed is determined solely by SET DECIMALS regardless <strong>of</strong> SET FIXED. A negative


nanoBase 1997 user manual 257<br />

nNumber returns zero.<br />

368.14.199 STR()<br />

String<br />

STR(nNumber, [nLength], [nDecimals]) ⇒ cNumber<br />

nNumber<br />

nLength<br />

nDecimals<br />

STR() returns nNumber formatted as a character string.<br />

368.14.200 STRTRAN()<br />

STRTRAN(cString, cSearch,<br />

[cReplace], [nStart], [nCount]) ⇒ cNewString<br />

is the numeric expression to convert to a character string.<br />

is the length <strong>of</strong> the character string to return, including decimal<br />

digits, decimal point, and sign.<br />

is the number <strong>of</strong> decimal places to return.<br />

cString is the character string or memo field to search.<br />

cSearch<br />

is the sequence <strong>of</strong> characters to locate.<br />

is the sequence <strong>of</strong> characters with which to replace cSearch.<br />

cReplace<br />

If this argument is not specified, the specified instances <strong>of</strong> the<br />

search argument are replaced with a null string ("").<br />

nStart<br />

is the first occurrence that will be replaced. If this argument is<br />

omitted, the default is one.<br />

nCount<br />

is the number <strong>of</strong> occurrences to replace. If this argument is<br />

not specified, the default is all.<br />

STRTRAN() returns a new character string with the specified instances <strong>of</strong> cSearch replaced with<br />

cReplace.<br />

368.14.201 STUFF()<br />

STUFF(cString, nStart,<br />

nDelete, cInsert) ⇒ cNewString<br />

cString<br />

is the target character string into which characters are inserted<br />

and deleted.<br />

nStart<br />

is the starting position in the target string where the insertion/deletion<br />

occurs.<br />

nDelete is the number <strong>of</strong> characters to delete.<br />

cInsert<br />

is the string to insert.<br />

STUFF() returns a copy <strong>of</strong> cString with the specified characters deleted and with cInsert inserted.


258 volume VIII Argomenti avanzati e accessori<br />

368.14.202 SUBSTR()<br />

Sub string<br />

SUBSTR(cString, nStart, [nCount]) ⇒ cSubstring<br />

cString is the character string from which to extract a substring.<br />

is the starting position in cString. If nStart is positive, it is<br />

nStart<br />

relative to the leftmost character in cString. If nStart is negative,<br />

it is relative to the rightmost character in the cString.<br />

is the number <strong>of</strong> characters to extract. If omitted, the substring<br />

begins at nStart and continues to the end <strong>of</strong> the string. If<br />

nCount<br />

nCount is greater than the number <strong>of</strong> characters from nStart<br />

to the end <strong>of</strong> cString, the extra is ignored.<br />

SUBSTR() is a character function that extracts a substring from another character string or memo<br />

field.<br />

368.14.203 TIME()<br />

TIME() ⇒ cTimeString<br />

TIME() returns the system time as a character string in the form hh:mm:ss. hh is hours in 24-hour<br />

format, mm is minutes, and ss is seconds.<br />

TIME() is a time function that displays the system time on the screen or prints it on a report.<br />

368.14.204 TONE()<br />

TONE(nFrequency, nDuration) ⇒ NIL<br />

nFrequency<br />

nDuration<br />

is a positive numeric value indicating the frequency <strong>of</strong> the<br />

tone to sound.<br />

is a positive numeric value indicating the duration <strong>of</strong> the tone<br />

measured in increments <strong>of</strong> 1/18 <strong>of</strong> a second. For example, an<br />

nDuration value <strong>of</strong> 18 represents one second.<br />

For both arguments, noninteger values are truncated (not rounded) to their integer portion.<br />

368.14.205 TRANSFORM()<br />

TRANSFORM(exp, cSayPicture) ⇒ cFormatString<br />

exp<br />

cSayPicture<br />

is the value to format. This expression can be any valid data<br />

type except array, code block, and NIL.<br />

is a string <strong>of</strong> picture and template characters that describes the<br />

format <strong>of</strong> the returned haracter string.<br />

TRANSFORM() converts exp to a formatted character string as defined by cSayPicture.


nanoBase 1997 user manual 259<br />

368.14.206 TYPE()<br />

TYPE(cExp) ⇒ cType<br />

cExp<br />

TYPE() returns one <strong>of</strong> the following characters:<br />

A<br />

Array<br />

B Block<br />

C Character<br />

D Date<br />

L Logical<br />

M Memo<br />

N Numeric<br />

O<br />

Object<br />

U NIL, local, or static<br />

UE Error syntactical<br />

UI Error indeterminate<br />

is a character expression whose type is to be determined.<br />

cExp can be a field, with or without the alias, a private or<br />

public variable, or an expression <strong>of</strong> any type.<br />

TYPE() is a system function that returns the type <strong>of</strong> the specified expression. TYPE() is like<br />

VALTYPE() but uses the macro operator (&) to determine the type <strong>of</strong> the argument. VALTYPE(),<br />

by contrast, evaluates an expression and determines the data type <strong>of</strong> the return value.<br />

368.14.207 UPDATED()<br />

UPDATED() ⇒ lChange<br />

UPDATED() returns true (‘.T.’) if data in a GET is added or changed; otherwise, it returns false<br />

(‘.F.’).<br />

368.14.208 UPPER()<br />

UPPER(cString) ⇒ cUpperString<br />

cString is the character string to convert.<br />

UPPER() returns a copy <strong>of</strong> cString with all alphabetical characters converted to uppercase. All<br />

other characters remain the same as in the original string.<br />

368.14.209 USED()<br />

USED() ⇒ lDbfOpen<br />

USED() returns true (‘.T.’) if there is a database file in USE in the current work area; otherwise,<br />

it returns false (‘.F.’).


260 volume VIII Argomenti avanzati e accessori<br />

368.14.210 VAL()<br />

Value<br />

VAL(cNumber) ⇒ nNumber<br />

cNumber<br />

is the character expression to convert.<br />

VAL() is a character conversion function that converts a character string containing numeric<br />

digits to a numeric value. When VAL() is executed, it evaluates cNumber until a second decimal<br />

point, the first non-numeric character, or the end <strong>of</strong> the expression is encountered.<br />

368.14.211 VALTYPE()<br />

Value type<br />

VALTYPE(exp) ⇒ cType<br />

exp is an expression <strong>of</strong> any type.<br />

VALTYPE() returns a single character representing the data type returned by exp. VALTYPE()<br />

returns one <strong>of</strong> the following characters:<br />

A<br />

Array<br />

B Block<br />

C Character<br />

D Date<br />

L Logical<br />

M Memo<br />

N Numeric<br />

O<br />

Object<br />

U NIL<br />

VALTYPE() is a system function that takes a single argument, evaluates it, and returns a one<br />

character string describing the data type <strong>of</strong> the return value.<br />

368.14.212 YEAR()<br />

YEAR(dDate) ⇒ nYear<br />

dDate is the date value to convert.<br />

YEAR() returns the year <strong>of</strong> the specified date value including the century digits as a four-digit<br />

numeric value. The value returned is not affected by the current DATE or CENTURY format.<br />

Specifying a null date (CTOD("")) returns zero.


nanoBase 1997 user manual 261<br />

368.15 nB functions<br />

Some functions made into nB are available for macro use. Not all available functions are here<br />

documented.<br />

368.15.1 ACCEPT()<br />

ACCEPT( Field, [cMessage], [cHeader] ) ⇒ updatedField|NIL<br />

It is a prompt function that shows cMessage asking to type something into Field. It returns the<br />

updated data or NIL if [ Esc ] was pressed. The string cHeader is showed centered at the top<br />

window.<br />

368.15.2 ACHOICE()<br />

ACHOICE(nTop, nLeft, nBottom, nRight,<br />

acMenuItems,<br />

[alSelectableItems],<br />

[nInitialItem],<br />

[lButtons | aButtons]) ⇒ nPosition<br />

nTop, nLeft, nBottom, nRight are the window coordinates.<br />

acMenuItems<br />

is an array <strong>of</strong> character strings to display as the menu items.<br />

is a parallel array <strong>of</strong> logical values (one element for each item<br />

in acMenuItems) that specify the selectable menu items. Elements<br />

can be logical values or character strings. If the ele-<br />

alSelectableItems<br />

ment is a character string, it is evaluated as a macro expression<br />

which should evaluate to a logical data type. A value <strong>of</strong><br />

false (‘.F.’) means that the corresponding menu item is not<br />

available, and a value <strong>of</strong> true (‘.T.’) means that it is available.<br />

By default, all menu items are available for selection.<br />

nInitialItem<br />

is the position in the acMenuItems array <strong>of</strong> the item that will<br />

be highlighted when the menu is initially displayed.<br />

lButtons<br />

if True means that default buttons will appear.<br />

aButtons<br />

is an array <strong>of</strong> buttons.<br />

aButtons[n][1] == N the nth button row position;<br />

aButtons[n][2] == N the nth button column position;<br />

aButtons[n][3] == C the nth button text;<br />

aButtons[n][4] == B the nth button code block.<br />

ACHOICE() returns the numeric position in the acMenuItems array <strong>of</strong> the menu item selected.<br />

If no choice is made, ACHOICE() returns zero.<br />

368.15.3 ACHOICEWINDOW()<br />

ACHOICEWINDOW( acMenuItems, [cDescription],<br />

nTop, nLeft, nBottom, nRight,<br />

[alSelectableItems],<br />

[nInitialItem] ) ⇒ nPosition<br />

acMenuItems<br />

is an array <strong>of</strong> character strings to display as the menu items.


262 volume VIII Argomenti avanzati e accessori<br />

cDescription is a header to be shown at the top <strong>of</strong> window.<br />

nTop, nLeft, nBottom, nRight are the window coordinates.<br />

is a parallel array <strong>of</strong> logical values (one element for each item<br />

in acMenuItems) that specify the selectable menu items. Elements<br />

can be logical values or character strings. If the element<br />

is a character string, it is evaluated as a macro expres-<br />

alSelectableItems<br />

sion which should evaluate to a logical data type. A value <strong>of</strong><br />

false (‘.F.’) means that the corresponding menu item is not<br />

available, and a value <strong>of</strong> true (‘.T.’) means that it is available.<br />

By default, all menu items are available for selection.<br />

is the position in the acMenuItems array <strong>of</strong> the item that will<br />

nInitialItem<br />

be highlighted when the menu is initially displayed.<br />

ACHOICEWINDOW() calls ACHOICE() with a window border around the ACHOICE() screen<br />

area.<br />

368.15.4 ALERTBOX()<br />

ALERTBOX( cMessage, [aOptions] ) ⇒ nChoice<br />

is the message text displayed, centered, in the alert box. If the<br />

cMessage<br />

message contains one or more semicolons, the text after the<br />

semicolons is centered on succeeding lines in the dialog box.<br />

aOptions defines a list <strong>of</strong> up to 4 possible responses to the dialog box.<br />

ALERTBOX() returns a numeric value indicating which option was chosen. If the [ Esc ] key is<br />

pressed, the value returned is zero. The ALERTBOX() function creates a simple modal dialog.<br />

The user can respond by moving a highlight bar and pressing the Return or SpaceBar keys, or<br />

by pressing the key corresponding to the first letter <strong>of</strong> the option. If aOptions is not supplied, a<br />

single "Ok" option is presented.<br />

ALERTBOX() is similar to ALERT() but it accept mouse input.<br />

368.15.5 ATB()<br />

ATB( [nTop], [nLeft], [nBottom], [nRight],<br />

aArray, [nSubscript],<br />

[acColSayPic],<br />

[acColTopSep], [acColBodySep], [acColBotSep],<br />

[acColHead], [acColFoot],<br />

[abColValid],<br />

[abColMsg],<br />

[cColor], [abColColors],<br />

[lModify],<br />

[lButtons | aButtons] ) ⇒ NIL<br />

nTop, nLeft, nBottom, nRight defines the screen area where browse have to take place.<br />

aArray bidimensional array to be browsed.<br />

nSubscript starting array position.<br />

acColSayPic is the picture array.<br />

acColTopSep is the top separation array: default is chr(194)+chr(196).


nanoBase 1997 user manual 263<br />

acColBodySep is the body separation array: default is chr(179).<br />

acColBotSep is the bottom separation array: default is chr(193)+chr(196).<br />

acColHead<br />

is the header array for every column.<br />

acColFoot<br />

is the footer array for every column.<br />

abColValid<br />

is the validation array that specify when a field is properly<br />

filled. The condition must be specified in code block format.<br />

is the message array that permits to show information at the<br />

abColMsg<br />

bottom <strong>of</strong> browse area. The array must be composed with<br />

code blocks which result with a character string.<br />

cColor<br />

is the color string: it may be longer than the usual 5 elements.<br />

is the color code block array. The code block receive as pa-<br />

abColColors<br />

rameter the value contained inside the field and must return<br />

an array containing two numbers: they correspond to the two<br />

color couple from cColor.<br />

lModify indicates whether the browse can modify data.<br />

lButtons<br />

if True, default buttons are displayed.<br />

aButtons<br />

array <strong>of</strong> buttons.<br />

aButtons[n][1] N the nth button row position;<br />

aButtons[n][2] N the nth button column position;<br />

aButtons[n][3] C the nth button text;<br />

aButtons[n][4] B the nth button code block.<br />

This function starts the browse <strong>of</strong> a bidimensional array. Only arrays containing monodimensional<br />

array containing the same kind <strong>of</strong> editable data are allowed. The function can handle a<br />

maximum <strong>of</strong> 61 columns.<br />

368.15.6 BCOMPILE()<br />

BCOMPILE( cString ) ⇒ bBlock<br />

Compiles the string cString and returns the code block bBlock<br />

368.15.7 BUTTON()<br />

BUTTON( @aButtons,<br />

[nRow], [nCol], [cText], [cColor],<br />

[bAction] ) ⇒ NIL<br />

aButtons<br />

the array <strong>of</strong> buttons to be increased with a new button array.<br />

nRow and nCol<br />

is the row and column starting position for the button string.<br />

cText<br />

is the text that make up the button.<br />

cColor<br />

is the color string.<br />

bAction is the code block associated to the button.<br />

This function adds to aButtons a new button array. Please note that the button array added is<br />

compatible only with the READ() function and not the other function using array <strong>of</strong> buttons: the<br />

others do not have a color string.


264 volume VIII Argomenti avanzati e accessori<br />

368.15.8 COLORARRAY()<br />

COLORARRAY( cColor ) ⇒ aColors<br />

cColors<br />

a color string to be translated into a color array.<br />

This function transform a color string into a color array. The array has as many elements as the<br />

colors contained inside cColor string.<br />

368.15.9 COORDINATE()<br />

COORDINATE( [@nTop, @nLeft], @nBottom, @nRight,<br />

[cHorizontal], [cVertical] ) ⇒ NIL<br />

nTop, nLeft, nBottom and nRight<br />

cHorozontal<br />

cVertical<br />

are the starting position <strong>of</strong> a window that is to be differently<br />

aligned.<br />

determinates the horizontal alignment:<br />

"L" all left;<br />

"l" middle left;<br />

"C" center;<br />

"c" center;<br />

"R" all right;<br />

"r" middle right.<br />

determinate the vertical alignment:<br />

"T" top;<br />

"t" up;<br />

"C" center;<br />

"c" center;<br />

"B" bottom;<br />

"b" down.<br />

This function helps with the windows alignment recalculating and modifying nTop, nLeft,<br />

nBottom and nRight in the way to obtain the desired alignment.<br />

368.15.10 COPYFILE()<br />

COPYFILE( cSourceFile, cTargetFile|cDevice ) ⇒ NIL<br />

cSourceFile the source filename.<br />

cTargetFile the target filename.<br />

cDevice<br />

the target devicename.<br />

This function copies the cSourceFile to cTargetFile or to cDevice.


nanoBase 1997 user manual 265<br />

368.15.11 DBAPP()<br />

DBAPP( cFileName, [acFields],<br />

[bForCondition], [bWhileCondition],<br />

[nNextRecords],<br />

[nRecord],<br />

[lRest],<br />

[cDriver] ) ⇒ NIL<br />

cFileName<br />

acFields<br />

bForCondition<br />

bWhileCondition<br />

nNextRecord<br />

nRecord<br />

lRest<br />

cDriver<br />

the filename containing data to append to the active alias.<br />

array <strong>of</strong> fieldnames indicating the fields that should be updated<br />

on the active alias (default is all).<br />

a code block containing the FOR condition to respect for the<br />

data append. Will be appended data that makes the evaluation<br />

<strong>of</strong> this code block True.<br />

a code block containing the WHILE condition to respect for<br />

the data append. Will be appended data as long as the evaluation<br />

<strong>of</strong> this code block is True: the first time it becomes False,<br />

the data appending is terminated.<br />

if used, means that only the first nNextRecords will be appended.<br />

if used, means that that only the record nRecord will be appended.<br />

this option is not available here also if the function saves a<br />

place for it.<br />

is the optional driver name to use to open the cFileName file.<br />

This function is used to append data to the active alias using data from the cFileName file, that<br />

in this case is a ‘.DBF’ file.<br />

368.15.12 DBCLOSE()<br />

DBCLOSE() ⇒ NIL<br />

It is a substitution function <strong>of</strong> DBCLOSEALL() to use inside "compiled" macros, as a true DB-<br />

CLOSEALL() will close the macro file too.<br />

368.15.13 DBCONTINUE()<br />

DBCONTINUE() ⇒ NIL<br />

This function resumes a pending DBLOCATE().<br />

368.15.14 DBCOPY()<br />

DBCOPY( cFileName, [acFields],<br />

[bForCondition], [bWhileCondition],<br />

[nNextRecords],<br />

[nRecord],<br />

[lRest],<br />

[cDriver] ) ⇒ NIL


266 volume VIII Argomenti avanzati e accessori<br />

cFileName<br />

acFields<br />

bForCondition<br />

bWhileCondition<br />

nNextRecord<br />

nRecord<br />

lRest<br />

cDriver<br />

the target filename for the data contained inside the active<br />

alias.<br />

array <strong>of</strong> fieldnames indicating the fields that should be used<br />

from the active alias (default is all).<br />

a code block containing the FOR condition to respect for the<br />

data copy. Will be copied the data that makes the evaluation<br />

<strong>of</strong> this code block True.<br />

a code block containing the WHILE condition to respect for<br />

the data copy. Will be copied data as long as the evaluation<br />

<strong>of</strong> this code block is True: the first time it becomes False, the<br />

data copying is terminated.<br />

if used, means that only the first nNextRecords will be<br />

copied.<br />

if used, means that that only the record nRecord will be<br />

copied.<br />

if used means that only the remaining records inside the active<br />

alias are copied.<br />

is the optional driver name to use to open the cFileName file.<br />

This function is used to copy data to cFileName form the active alias.<br />

368.15.15 DBCOPYSTRUCT()<br />

DBCOPYSTRUCT( cDatabase, [acFields] ) ⇒ NIL<br />

cDatabase<br />

acFields<br />

is a structure ‘.DBF’ file that will be filled with structure information<br />

about the active alias.<br />

is an array <strong>of</strong> fieldnames that should be taken into consideration.<br />

This function creates a structure ‘.DBF’ file copying the structure <strong>of</strong> the active alias.<br />

368.15.16 DBCOPYXSTRUCT()<br />

DBCOPYXSTRUCT( cExtendedDatabase ) ⇒ NIL<br />

cExtendedDatabase<br />

is a structure ‘.DBF’ file that will be filled with structure information<br />

about the active alias, accepting extended structure<br />

informations.<br />

This function creates a structure ‘.DBF’ file copying the structure <strong>of</strong> the active alias. This function<br />

accept non-standard structure, that is, the extended structure available inside Clipper.<br />

368.15.17 DBDELIM()<br />

DBDELIM( lCopyTo, cFileName, [cDelimiter], [acFields],<br />

[bForCondition], [bWhileCondition],<br />

[nNextRecords], [nRecord], [lRest] ) ⇒ NIL


nanoBase 1997 user manual 267<br />

lCopyTo<br />

cFileName<br />

cDelimiter<br />

acFields<br />

bForCondition<br />

bWhileCondition<br />

nNextRecord<br />

nRecord<br />

lRest<br />

if True the function work copying data to cFileName from the<br />

active alias, if False the function work appending data from<br />

cFileName to the active alias.<br />

the filename containing data to append to the active alias or<br />

to use as the target <strong>of</strong> the data copy from the active alias.<br />

the delimiter string (or character) used to separate fields inside<br />

cFileName.<br />

array <strong>of</strong> fieldnames indicating the fields <strong>of</strong> the active alias that<br />

should be taken into consideration (default is all).<br />

a code block containing the FOR condition to respect. The operation<br />

will be made for all records that respect the condition.<br />

a code block containing the WHILE condition to respect. The<br />

first time it becomes False, the operation is terminated.<br />

if used, means that only the first nNextRecords will be appended/copied.<br />

if used, means that that only the record nRecord will be ap-<br />

pended/copied.<br />

if used means that only the remaining records will be taken<br />

into consideration.<br />

This function is used to append data to the active alias using data from the cFileName file or to<br />

copy data into cFileName using the active alias as the source. cFileName is a delimited ASCII<br />

file.<br />

368.15.18 DBISTATUS()<br />

DBISTATUS() ⇒ cDBInformations<br />

This function returns the informations on the active alias in a text form.<br />

368.15.19 DBISTRUCTURE()<br />

DBISTRUCTURE() ⇒ cTextStructure | NIL<br />

This function returns the structure information on the active alias in a text form.<br />

368.15.20 DBJOIN()<br />

DBJOIN( cAlias, cDatabase,<br />

[acFields], [bForCondition] ) ⇒ NIL<br />

cAlias<br />

cDatabase<br />

acFields<br />

the name <strong>of</strong> the alias to use to merge with records from the<br />

active alias.<br />

the target ‘.DBF’ filename.<br />

the array <strong>of</strong> fieldnames which represent the projection <strong>of</strong><br />

fields form both Aliases into the new ‘.DBF’ file. If not specified,<br />

all fields from the primary work area are included in the<br />

target ‘.DBF’ file.<br />

This function creates a new database file by merging selected records and fields form two work<br />

areas (Aliases) based on a general condition. It works by making a complete pass through the<br />

secondary work area cAlias for each record in the primary work area (the active alias), evaluating


268 volume VIII Argomenti avanzati e accessori<br />

the condition for each record in the secondary work area. When bForCondition is evaluated True,<br />

a new record is created in the target database file cDatabase using the fields specified from both<br />

work areas inside acFields.<br />

368.15.21 DBLABELFORM()<br />

DBLABELFORM( cLabel, [lToPrinter], [cFile],<br />

[lNoConsole], [bForCondition], [bWhileCondition],<br />

[nNextRecords], [nRecord], [lRest], [lSample] )<br />

⇒ NIL<br />

cLabel<br />

is the name <strong>of</strong> the label file (.LBL) that contains the label<br />

format definition.<br />

lToPrinter<br />

if True, the output is copied to printer (‘LPT1:’).<br />

cFile<br />

if present, it is the name <strong>of</strong> a ASCII file where the output is<br />

copied.<br />

lNoConsole<br />

if True, the output is not sent to the console.<br />

a code block containing the FOR condition to respect for label<br />

bForCondition<br />

print. Only the records contained inside the active alias that<br />

respect the condition will be used for labels.<br />

a code block containing the WHILE condition to respect for<br />

bWhileCondition<br />

the label print. The first time that the condition is False, the<br />

label print terminates.<br />

nNextRecord<br />

if used, means that only the first nNextRecords will be used.<br />

nRecord<br />

if used, means that that only the record nRecord will be used.<br />

lRest<br />

if used means that only the remaining records inside the active<br />

alias will be used.<br />

lSample if True displays test labels as rows <strong>of</strong> asterisks.<br />

This function prints labels to the console.<br />

368.15.22 DBLIST()<br />

DBLIST( [lToDisplay], abListColumns,<br />

[lAll],<br />

[bForCondition], [bWhileCondition],<br />

[nNextRecords], [nRecord], [lRest],<br />

[lToPrinter], [cFileName] )<br />

lToDisplay if True the printout is sent to the console screen.<br />

abListColumns<br />

is an array <strong>of</strong> columns expressions to list.<br />

lAll<br />

if True prints all the records contained inside the active alias.<br />

a code block containing the FOR condition to respect. Only<br />

bForCondition<br />

the records contained inside the active alias that respect the<br />

condition will be used for list.<br />

bWhileCondition<br />

a code block containing the WHILE condition to respect. The<br />

first time that the condition is False, the list terminates.<br />

nNextRecord<br />

if used, means that only the first nNextRecords will be used.<br />

nRecord<br />

if used, means that that only the record nRecord will be used.<br />

lRest<br />

if used means that only the remaining records inside the active<br />

alias will be used.<br />

lToPrinter<br />

if True, the output is copied to printer (‘LPT1:’).<br />

cFileName<br />

if present, it is the name <strong>of</strong> a ASCII file where the output is<br />

copied.


nanoBase 1997 user manual 269<br />

This function prints a list <strong>of</strong> records to the console.<br />

368.15.23 DBLOCATE()<br />

DBLOCATE( [bForCondition], [bWhileCondition],<br />

[nNextRecords], [nRecord], [lRest] ) ⇒ NIL<br />

bForCondition<br />

bWhileCondition<br />

nNextRecord<br />

nRecord<br />

lRest<br />

a code block containing the FOR condition to respect. Only<br />

the records contained inside the active alias that respect the<br />

condition will be taken into consideration.<br />

a code block containing the WHILE condition to respect. The<br />

first time that the condition is False, the locate terminates.<br />

if used, means that only the first nNextRecords will be used.<br />

if used, means that that only the record nRecord will be used.<br />

if used means that only the remaining records inside the active<br />

alias will be used.<br />

This function searches sequentially for the first record matching the FOR and WHILE conditions.<br />

Once a DBLOCATE() has been issued you can resume the search from the current record pointer<br />

position with DBCONTINUE().<br />

The WHILE condition and the scope (nNextRecord, nRecord and lRest) apply only to the initial<br />

DBLOCATE() and are not operational for any subsequent DBCONTINUE() call.<br />

368.15.24 DBOLDCREATE()<br />

DBOLDCREATE( cDatabase, cExtendedDatabase,<br />

[cDriver], [lNew], [cAlias] ) ⇒ NIL<br />

cDatabase<br />

cExtendedDatabase<br />

cDriver<br />

lNew<br />

cAlias<br />

is the name <strong>of</strong> the new database file, with an optional drive and<br />

directory, specified as a character string. If specified without<br />

an extension (.dbf) is assumed.<br />

is a ‘.DBF’ file containing the structure information <strong>of</strong> the file<br />

to create.<br />

specifies the replaceable database driver (RDD) to use to process<br />

the current work area. cDriver is the name <strong>of</strong> the RDD<br />

specified as a character expression.<br />

if True the newly created ‘.DBF’ file is opened using the next<br />

available work area making it the current work area (the active<br />

alias).<br />

if lNew is set to True, this is the alias name to use to open the<br />

file.<br />

This function is a old database function (superseded form DBCREATE() ) that creates a database<br />

file from the structure information contained inside a structure file.


270 volume VIII Argomenti avanzati e accessori<br />

368.15.25 DBPACK()<br />

DBPACK() ⇒ NIL<br />

This function eliminates definitively the active alias records previously signed for deletion. It<br />

works only if the active alias is opened in exclusive mode.<br />

368.15.26 DBSDF()<br />

DBSDF( lCopyTo, cFileName, [acFields],<br />

[bForCondition], [bWhileCondition],<br />

[nNextRecords], [nRecord], [lRest] ) ⇒ NIL<br />

lCopyTo<br />

cFileName<br />

acFields<br />

bForCondition<br />

bWhileCondition<br />

nNextRecord<br />

nRercord<br />

lReset<br />

if True the function works copying data to cFileName from<br />

the active alias, if False the function work appending data<br />

from cFileName to the active alias.<br />

the filename containing data to append to the active alias or to<br />

use as the target <strong>of</strong> the data copy from the active alias.<br />

array <strong>of</strong> fieldnames indicating the fields <strong>of</strong> the active alias that<br />

should be taken into consideration (default is all).<br />

a code block containing the FOR condition to respect. The operation<br />

will be made for all records that respect the condition.<br />

a code block containing the WHILE condition to respect. The<br />

first time it becomes False, the operation is terminated.<br />

if used, means that only the first nNextRecords will be appended/copied.<br />

if used, means that that only the record nRecord will be ap-<br />

pended/copied.<br />

if used means that only the remaining records will be taken<br />

into consideration.<br />

This function is used to append data to the active alias using data from the cFileName file or to<br />

copy data into cFileName using the active alias as the source. cFileName is a SDF ASCII file.<br />

368.15.27 DBSORT()<br />

DBSORT( cDatabase, [acFields],<br />

[bForCondition], [bWhileCondition],<br />

[nNextRecords], [nRecord], [lRest] ) ⇒ NIL<br />

cDatabase the ‘.DBF’ file to create.<br />

acFields<br />

the array <strong>of</strong> fields to be used to create the new sorted<br />

cDatabase file.<br />

a code block containing the FOR condition to respect. Only<br />

bForCondition<br />

the records contained inside the active alias that respect the<br />

condition will be taken into consideration.<br />

bWhileCondition<br />

a code block containing the WHILE condition to respect. The<br />

first time that the condition is False, the sort terminates.<br />

nNextRecord<br />

if used, means that only the first nNextRecords inside the active<br />

alias will be used.<br />

nRecord<br />

if used, means that that only the record nRecord will be used.<br />

lRest<br />

if used means that only the remaining records inside the active<br />

alias will be used.


nanoBase 1997 user manual 271<br />

Copy the active alias to a ‘.DBF’ file in sorted order.<br />

368.15.28 DBTOTAL()<br />

DBTOTAL( cDatabase, bKey, [acFields],<br />

[bForCondition], [bWhileCondition],<br />

[nNextRecords], [nRecord], [lRest] ) ⇒ NIL<br />

cDatabase<br />

bKey<br />

acFields<br />

bForCondition<br />

bWhileCondition<br />

nNextRecords<br />

nRecord<br />

lRest<br />

the ‘.DBF’ file to create that will contain the copy <strong>of</strong> summarised<br />

records.<br />

the code block key expression that should correspond to the<br />

key expression <strong>of</strong> the active index <strong>of</strong> the active alias.<br />

the array <strong>of</strong> fields to be used to create the new cDatabase file.<br />

a code block containing the FOR condition to respect. Only<br />

the records contained inside the active alias that respect the<br />

condition will be taken into consideration.<br />

a code block containing the WHILE condition to respect. The<br />

first time that the condition is False, the sort terminates.<br />

if used, means that only the first nNextRecords inside the active<br />

alias will be used.<br />

if used, means that that only the record nRecord will be used.<br />

if used means that only the remaining records inside the active<br />

alias will be used.<br />

This function summarises records by key value to a ‘.DBF’ file. It sequentially process the active<br />

alias scanning the specified scope <strong>of</strong> records. Records with the same key will be summarised<br />

inside the destination ‘.DBF’ file. The value <strong>of</strong> numeric fields <strong>of</strong> records with the same key are<br />

added.<br />

368.15.29 DBUPDATE()<br />

DBUPDATE( cAlias, bKey, [lRandom], [bReplacement] )<br />

cAlias<br />

bKey<br />

lRandom<br />

bReplacement<br />

is the alias containing data to be used to update the active<br />

alias.<br />

is a code block expression using information form the cAlias<br />

to obtain a key to refer to the active alias.<br />

if True, allows record in the cAlias to be in any order. In this<br />

case, the active alias must be indexed with the same key as<br />

bKey.<br />

is the code block that will be executed when records matches:<br />

it should contains the criteria for data update.<br />

This function updates the active alias with data from another .DBF file.<br />

Example:<br />

dbUpdate( "INVOICE", {|| LAST}, .T.,;<br />

{|| FIELD->TOTAL1 := INVOICE->SUM1,;<br />

FIELD->TOTAL2 := INVOICE->SUM2 } )


272 volume VIII Argomenti avanzati e accessori<br />

368.15.30 DBZAP()<br />

DBZAP() ⇒ NIL<br />

This function erases immediately all the records contained inside the active alias.<br />

368.15.31 DISPBOXCOLOR()<br />

DISPBOXCOLOR( [nColorNumber], [cBaseColor] ) ⇒ cColor<br />

nColorNumber<br />

cBaseColor<br />

may be 1 or 2 and are the two color used to create shadowed<br />

borders. 1 is usually used for the left and top line; 2 is used<br />

for the right and bottom line.<br />

is the starting color string. The default is the actual color.<br />

This function return a color string used for DISPBOXSHADOW() the function that create a<br />

shadowed border around a screen window.<br />

368.15.32 DISPBOXSHADOW()<br />

DISPBOXSHADOW( nTop, nLeft, nBottom, nRight,<br />

[cBoxString], [cColor1], [cColor2] ) ⇒ NIL<br />

nTop, nLeft, nBottom and nRight are the screen coordinate where the box is to be displayed.<br />

cBoxString<br />

is the box string containing the character to use to build the<br />

box. Default is a single line box.<br />

cColor1<br />

is the color string to use for the left and top side <strong>of</strong> the box.<br />

cColor2<br />

is the color string to use for the right and bottom side <strong>of</strong> the<br />

box.<br />

This function draws a screen box like DISPBOX() but allowing the variation <strong>of</strong> colors around the<br />

border to simulate a sort <strong>of</strong> shadow.<br />

368.15.33 DIR()<br />

DIR( [cFileSpec], [lDrives], [lDirs], [lFiles],<br />

[lNoDirReturn], [nSortColumn] ) ⇒ cPathname<br />

cFileSpec the filename or Pathname, also with wildcards, to be searched.<br />

lDrives true (‘.T.’) means: include drives letters.<br />

lDirs<br />

true (‘.T.’) means: include directory names.<br />

lFiles true (‘.T.’) means: include file names.<br />

true (‘.T.’) means: do not return the shown directory if [ Esc ]<br />

lNoRirReturn<br />

is used to exit.<br />

the column number to use to sort the list. The columns are:<br />

Name = 1,<br />

Size = 2,<br />

nSortColumn<br />

Date = 3,<br />

Time = 4,<br />

Attribute = 5.<br />

It is not possible to sort for extention.


nanoBase 1997 user manual 273<br />

It is a window function useful to search a file or a directory. The complete pathname <strong>of</strong> the<br />

selected file is returned.<br />

368.15.34 DOC()<br />

DOC( [cTextFileName] ) ⇒ NIL<br />

cTextFileName<br />

can contain the text file to open and edit; if empty, the editing<br />

<strong>of</strong> ‘UNTITLED.TXT’ will start.<br />

It is the nB Text editor useful for small text files (less then 64K) and contains a complete menu<br />

that can be started with[F10].<br />

Attention: doc() should not be used inside macros.<br />

368.15.35 DOTLINE()<br />

DOTLINE() ⇒ NIL<br />

This function is a "dot" command line useful for calculations resolution. The dot-line content<br />

may be passed to the keyboard buffer.<br />

368.15.36 DTEMONTH()<br />

Date <strong>of</strong> month<br />

DTEMONTH( nMonth, cLanguage ) ⇒ cMonth<br />

nMonth the month number.<br />

cLanguage the language name.<br />

This function translates the nMonth number into the month name translated using the<br />

cLanguage language.<br />

368.15.37 DTEWEEK()<br />

Date <strong>of</strong> week<br />

DTEWEEK( nWeek, cLanguage ) ⇒ cWeek<br />

nWeek<br />

cLanguage<br />

is the week number (1 is Sunday, 7 is Saturday) to be translated<br />

into text.<br />

is the language name into which the week must be expressed.<br />

At the moment it works only for Italian, so cLanguage can<br />

only contain "ITALIANO".<br />

This function translates the week number into the week name translated using the cLanguage<br />

language.


274 volume VIII Argomenti avanzati e accessori<br />

368.15.38 EX()<br />

Execute<br />

EX( cFileMacro ) ⇒ nExitCode<br />

Executes the macro file cFileName. The extention must be specified.<br />

cFileMacro may be the name <strong>of</strong> a "compiled" macro or a text macro file.<br />

368.15.39 GET()<br />

GET( @aGetList,<br />

[nTop], [nLeft],<br />

{ |x| iif( pcount() > 0, Var := x, Var ) }<br />

[cGetPicture], [cColorString],<br />

[bPreExpression], [bValid] )<br />

aGetList<br />

is the get list array that will be increased with this get().<br />

nTop and nLeft define the starting position <strong>of</strong> this get object on the screen.<br />

Var<br />

is the variable that is to be edited with this get. Var is in fact<br />

sent to the GET() function using a code block.<br />

cGetPicture<br />

is the get picture to use for Var.<br />

cColorString is the color string to use for the get.<br />

is a code block that will be evaluated before the get object<br />

bPreExpression<br />

will became active. It must result True to obtain that the get<br />

object became active.<br />

is a code block that will be evaluated after the get object is<br />

bValid<br />

edited. It must result True to obtain that the get object may<br />

become inactive.<br />

Create screen editing masks.<br />

368.15.40 GVADD()<br />

Get validation add<br />

GVADD( @cField, cAdd ) ⇒ .T.<br />

cField the field to fill with more data.<br />

cAdd<br />

is the string to be added to the content <strong>of</strong> cField.<br />

This function is to be used inside GETs for pre/post validation, when a the content <strong>of</strong> a field<br />

should be added with more data.<br />

cField is returned with the same length as before to avoid troubles with current and future GETs.


nanoBase 1997 user manual 275<br />

368.15.41 GVDEFAULT()<br />

Get validation default<br />

GVDEFAULT( @cField, cDefault ) ⇒ .T.<br />

@cField<br />

the field to check and if empty correct with cDefault.<br />

cDefault is the default value to be used to replace cField.<br />

This function is to be used inside GETs for pre/post validation, when a field should have a default<br />

value.<br />

cField is returned with the same length as before to avoid troubles with current and future GETs.<br />

368.15.42 GVFILEDIR()<br />

Get validation file directory<br />

GVFILEDIR( @cWildName ) ⇒ .T.<br />

cWildName<br />

is the file name taken from the current get to be used for search<br />

with DIR().<br />

This function is to be used inside GETs for pre validation: the cWildName is a file name with<br />

wild cards that can be searched with the DIR() function after that a specific key is pressed.<br />

cWildName is returned with the same length as before to avoid troubles with current and future<br />

GETs.<br />

368.15.43 GVFILEEXIST()<br />

GVFILEEXIST( @cNameToTest, [cExtention] ) ⇒ lSuccess<br />

@cNameToTest<br />

is the file name taken from the current get to test for existence.<br />

cExtention is the normal extention <strong>of</strong> the file.<br />

This function is to be used inside GETs for post validation: the file name have to exist.<br />

cNameToTest is returned with the same length as before to avoid troubles with current and future<br />

GETs.<br />

368.15.44 GVFILEEXTENTION()<br />

GVFILEEXTENTION( @cName, cExt ) ⇒ .T.<br />

@cName<br />

the file name to be eventually corrected with file extention.<br />

cExt the file extention to use as default.<br />

This function is to use inside GETs for pre/post validation, when the content <strong>of</strong> a field should<br />

contain a file name that should be corrected adding a default extention if not given from the user.


276 volume VIII Argomenti avanzati e accessori<br />

368.15.45 GVSUBST()<br />

GVSUBST( @cField, cSubst ) ⇒ .T.<br />

@cField<br />

cSubst<br />

the field to be replaced with cSubst.<br />

is the string to be used to replace the content <strong>of</strong> cField.<br />

This function is to use inside GETs for pre/post validation, when the content <strong>of</strong> a field should be<br />

replaced with other data.<br />

cField is returned with the same length as before to avoid troubles with current and future GETs.<br />

368.15.46 HTF()<br />

HTF( [nInitialRecord] ) ⇒ NIL<br />

nInitialRecord<br />

is the record number where to start the Help Text File browse.<br />

Default is the actual record pointer.<br />

This function browse a Help Text File that must be already opened and be the active alias.<br />

368.15.47 ISFILE()<br />

ISFILE( cName ) ⇒ lFileExists<br />

cName<br />

is the file name (with or without path) to be checked for existence.<br />

This function returns true (‘.T.’) if the file cName exists. The difference between this function<br />

and the standard FILE() function is that ISFILE() checks for wildcards before. If cName contains<br />

wildcards, the result is false (‘.F.’).<br />

368.15.48 ISWILD()<br />

ISWILD( cName ) ⇒ lIsWild<br />

cName<br />

This function returns true (‘.T.’) if cName contains wildcards.<br />

368.15.49 ISMEMVAR()<br />

ISMEMVAR( cName ) ⇒ lIsMemvar<br />

cName<br />

is the file name (with or without path) to be checked for wildcards<br />

presence.<br />

is the name <strong>of</strong> a possible memvar.<br />

This function returns true (‘.T.’) if the cName is a declared Memvar.


nanoBase 1997 user manual 277<br />

368.15.50 ISCONSOLEON()<br />

ISCONSOLEON() ⇒ lConsoleIsOn<br />

This function returns true (‘.T.’) if the console will show the result <strong>of</strong> QOUT() and QQOUT().<br />

368.15.51 ISPRINTERON()<br />

ISPRINTERON() ⇒ lPrinterIsOn<br />

This function returns true (‘.T.’) if the default printer will report the the result <strong>of</strong> QOUT() and<br />

QQOUT().<br />

The default printer is ‘PRN:’ or ‘LPT1:’. If SET ALTERNATE TO is configured to send outputs<br />

to ‘LPT2:’ or another printer, the function will report false (‘.F.’).<br />

368.15.52 KEYBOARD()<br />

KEYBOARD( [cString] ) ⇒ NIL<br />

This function stuff a string into the keyboard buffer.<br />

368.15.53 LISTWINDOW()<br />

LISTWINDOW( acMenuItem, [cDescription],<br />

[nTop], [nLeft], [nBottom], [nRight],<br />

[cColorTop], [cColorBody] ) ⇒ nPosition<br />

acMenuItem<br />

is the character array containing the list <strong>of</strong> choices.<br />

cDescription is the header to be shown at the top window.<br />

nTop, nLeft, nBottom, nRight are the window coordinates.<br />

cColorTop is the color to use for window header and footer.<br />

cColorBody<br />

is the color to use for the window body that is the space where<br />

the text appears.<br />

This function is an similar to achoice(), but it shows a header and footer, and it saves the screen,<br />

acting like a window.<br />

368.15.54 MEMOWINDOW()<br />

MEMOWINDOW( cVar, [cDescription], [nTop], [nLeft],<br />

[nBottom], [nRight], [cColorTop], [cColorBody],<br />

[lEditMode], [nLineLength], [nTabSize] ) ⇒ cVar<br />

cVar is the character field (variable) to be edited.<br />

cDescription is the header to be shown at the top window.<br />

nTop, nLeft, nBottom, nRight are the window coordinates.<br />

cColorTop is the color to use for window header and footer.<br />

cColorBody<br />

is the color to use for the window body that is the space where<br />

the text appears.<br />

lEditMode<br />

is equivalent to memoedit().


278 volume VIII Argomenti avanzati e accessori<br />

nLineLength is equivalent to memoedit().<br />

nTabSize is equivalent to memoedit().<br />

This function lets you easily edit a long character field (memo) defining automatically a simple<br />

window and providing a simple help.<br />

368.15.55 MEMPUBLIC()<br />

MEMPUBLIC( cMemvarName|acMemvarNames ) ⇒ NIL<br />

cMemvarName<br />

acMemvarNames<br />

Creates a PUBLIC variables or a group <strong>of</strong> variables.<br />

368.15.56 MEMRELEASE()<br />

MEMRELEASE( cMemvarName|acMemvarNames ) ⇒ NIL<br />

is the name <strong>of</strong> the PUBLIC variable to create (max 10 characters).<br />

is an array <strong>of</strong> PUBLIC variable names to create (max 10 characters).<br />

cMemvarName is the name <strong>of</strong> the PUBLIC variable to be released.<br />

acMemvarNames<br />

is an array <strong>of</strong> PUBLIC variable names to be released.<br />

This function releases a previously created PUBLIC variables or a group <strong>of</strong> variables.<br />

368.15.57 MEMRESTORE()<br />

MEMRESTORE( cMemFileName, [lAdditive] ) ⇒ NIL<br />

cMemFileName<br />

lAdditive<br />

Retrieve memory variables form a memory file (.MEM).<br />

368.15.58 MEMSAVE()<br />

MEMSAVE( cMemFileName, [cSkeleton], [lLike] ) ⇒ NIL<br />

cMemFileName<br />

cSkeleton<br />

lLike<br />

the memory file (.MEM) to load from disk.<br />

if True causes memory variables loaded from the memory file<br />

to be added to the existing pool <strong>of</strong> memory variables. If False,<br />

the existing memory variables are automatically released.<br />

the memory file (.MEM) where public variables should be<br />

saved.<br />

the skeleton mask for defining a group <strong>of</strong> variables. Wildcard<br />

characters may be used: _*_ and _?_.<br />

if True, the variables grouped with cSkeleton are saved, else<br />

only the other variables are saved.


nanoBase 1997 user manual 279<br />

Saves memory variables to a memory file (.MEM).<br />

368.15.59 MENUPROMPT()<br />

MENUPROMPT( @aoGet,<br />

[nRow], [nCol],<br />

[cPrompt], [bBlock] ) ⇒ NIL<br />

aoGet<br />

is an array <strong>of</strong> get objects where a new get is added by<br />

MENUPROMPT(). These gets are read only.<br />

nRow and nCol<br />

are the screen coordinates where the menu prompt will appear.<br />

cPrompt is the menu prompt string.<br />

is the code block to execute when the cursor is on the current<br />

bBlock<br />

menu prompt. It is usually a code block that shows a message<br />

somewhere on the screen.<br />

This function should substitute the @...PROMPT command and handle the mouse.<br />

368.15.60 MENUTO()<br />

MENUTO( aoGet, nPos ) ⇒ nChoice<br />

aoGet<br />

nPos<br />

array <strong>of</strong> get objects.<br />

starting position to be edited.<br />

Like MENU TO. It returns the selected menu item created with MENUPROMPT(). It supports<br />

the mouse.<br />

368.15.61 MESSAGELINE()<br />

MESSAGELINE( [cMessage], [cColor], [nPosTop], [nPosLeft] )<br />

⇒ NIL<br />

aMessage the message to be displayed.<br />

cColor<br />

the color string.<br />

the starting position where the string message would appear<br />

nPosTop and nPosLeft<br />

on the screen. Default values are respectively ROW() and<br />

COL().<br />

MESSAGELINE() is a function that display a message on the screen on the selected position. If<br />

cMessage is NIL, the message is eliminated from screen restoring the previous screen content.


280 volume VIII Argomenti avanzati e accessori<br />

368.15.62 MOUSESCRSAVE()<br />

MOUSESCRSAVE( [nTop], [nLeft], [nBottom], [nRight] )<br />

⇒ cSavedScreen<br />

nTop, nLeft, nBottom and nRight are the screen coordinates that will be to save the screen.<br />

This function works line SAVESCREEN() but it hide the mouse cursor before a screen save is<br />

made.<br />

368.15.63 MOUSESCRRESTORE()<br />

MOUSESCRRESTORE( [nTop], [nLeft], [nBottom], [nRight],<br />

[cScreen] ) ⇒ cSavedScreen<br />

nTop, nLeft, nBottom and nRight<br />

cScreen<br />

are the screen coordinates where the saved screen will be restored.<br />

is the previously saved screen to restore.<br />

This function works line RESTSCREEN() but it hide the mouse cursor before a screen restore is<br />

made.<br />

368.15.64 PICCHRMAX()<br />

PICCHRMAX( [nCol], [nMaxCol] ) ⇒ cPictureString<br />

nCol<br />

nMaxCol<br />

is the starting position on the screen for the get field.<br />

is the end position on the screen <strong>of</strong> the get field.<br />

This function is useful when a character field is to be used on a get object. The generated picture<br />

will be the <strong>of</strong> the maximum possible extention, eventually with scroll.<br />

368.15.65 QUIT()<br />

QUIT() ⇒ NIL<br />

Terminates program execution.<br />

368.15.66 READ()<br />

READ( aoGet, [nPos], [aButtons], [lReadOnly] )<br />

⇒ lUpdated<br />

aoGet<br />

nPos<br />

aButtons<br />

lReadOnly<br />

is the array <strong>of</strong> get objects.<br />

is the starting position.<br />

is the array <strong>of</strong> buttons.<br />

if True, get fields cannot be modified; the default value is<br />

False.


nanoBase 1997 user manual 281<br />

This function is made to substitute the READMODAL() allowing the use <strong>of</strong> the mouse. The array<br />

aButtons is made with the help <strong>of</strong> the function BUTTON().<br />

368.15.67 RF()<br />

RF( cFRMName,<br />

[bForCondition], [bWhileCondition],<br />

[nNext], [nRecord], [lRest], [lPlain],<br />

[cbHeading], [lBeforeEject], [lSummary],<br />

[lDate], [acExtra] ) ⇒ NIL<br />

cFRMName<br />

the form (.FRM) file to use to print the active alias.<br />

bForCondition code block for the FOR condition.<br />

bWhileCondition code block for the WHILE condition.<br />

nNext see REPORT FORM.<br />

nRecord see REPORT FORM<br />

lRest see REPORT FORM<br />

lPlain<br />

if true (‘.T.’), force the print in a simple way.<br />

additional header in character or code block form. If a code<br />

cbHeading<br />

block is sent, the final result must be a character string.<br />

lBeforeEject if true (‘.T.’), force a form feed before the print.<br />

lSummary if true (‘.T.’), force a summary print only.<br />

lDate<br />

if false (‘.F.’), force the print without date at the top <strong>of</strong> page.<br />

a character array that may be used for translating standard<br />

printed report form words and to add vertical and horizontal<br />

separations. The default value <strong>of</strong> acExtra is:<br />

acExtra[1] "Page No."<br />

acExtra<br />

acExtra[2] "** Subtotal **"<br />

acExtra[3] "* Subsubtotal *"<br />

acExtra[4] "*** Total ***"<br />

acExtra[5] " " vertical column separation<br />

axExtra[6] "" horizontal separation: no separation.<br />

This function does the same work <strong>of</strong> REPORT FORM or __ReportForm or dbReportForm, but it<br />

prints where qout() and qqout() print.<br />

368.15.68 RPT()<br />

RPT( cText ) ⇒ NIL<br />

This function prints the text contained into cText using print commands. This function accepts<br />

other parameters here not described, as they are not to be used for macro purpose. The printing<br />

is made using QOUT() and QQOUT(), this way it is sensible to the "alternate" file definition.<br />

368.15.69 RPTMANY()<br />

RPTMANY( cText, [bWhileCondition], [bForCondition] )<br />

⇒ NIL<br />

cText<br />

bWhileCondition<br />

bForCondition<br />

is the text to be printed.<br />

is a code block for a WHILE condition to respect for the<br />

records to print.<br />

is a code block for a FOR condition to respect for the records<br />

to print.


282 volume VIII Argomenti avanzati e accessori<br />

This function prints the text contained into cText many times: one for every record contained<br />

into the active alias.<br />

368.15.70 RPTTRANSLATE()<br />

RPTTRANSLATE( cText ) ⇒ cTranslatedText<br />

This function translates once cText replacing variables with memvars or Fields.<br />

368.15.71 RUN()<br />

RUN( cCommand ) ⇒ NIL<br />

This function start execution <strong>of</strong> cCommand in a DOS session. It works only if there is enough<br />

available memory.<br />

368.15.72 SAY()<br />

SAY( nTop, nLeft, Expr,<br />

[cSayPicture], [cColorString] ) ⇒ NIL<br />

nTop and nLeft<br />

define the starting position on the screen where the Expr<br />

should be displayed.<br />

nLeft is an expression that will be solved and displayed.<br />

cSayPicture is the picture to use to display Expr.<br />

cColorString is the color string to use.<br />

This function displays the result <strong>of</strong> Expr on the screen on the desired position.<br />

368.15.73 SETCOLORSTANDARD()<br />

SETCOLORSTANDARD( [nColor], [cColor|acColor] )<br />

nColor<br />

cColor<br />

acColor<br />

⇒ cPreviousColor|acPreviousColor<br />

is the color number to take into consideration:<br />

0 All colors<br />

1 Base<br />

2 Menu<br />

3 Head<br />

4 Body (Say - Get)<br />

5 Button (Mouse buttons)<br />

6 Message<br />

7 Alert<br />

the color string to be associated with nColor.<br />

it the color array<br />

This function is a way to handle colors inside the application. The functions that display something<br />

use a default color depending on what they does. These colors may be changed with SET-<br />

COLORSTANDARD(), all together or only one.


nanoBase 1997 user manual 283<br />

368.15.74 SETFUNCTION()<br />

SETFUNCTION( nFunctionKey, cString ) ⇒ NIL<br />

nFunctionKey<br />

the number <strong>of</strong> the function key ( 1=F1, 12=F12) to be assigned.<br />

cString the character string.<br />

This function assigns a character string to a function key (obsolete).<br />

368.15.75 SETMOUSE()<br />

SETMOUSE( [lShow] )⇒ lPrevious<br />

lShow<br />

True shows the mouse cursor, False hide the mouse cursor,<br />

NIL reports only the status.<br />

This function is made to show, hide or report only the mouse cursor status.<br />

368.15.76 SETOUTPUT()<br />

SETOUTPUT( [cPeriperal|aPeripheral] )<br />

⇒ aPrevious_Output_Peripherals<br />

cPeripheral is the new output peripheral for qout() and qqout() functions.<br />

aPeripheral<br />

are the new output peripherals configurations for qout() and<br />

qqout() functions.<br />

nB is organised in the way to have only one output peripheral at the time. This function help to<br />

make order inside SET CONSOLE, SET PRINTER and SET ALTERNATE.<br />

If cPeripheral contains:<br />

"CON"<br />

"PRN"<br />

SET CONSOLE is set to ON,<br />

SET PRINTER is set to OFF,<br />

SET ALTERNATE is set to OFF;<br />

SET CONSOLE is set to OFF,<br />

SET PRINTER is set to ON,<br />

SET ALTERNATE is set to OFF;<br />

"LPT1"<br />

same as "PRN";<br />

otherwise<br />

SET CONSOLE is set to OFF,<br />

SET PRINTER is set to OFF,


284 volume VIII Argomenti avanzati e accessori<br />

SET ALTERNATE is set to ON,<br />

SET ALTERNATE TO is set to cPeripheral.<br />

aPeripheral is organised this way:<br />

aPeripheral[1] = _SET_CONSOLE<br />

aPeripheral[2] = _SET_PRINTER<br />

aPeripheral[3] = _SET_ALTERNATE<br />

aPeripheral[4] = _SET_ALTFILE<br />

aPeripheral[5] = _SET_EXTRA<br />

aPeripheral[6] = _SET_EXTRAFILE<br />

This function is necessary because SET ALTERNATE alone is not enough to print on the screen<br />

when the peripheral name is "CON" or to print on the printer when the peripheral name is "PRN"<br />

or "LPT1". In fact, in the first case, ROW() and COL() will not be updated, in the second case,<br />

PROW() and PCOL() will not be updated.<br />

This function returns an array organised in the same way as aPeripheral is, that shows the active<br />

output configuration.<br />

368.15.77 SETRPTEJECT()<br />

SETRPTEJECT( [lbEject] ) ⇒ lPreviousEjectMode<br />

This function is used to set the eject mode after every page print for RPT(). If single sheet paper is<br />

used, then SETRPTEJECT(.T.) must be set; for continuous paper, SETRPTEJECT(.F.) is correct.<br />

The default value is .F..<br />

lbEject<br />

368.15.78 SETRPTLINES()<br />

SETRPTLINES() ⇒ nRemainingLines<br />

logical or code block, is the eject mode to set. Default is no<br />

change, the starting value is ‘.F.’<br />

This function is used to report the number <strong>of</strong> lines available before the completion <strong>of</strong> the page<br />

print for RPT().<br />

368.15.79 SETVERB()<br />

Set verbose<br />

SETVERB( cSpecifier, [xNewSetting], [lOpenMode] )<br />

⇒ xPreviousValueSet<br />

cSpecifier a word that defines the kind <strong>of</strong> set is going to be considered.<br />

xNewSetting is the new value to set up.<br />

lOpenMode used only for some kind <strong>of</strong> set.<br />

This function is analogue to SET() but it uses a character string (with cSpecifier) and not a


nanoBase 1997 user manual 285<br />

number to select the set. This is made to make easier the work with macros.<br />

cSpecifier may contain:<br />

"EXACT"<br />

"FIXED"<br />

"DECIMALS"<br />

"DATEFORMAT"<br />

"EPOCH"<br />

"PATH"<br />

"DEFAULT"<br />

"EXCLUSIVE"<br />

"SOFTSEEK"<br />

"UNIQUE"<br />

"DELETED"<br />

"CANCEL"<br />

"TYPEAHEAD"<br />

"COLOR"<br />

"CURSOR"<br />

"CONSOLE"<br />

"ALTERNATE"<br />

"ALTFILE"<br />

"DEVICE"<br />

"EXTRA"<br />

"EXTRAFILE"<br />

"PRINTER"<br />

"PRINTFILE"<br />

"MARGIN"<br />

"BELL"<br />

"CONFIRM"<br />

"ESCAPE"<br />

"INSERT"<br />

"EXIT"<br />

"INTENSITY"<br />

"SCOREBOARD"<br />

"DELIMITERS"<br />

"DELIMCHARS"<br />

"WRAP"<br />

"MESSAGE"<br />

"MCENTER"<br />

368.15.80 SETVERB("EXACT") (obsolete)<br />

SETVERB( "EXACT", [lExact] ) ⇒ lPrevious<br />

If lExact is True, it forces exact comparison <strong>of</strong> character strings, including length. If it is False,<br />

character strings are compared until the left string length is exhausted; that is that "" (the null<br />

string) is equal to any other string.<br />

Please note that the == operator is a comparison operator for exact match and using it,<br />

SETVERB("EXACT", ‘.F.’) will not work.


286 volume VIII Argomenti avanzati e accessori<br />

The starting value is True; the recommended value is True.<br />

368.15.81 SETVERB("FIXED")<br />

SETVERB( "FIXED", [lFixed] ) ⇒ lPrevious<br />

If lFixed contains True, numeric values are displayed ever with a fixed number <strong>of</strong> decimal digits,<br />

depending on the value set by SETVERB("DECIMALS").<br />

The starting value is False.<br />

The recommended value is False: if you have to display a fixed number <strong>of</strong> decimal digits it is<br />

better to define a good display picture.<br />

368.15.82 SETVERB("DECIMALS")<br />

SETVERB( "DECIMALS", [nDecimals] ) ⇒ nPrevious<br />

nDecimals is the number <strong>of</strong> digits to display after the decimal position. This set is enabled <strong>of</strong><br />

disabled with SETVERB("FIXED").<br />

The starting value is 8.<br />

368.15.83 SETVERB("DATEFORMAT")<br />

SETVERB( "DATEFORMAT", [cDateFormat] ) ⇒ cPrevious<br />

cDateFormat is a character expression that specifies the date format.<br />

The starting value is "dd/mm/yyyy".<br />

Some date format examples:<br />

AMERICAN<br />

ANSI<br />

BRITISH<br />

FRENCH<br />

GERMAN<br />

ITALIAN<br />

JAPAN<br />

USA<br />

368.15.84 SETVERB("EPOCH")<br />

SETVERB( "EPOCH", [nYear] ) ⇒ nPrevious<br />

"mm/dd/yyyy"<br />

"yyyy.mm.dd"<br />

"dd/mm/yyyy"<br />

"dd/mm/yyyy"<br />

"dd.mm.yyyy"<br />

"dd-mm-yyyy"<br />

"yyyy/mm/dd"<br />

"mm-dd-yyyy"<br />

nYear specifies the base year <strong>of</strong> 100-year period in which all dates containing only two year<br />

digits are assumed to fall.<br />

The starting value is 1900.


nanoBase 1997 user manual 287<br />

368.15.85 SETVERB("PATH")<br />

SETVERB( "PATH", [cPath] ) ⇒ cPrevious<br />

cPath identifies the paths that nB uses when searching for a file not found in the current directory.<br />

The list <strong>of</strong> paths can be separated by commas or semicolons.<br />

The starting value is "".<br />

368.15.86 SETVERB("DEFAULT")<br />

SETVERB( "DEFAULT", [cPath] ) ⇒ cPrevious<br />

cPath identifies the default disk drive and directory.<br />

The starting value is "".<br />

368.15.87 SETVERB("EXCLUSIVE")<br />

SETVERB( "EXCLUSIVE", [lExclusive] ) ⇒ lPrevious<br />

If lPath is True, the default database (.DBF) file open is made in exclusive mode; in the other<br />

case, in shared mode.<br />

The starting value is True.<br />

368.15.88 SETVERB("SOFTSEEK")<br />

SETVERB( "SOFTSEEK", [lS<strong>of</strong>tSeek] ) ⇒ lPrevious<br />

If lS<strong>of</strong>tSeek is True, if a DBSEEK() index search fails, the record pointer is moved to the next<br />

record with a higher key. If it is False, in case <strong>of</strong> a DBSEEK() index search failure, the record<br />

pointer is moved at EOF().<br />

The starting value is False.<br />

368.15.89 SETVERB("UNIQUE") (obsolete)<br />

SETVERB( "UNIQUE", [lUnique] ) ⇒ lPrevious<br />

If lUnique is True, during creation or update <strong>of</strong> ‘.DBF’ indexes, if two or more records are found<br />

with the same key, only the first record will be included inside the index.<br />

If lUnique is False, duplicated record keys are allowed.<br />

The starting value is False.


288 volume VIII Argomenti avanzati e accessori<br />

368.15.90 SETVERB("DELETED")<br />

SETVERB( "DELETED", [lDeleted] ) ⇒ lPrevious<br />

If lDeleted is True, record signed for deletion are not filtered, that is, these are still normally<br />

visible as they were not deleted. In the other case, they hare (in most cases) hidden to the user.<br />

The starting value is False.<br />

368.15.91 SETVERB("CANCEL")<br />

SETVERB( "CANCEL", [lCancel] ) ⇒ lPrevious<br />

If lCancel is True, enables [ Alt+c ] and [ Ctrl+Break ] as termination keys. In the other case, not.<br />

The starting value is True.<br />

368.15.92 SETVERB("TYPEAHEAD")<br />

SETVERB( "TYPEAHEAD", [nTypeAhead] ) ⇒ nPrevious<br />

nTypeAhead is the number <strong>of</strong> keystrokes the keyboard buffer can hold from a minimum <strong>of</strong> zero<br />

to a maximum <strong>of</strong> 4096.<br />

The starting value is 15.<br />

368.15.93 SETVERB("COLOR")<br />

SETVERB( "COLOR", [cColorString] ) ⇒ cPrevious<br />

nColorString defines the normal screen colors. There are five couple <strong>of</strong> colors, but only three are<br />

really operative:<br />

standard<br />

This is the standard color used for screen output.<br />

enhanced<br />

This is the color used for highlighted screen output.<br />

border<br />

Normally unused.<br />

background Normally unused.<br />

unselected This is the color used for GET fields without focus.<br />

The default color string is "BG+/B,N/W,N/N,N/N,W/N" that is:<br />

standard<br />

bright Cyan on Blue<br />

enhanced Black on White<br />

border Black on Black<br />

background Black on Black<br />

unselected White on Black<br />

The following table explains the use <strong>of</strong> letters inside the color string. Note that the plus sign (+)<br />

means high intensity, the star (*) means blink and that + and * can be allowed only to the first<br />

letter inside a couple.<br />

Color Letter Monochrome<br />

Black N, Space Black


nanoBase 1997 user manual 289<br />

Color Letter Monochrome<br />

Blue B Underline<br />

Green G White<br />

Cyan BG White<br />

Red R White<br />

Magenta RB White<br />

Brown GR White<br />

White W White<br />

Gray N+ Black<br />

Bright Blue B+ Bright Underline<br />

Bright Green G+ Bright White<br />

Bright Cyan BG+ Bright White<br />

Bright Red R+ Bright White<br />

Bright Magenta RB+ Bright White<br />

Bright Brown GR+ Bright White<br />

Bright White W+ Bright White<br />

Black U Underline<br />

Inverse Video I Inverse Video<br />

Blank X Blank<br />

368.15.94 SETVERB("CURSOR")<br />

SETVERB( "CURSOR", [lCursor] ) ⇒ lPrevious<br />

If lCursor is True, the cursor is showed, else it is hidden.<br />

The starting value is True.<br />

368.15.95 SETVERB("CONSOLE")<br />

SETVERB( "CONSOLE", [lConsole] ) ⇒ lPrevious<br />

If lConsole is True, the output <strong>of</strong> console commands is displayed on the screen, else it is not.<br />

The starting value is True.<br />

368.15.96 SETVERB("ALTERNATE")<br />

SETVERB( "ALTERNATE", [lAlternate] ) ⇒ lPrevious<br />

If lAlternate is True, the output <strong>of</strong> console commands is send also to a standard ASCII text file.<br />

The starting value is False.<br />

368.15.97 SETVERB("ALTFILE")<br />

SETVERB( "ALTFILE", [cAlternateFilename], [lAdditive] )<br />

⇒ cPrevious<br />

If SETVERB("ALTERNATE") is True, the output <strong>of</strong> the console is send also to<br />

cAlternateFilename, a standard ASCII file.


290 volume VIII Argomenti avanzati e accessori<br />

If lAdditive is True, the output is appended to the ASCII file if it already exists, else it is erased<br />

first.<br />

368.15.98 SETVERB("DEVICE")<br />

SETVERB( "DEVICE", [cDevice] ) ⇒ cPrevious<br />

cDevice is the name <strong>of</strong> the device where SAY() will display its output.<br />

The starting value is "SCREEN", the alternative is "PRINTER".<br />

The recommended value is "SCREEN".<br />

368.15.99 SETVERB("EXTRA")<br />

SETVERB( "EXTRA", [lExtra] ) ⇒ lPrevious<br />

If lExtra is True, the output <strong>of</strong> console commands is send also to a standard ASCII text file.<br />

The starting value is False.<br />

368.15.100 SETVERB("EXTRAFILE")<br />

SETVERB( "EXTRAFILE", [cExtraFilename], [lAdditive] )<br />

⇒ cPrevious<br />

If SETVERB("EXTRA") is True, the output <strong>of</strong> the console is send also to cExtraFilename, a<br />

standard ASCII file.<br />

If lAdditive is True, the output is appended to the ASCII file if it already exists, else it is erased<br />

first.<br />

368.15.101 SETVERB("PRINTER")<br />

SETVERB( "PRINTER", [lPrinter] ) ⇒ lPrevious<br />

If lPrinter is True, the output <strong>of</strong> console commands is also printed, else it is not.<br />

The starting value is False.<br />

368.15.102 SETVERB("PRINTFILE")<br />

SETVERB( "PRINTFILE", [cPrintFileName] ) ⇒ cPrevious<br />

cPrintFileName is the name <strong>of</strong> the printer peripheral name.<br />

The starting value is "" (null string).


nanoBase 1997 user manual 291<br />

368.15.103 SETVERB("MARGIN")<br />

SETVERB( "MARGIN", [nPageOffset] ) ⇒ nPrevious<br />

nPageOffset is the positive number <strong>of</strong> column to be used as a left margin for all printer output.<br />

The starting value is 0.<br />

368.15.104 SETVERB("BELL")<br />

SETVERB( "BELL", [lBell] ) ⇒ lPrevious<br />

If lBell is True, the sound <strong>of</strong> the bell is used to get the attention <strong>of</strong> the user when some wrong<br />

actions are made.<br />

The starting value is False.<br />

368.15.105 SETVERB("CONFIRM")<br />

SETVERB( "CONFIRM", [lConfirm] ) ⇒ lPrevious<br />

If lConfirm is False, the GET is simply terminated typing over the end <strong>of</strong> the get field; in the<br />

other case (True), the GET is terminated only pressing an "exit key". The starting value is True.<br />

368.15.106 SETVERB("ESCAPE")<br />

SETVERB( "ESCAPE", [lEscape] ) ⇒ lPrevious<br />

If lEscape is True, the [ Esc ] key is enabled to be a READ exit key, in the other case not.<br />

The starting value is True.<br />

The recommended value is True.<br />

368.15.107 SETVERB("INSERT")<br />

SETVERB( "INSERT", [lInsert] ) ⇒ lPrevious<br />

If lInsert is True, the data editing is in INSERT mode, in the other case, it is in OVERWRITE<br />

mode.<br />

The starting value is True.<br />

368.15.108 SETVERB("EXIT")<br />

SETVERB( "EXIT", [lExit] ) ⇒ lPrevious<br />

If lExit is True,[Up] and[Down] key may be used as exit key when the cursor is (respectively)<br />

on the first or on the last GET field. In the other case not.<br />

The starting value is False.<br />

The recommended value is False.


292 volume VIII Argomenti avanzati e accessori<br />

368.15.109 SETVERB("INTENSITY")<br />

SETVERB( "INTENSITY", [lIntensity] ) ⇒ lPrevious<br />

If lIntensitiy is True, the display <strong>of</strong> standard and enhanced display colors are enabled. In the<br />

other case, only standard colors are enabled.<br />

The starting value is True.<br />

The recommended value is True.<br />

368.15.110 SETVERB("SCOREBOARD")<br />

SETVERB( "SCOREBOARD", [lScoreboard] ) ⇒ lPrevious<br />

If lScoreboard is True, the display <strong>of</strong> messages from READ() and MEMOREAD() is allowed;<br />

in the order case not.<br />

The starting value is False.<br />

The recommended value is False: nB do not support scoreboard.<br />

368.15.111 SETVERB("DELIMITERS")<br />

SETVERB( "DELIMITERS", [lDelimiters] ) ⇒ lPrevious<br />

If lDelimiters is True, GET variables appear on the screen delimited with the delimiter symbols.<br />

In the other case, GET variables are not delimited this way, but only with the use <strong>of</strong> different<br />

colors.<br />

The starting value is False.<br />

The recommended value is False: the use <strong>of</strong> delimiters creates one more trouble when designing<br />

a screen mask.<br />

368.15.112 SETVERB("DELIMCHARS")<br />

SETVERB( "DELIMCHARS", [cDelimterCharacters] ) ⇒ cPrevious<br />

cDelimterCharacters are the delimiter characters used to delimit a GET field when<br />

SETVERB("DELIMITERS") is True.<br />

The starting value is "::".<br />

368.15.113 SETVERB("WRAP")<br />

SETVERB( "WRAP", [lWrap] ) ⇒ lPrevious<br />

If lWrap is True, the wrapping <strong>of</strong> the highlight in MENUs should be active, but this option is<br />

actually not active and all works as it is False.<br />

The starting value is False.


nanoBase 1997 user manual 293<br />

368.15.114 SETVERB("MESSAGE")<br />

SETVERB( "MESSAGE", [nMessageRow] ) ⇒ nPrevious<br />

nMessageRow is the row number where the @..PROMPT message line should appear on the<br />

screen. This option is not supported.<br />

The starting value is 0.<br />

368.15.115 SETVERB("MCENTER")<br />

SETVERB( "MCENTER", [lMessageCenter] ) ⇒ lPrevious<br />

If lMessageCenter is True, the @..PROMPT message line should appear centered on the screen.<br />

This option is not supported.<br />

The starting value is False.<br />

368.15.116 STRADDEXTENTION()<br />

STRADDEXTENTION( cName, cExt ) ⇒ cCompleteName<br />

cName<br />

the file name (with or without path) that is probably without<br />

extention.<br />

cExt the extention that must be added to cName if it has not one.<br />

This function check cName for the presence <strong>of</strong> an extention. It it has not one, cExt will be added.<br />

368.15.117 STRCUTEXTENTION()<br />

STRCUTEXTENTION( cName ) ⇒ cName<br />

cName<br />

the file name (with or without path) that is probably with extention.<br />

This function check cName for the presence <strong>of</strong> an extention. It it has one, the extention is removed.<br />

368.15.118 STRDRIVE()<br />

STRDRIVE( cName ) ⇒ cDrive<br />

cName<br />

the file name (with or without path) that contains the drive<br />

letter.<br />

This function tries to extract the drive letter information from cName.


294 volume VIII Argomenti avanzati e accessori<br />

368.15.119 STREXTENTION()<br />

STREXTENTION( cName ) ⇒ cExtention<br />

cName<br />

the file name (with or without path) that contains an extention.<br />

This function tries to extract the extention information from cName.<br />

368.15.120 STRFILE()<br />

STRFILE( cName ) ⇒ cFileName<br />

cName<br />

the file name with or without path.<br />

This function tries to extract the file name without path from cName.<br />

368.15.121 STRFILEFIND()<br />

STRFILEFIND( cName, cPath ) ⇒ cFileName<br />

cName<br />

cPath<br />

the file name or pathname containing the file name to search<br />

inside the cPath list.<br />

a list <strong>of</strong> paths separated with semicolon (just like Dos does),<br />

where cFile should be searched.<br />

If your file is to be found on different possible positions, this function search the first place where<br />

the file is found and returns a valid pathname to that file.<br />

368.15.122 STRGETLEN()<br />

STRGETLEN( xExpr, cPicture ) ⇒ nFieldLength<br />

xExpr a generic expression.<br />

cPicture<br />

the picture string.<br />

This function returns the length <strong>of</strong> field when using xExpr with cPicture.<br />

368.15.123 STRLISTASARRAY()<br />

STRLISTASARRAY( cList, [cDelimiter] ) ⇒ aList<br />

cList<br />

cDelimiter<br />

This function transform a character string list into an array.<br />

a character string containing a list separated with cDelimiter.<br />

the delimiter used to separate the elements contained inside<br />

the list.


nanoBase 1997 user manual 295<br />

368.15.124 STROCCURS()<br />

STROCCURS( cSearch, cTarget ) ⇒ nOccurrence<br />

cSearch<br />

the search string to find inside cTarget.<br />

cTarget the string to be searched for the presence <strong>of</strong> cSearch.<br />

This function returns the number <strong>of</strong> occurrence that cSearch is contained inside cTarget.<br />

368.15.125 STRPARENT()<br />

STRPARENT( cName ) ⇒ cParentPath<br />

cName<br />

the pathname.<br />

This function tries to return a parent path from cName.<br />

368.15.126 STRPATH()<br />

STRPATH( cName ) ⇒ cPath<br />

cName<br />

the pathname.<br />

This function tries to extract the path from cName.<br />

368.15.127 STRTEMPPATH()<br />

STRTEMPPATH() ⇒ cTempPath<br />

This function returns a temporary path searching for possible definitions inside the environmental<br />

variables.<br />

368.15.128 STRXTOSTRING()<br />

STRXTOSTRING( xVar, [cType] ) ⇒ cTrasformed_to_string<br />

xVar<br />

is the data <strong>of</strong> any type to be converted into string.<br />

cType is the type <strong>of</strong> the data contained inside xVar.<br />

This function returns xVar transformed into a character string.


296 volume VIII Argomenti avanzati e accessori<br />

368.15.129 TB()<br />

TB( [nTop], [nLeft], [nBottom], [nRight],<br />

[acCol], [acColSayPic],<br />

[acColTopSep], [acColBodySep], [acColBotSep],<br />

[acColHead], [acColFoot],<br />

[alColCalc],<br />

[abColValid],<br />

[abColMsg],<br />

[cColor], [abColColors],<br />

[nFreeze],<br />

[lModify],<br />

[lAppend],<br />

[lDelete],<br />

[lButtons | aButtons] ) ⇒ NIL<br />

nTop, nLeft, nBottom, nRight defines the screen area where browse have to take place.<br />

acCol<br />

is the columns array to be included into the browse.<br />

acColSayPic is the picture array.<br />

acColTopSep is the top separation array: default is chr(194)+chr(196).<br />

acColBodySep is the body separation array: default is chr(179).<br />

acColBotSep is the bottom separation array: default is chr(193)+chr(196).<br />

acColHead<br />

is the header array for every column.<br />

acColFoot<br />

is the footer array for every column.<br />

alColCalc<br />

is the array that identify the calculated column (not editable).<br />

True (‘.T.’) means calculated.<br />

abColValid<br />

is the validation array that specify when a field is properly<br />

filled. The condition must be specified in code block format.<br />

is the message array that permits to show information at the<br />

abColMsg<br />

bottom <strong>of</strong> browse area. The array must be composed with<br />

code blocks which result with a character string.<br />

cColor<br />

is the color string: it may be longer than the usual 5 elements.<br />

is the color code block array. The code block receive as pa-<br />

abColColors<br />

rameter the value contained inside the field and must return<br />

an array containing two numbers: they correspond to the two<br />

color couple from cColor.<br />

nFreeze<br />

indicates the number <strong>of</strong> columns to be left frozen on the left<br />

side.<br />

lModify indicates whether the browse can modify data.<br />

lDelete indicates whether the browse can delete and recall records.<br />

lButtons<br />

if True, default buttons are displayed.<br />

aButtons<br />

array <strong>of</strong> buttons.<br />

aButtons[n][1] N the nth button row position;<br />

aButtons[n][2] N the nth button column position;<br />

aButtons[n][3] C the nth button text;<br />

aButtons[n][4] B the nth button code block.<br />

This function, called without parameters, starts the browse <strong>of</strong> the active alias, and if relations are<br />

established, the browse includes also related data.<br />

Please note that due to an unresolved problem, the field names contained inside acCol should<br />

better contain also the alias (ALIAS->FIELD_NAME). See also the examples.


nanoBase 1997 user manual 297<br />

368.15.130 TEXT()<br />

TEXT( cText ) ⇒ NIL<br />

Shows the text contained into cText.<br />

368.15.131 TGLINSERT()<br />

TGLINSERT() ⇒ NIL<br />

Toggle the global insert mode and the cursor shape.<br />

368.15.132 TIMEX2N()<br />

TIMEX2N( [nHH], [nMM], [nSS] ) ⇒ nTime<br />

nHH is the number <strong>of</strong> hours.<br />

nMM is the number <strong>of</strong> minutes.<br />

nSS is the number <strong>of</strong> seconds.<br />

This function calculate the "time number" that is a number representing days and/or portion <strong>of</strong> a<br />

day: 1 is 1 day or 24 hours, 0.5 is 12 hours, and so on.<br />

368.15.133 TIMEN2H()<br />

TIMEN2H( nTime ) ⇒ nHours<br />

nTime<br />

is the "time number" that is a number representing days and/or<br />

portion <strong>of</strong> a day: 1 is 1 day or 24 hours, 0.5 is 12 hours, and<br />

so on.<br />

This function returns the integer number <strong>of</strong> hours contained inside nTime.<br />

368.15.134 TIMEN2M()<br />

TIMEN2M( nTime ) ⇒ nMinutes<br />

nTime<br />

is the "time number" that is a number representing days and/or<br />

portion <strong>of</strong> a day: 1 is 1 day or 24 hours, 0.5 is 12 hours, and<br />

so on.<br />

This function returns the integer number <strong>of</strong> minutes contained inside nTime after subtracting the<br />

hours.


298 volume VIII Argomenti avanzati e accessori<br />

368.15.135 TIMEN2S()<br />

TIMEN2S( nTime ) ⇒ nSeconds<br />

nTime<br />

is the "time number" that is a number representing days and/or<br />

portion <strong>of</strong> a day: 1 is 1 day or 24 hours, 0.5 is 12 hours, and<br />

so on.<br />

This function returns the number <strong>of</strong> seconds (with eventual decimals) contained inside nTime<br />

after subtracting the hours and the minutes.<br />

368.15.136 TRUESETKEY()<br />

TRUESETLEY( nInkeyCode, bAction ) ⇒ .T.<br />

This function is equivalent to SETKEY() but it returns always ‘.T.’<br />

368.15.137 WAITFILEEVAL()<br />

WAITFILEEVAL( lClose ) ⇒ .T.<br />

Shows a wait bar calling WAITPROGRESS() for operation on records <strong>of</strong> a database.<br />

If there is no index active, it is equivalent to WAITPROGRES(RECNO()/LASTREC()).<br />

if an index is active, this cannot work, so an increment for each call is made: WAITPRO-<br />

GRES((nIncrement++)/LASTREC()).<br />

This function must be closed calling it with the lClose parameter to true (‘.T.’). This way,<br />

internal counters are closed and WAITPROGRESS() is closed too.<br />

368.15.138 WAITFOR()<br />

WAITFOR( [cMessage] ) ⇒ NIL<br />

Shows cMessage until it is called again. The wait window is closed when called without parameter<br />

or with NIL.<br />

368.15.139 WAITPROGRESS()<br />

WAITPROGRESS( [nPercent] ) ⇒ .T.<br />

Shows a wait bar on the screen top depending on the value contained into nPercent. nPercent<br />

starts form 0 and ends to 1 (100%). If a value <strong>of</strong> one or more, or NIL is passed, the wait window<br />

is closed.


nanoBase 1997 user manual 299<br />

368.16 Normal command substitution<br />

Clipper works only with functions and commands that are converted into function using the<br />

‘STD.CH’. Here are described some command replacement that can be used also with nB macros.<br />

?<br />

@BOX<br />

@TO<br />

@GET<br />

@SAY<br />

? [exp_list]<br />

qout([exp_list])<br />

?? [exp_list]<br />

qqout([exp_list])<br />

@ nTop, nLeft, nBottom, nRight BOX cnBoxString [COLOR cColorString]<br />

dispbox(nTop, nLeft, nBottom, nRight, [cnBoxString], [cColorString])<br />

@ nTop, nLeft TO nBottom, nRight DOUBLE [COLOR cColorString]<br />

dispbox(nTop, nLeft, nBottom, nRight, 2 [,cColorString])<br />

@ nTop, nLeft TO nBottom, nRight [COLOR cColorString]<br />

dispbox(nTop, nLeft, nBottom, nRight, 1 [,cColorString])<br />

@ nTop, nLeft CLEAR [TO nBottom, nRight]<br />

scroll([nTop], [nLeft], [nBottom, nRight])<br />

setpos(nRow, nCol)<br />

@ nTop, nLeft GET Var [PICTURE cGetPicture] [COLOR cColorString] [WHEN lPreExpression] ←↪<br />

↩→[VALID lPostExpression]<br />

setpos(nTop, nLeft)<br />

aadd( GetList, _GET_( Var, "Var", cGetPicture, [{|| lPostExpression}],←↪<br />

↩→[{|| lPreExpression}] ):display() ) atail(GetList):colorDisp(cColorString)<br />

@ nTop, nLeft SAY exp [COLOR cColorString]<br />

devpos(nTop, nLeft)<br />

devout(exp [, cColorString])<br />

@ nTop, nLeft SAY exp PICTURE cSayPicture [COLOR cColorString]<br />

devpos(nTop, nLeft)<br />

devoutpic(exp, cSayPicture, [cColorString])<br />

APPEND<br />

CLEAR<br />

APPEND BLANK<br />

dbappend()<br />

CLEAR<br />

Scroll()<br />

SetPos(0,0)


300 volume VIII Argomenti avanzati e accessori<br />

CLOSE<br />

ReadKill(.T.)<br />

GetList := {}<br />

CLEAR GETS<br />

ReadKill(.T.)<br />

GetList := {}<br />

CLEAR SCREEN | CLS<br />

Scroll()<br />

SetPos(0,0)<br />

CLOSE<br />

dbCloseArea()<br />

CLOSE idAlias<br />

idAlias->( dbCloseArea() )<br />

CLOSE ALTERNATE<br />

Set(19, "")<br />

CLOSE DATABASES<br />

dbCloseAll()<br />

CLOSE INDEXES<br />

dbClear<strong>Index</strong>()<br />

COMMIT<br />

COMMIT<br />

COUNT<br />

dbCommitAll()<br />

COUNT TO idVar [FOR lForCondition] [WHILE lWhileCondition] [NEXT nNextRecords]←↪<br />

↩→[RECORD nRecord] [REST] [ALL]<br />

dbeval( {||idVar:=idVar+1}, {||lForCondition}, {||lWhileCondition},←↪<br />

↩→nNextRecords, nRecord, lRest )<br />

DEFAULT<br />

DEFAULT xVar TO xDefaultValue<br />

DEFAULT( @xVar, xDefaultValue ) ⇒ xVar<br />

DELETE<br />

EJECT<br />

DELETE<br />

dbDelete()<br />

DELETE [FOR lForCondition] [WHILE lWhileCondition] [NEXT nNextRecords]←↪<br />

↩→[RECORD nRecord] [REST] [ALL]<br />

dbeval( {||dbDelete()}, {||lForCondition}, {||lWhileCondition},←↪<br />

↩→nNextRecords, nRecord, lRest )<br />

DELETE FILE xcFile<br />

ferase( cFile )<br />

EJECT<br />

ERASE<br />

qqout( chr(13) )<br />

ERASE xcFile<br />

ferase( cFile )


nanoBase 1997 user manual 301<br />

FIND<br />

GO<br />

FIND xcSearchString<br />

dbSeek( cSearchString )<br />

GO[TO] nRecord<br />

dbgoto(nRecord)<br />

GO[TO] BOTTOM<br />

dbGoBottom()<br />

GO[TO] TOP<br />

dbgotop()<br />

INDEX ON<br />

READ<br />

INDEX ON expKey TO xc<strong>Index</strong>Name [UNIQUE] [FOR lForCondition]←↪<br />

↩→[WHILE lWhileCondition] [[EVAL lEvalCondition] [EVERY nRecords]] [ASCENDING|<br />

DESCENDING]<br />

ordCondSet( [cForCondition], [bForCondition], , [bWhileCondition],←↪<br />

↩→[bEvalCondition], [nRecords], RECNO(), , , , lDescending )<br />

ordCreate( c<strong>Index</strong>Name, , cExpKey, bExpKey, lUnique )<br />

READ<br />

ReadModal(GetList)<br />

GetList := {}<br />

READ SAVE<br />

ReadModal(GetList)<br />

RECALL<br />

RECALL<br />

dbRecall()<br />

RECALL [FOR lForCondition] [WHILE lWhileCondition] [NEXT nNextRecords]←↪<br />

↩→[RECORD nRecord] [REST] [ALL]<br />

dbeval( {||dbRecall()}, {||lForCondition}, {||lWhileCondition},←↪<br />

↩→nNextRecords, nRecord, lRest )<br />

REINDEX<br />

REINDEX [EVAL lEvalCondition] [EVERY nRecords]<br />

ordCondSet(, , , , [bEvalCondition], [nRecords], , , , , , , )<br />

ordListRebuild()<br />

RENAME<br />

RENAME xcOldFile TO xcNewFile<br />

frename( cOldFile, cNewFile )<br />

REPLACE<br />

REPLACE idField1 WITH exp1 [, idField2 WITH exp2...]←↪<br />

↩→[FOR lForCondition] [WHILE lWhileCondition] [NEXT nNextRecords]←↪<br />

↩→[RECORD nRecord] [REST] [ALL]<br />

dbeval( {|| idField1 := exp1 [, idField2 := exp2...]},←↪<br />

↩→{||lForCondition}, {||lWhileCondition}, nNextRecords,←↪<br />

↩→nRecord, lRest )


302 volume VIII Argomenti avanzati e accessori<br />

REPLACE idField1 WITH exp1<br />

idField1 := exp1<br />

RESTORE<br />

SAVE<br />

SEEK<br />

RESTORE SCREEN FROM cScreen<br />

restscreen( 0, 0, Maxrow(), Maxcol(), cScreen )<br />

SAVE SCREEN TO cScreen<br />

cScreen := savescreen( 0, 0, maxrow(), maxcol() )<br />

SEEK expSearch [SOFTSEEK]<br />

dbSeek( expSearch [, lS<strong>of</strong>tSeek] )<br />

SELECT<br />

SET<br />

SELECT xnWorkArea | idAlias<br />

dbSelectArea( nWorkArea | cIdAlias )<br />

SET ALTERNATE TO xcFile [ADDITIVE]<br />

Set( 19, cFile, lAdditive )<br />

SET ALTERNATE ON | OFF | xlToggle<br />

Set( 18, "ON" | "OFF" | lToggle )<br />

SET BELL ON | OFF | xlToggle<br />

Set( 26, "ON" | "OFF" | lToggle )<br />

SET COLOR | COLOUR TO (cColorString)<br />

SetColor( cColorString )<br />

SET CONFIRM ON | OFF | xlToggle<br />

Set( 27, "ON" | "OFF" | lToggle )<br />

SET CONSOLE ON | OFF | xlToggle<br />

Set( 17, "ON" | "OFF" | lToggle )<br />

SET CURSOR ON | OFF | xlToggle<br />

SetCursor( 1 | 0 | iif( lToggle, 1, 0 ) )<br />

SET DATE FORMAT [TO] cDateFormat<br />

Set( 4, cDateFormat )<br />

SET DECIMALS TO<br />

Set( 3, 0 )<br />

SET DECIMALS TO nDecimals<br />

Set( 3, nDecimals )<br />

SET DEFAULT TO<br />

Set( 7, "" )<br />

SET DEFAULT TO xcPathspec<br />

Set( 7, cPathspec )


nanoBase 1997 user manual 303<br />

SET DELETED ON | OFF | xlToggle<br />

Set( 11, "ON" | "OFF" | lToggle )<br />

SET DELIMITERS ON | OFF | xlToggle<br />

Set( 33, "ON" | "OFF" | lToggle )<br />

SET DELIMITERS TO [DEFAULT]<br />

Set( 34, "::" )<br />

SET DELIMITERS TO cDelimiters<br />

Set( 34, cDelimiters )<br />

SET DEVICE TO SCREEN | PRINTER<br />

Set( 20, "SCREEN" | "PRINTER" )<br />

SET EPOCH TO nYear<br />

Set( 5, nYear )<br />

SET ESCAPE ON | OFF | xlToggle<br />

Set( 28, "ON" | "OFF" | lToggle )<br />

SET EXACT ON | OFF | xlToggle<br />

Set( 1, "ON" | "OFF" | lToggle )<br />

SET EXCLUSIVE ON | OFF | xlToggle<br />

Set( 8, "ON" | "OFF" | lToggle )<br />

SET FILTER TO<br />

dbclearfilter()<br />

SET FILTER TO lCondition<br />

dbsetfilter( bCondition, cCondition )<br />

SET FIXED ON | OFF | xlToggle<br />

Set( 2, "ON" | "OFF" | lToggle )<br />

SET INDEX TO [xc<strong>Index</strong> [, xc<strong>Index</strong>1... ] ]<br />

ordListClear()<br />

ordListAdd( c<strong>Index</strong> )<br />

ordListAdd( c<strong>Index</strong>1 )<br />

...<br />

SET INTENSITY ON | OFF | xlToggle<br />

Set( 31, "ON" | "OFF" | lToggle )<br />

SET KEY nInkeyCode [TO]<br />

SetKey( nInkeyCode, NIL )<br />

SET KEY nInkeyCode TO [idProcedure]<br />

SetKey( nInkeyCode, { |p, l, v| idProcedure(p, l, v)} )<br />

SET MARGIN TO<br />

Set( 25, 0 )<br />

SET MARGIN TO [nPageOffset]<br />

Set( 25, nPageOffset )<br />

SET MESSAGE TO<br />

Set( 36, 0 )


304 volume VIII Argomenti avanzati e accessori<br />

SKIP<br />

STORE<br />

Set( 37, .F. )<br />

SET MESSAGE TO [nRow [CENTER | CENTRE]]<br />

Set( 36, nRow )<br />

Set( 37, lCenter )<br />

SET ORDER TO [n<strong>Index</strong>]<br />

ordSetFocus( n<strong>Index</strong> )<br />

SET PATH TO<br />

Set( 6, "" )<br />

SET PATH TO [xcPathspec [, cPathspec1... ] ]<br />

Set( 6, cPathspec [, cPathspec1... ] )<br />

SET PRINTER ON | OFF | xlToggle<br />

Set( 23, "ON" | "OFF" | lToggle )<br />

SET PRINTER TO<br />

Set( 24, "" )<br />

SET PRINTER TO [xcDevice|xcFile [ADDITIVE]]<br />

Set( 24, cDevice|cFile, lAdditive )<br />

SET RELATION TO<br />

dbclearrelation()<br />

SET RELATION TO [expKey1 INTO xcAlias1]<br />

[, [TO] expKey2 INTO xcAlias2...]<br />

[ADDITIVE]<br />

if !lAdditive<br />

dbClearRel()<br />

end<br />

dbSetRelation( cAlias1, {|| expKey1}, ["expKey1"] )<br />

dbSetRelation( cAlias2, {|| expKey2}, ["expKey1"] )<br />

SET SCOREBOARD ON | OFF | xlToggle<br />

Set( 32, "ON" | "OFF" | lToggle )<br />

SET SOFTSEEK ON | OFF | xlToggle<br />

Set( 9, "ON" | "OFF" | lToggle )<br />

SET TYPEAHEAD TO nKeyboardSise<br />

Set( 14, nKeyboardSise )<br />

SET UNIQUE ON | OFF | xlToggle<br />

Set( 10, "ON" | "OFF" | lToggle )<br />

SET WRAP ON | OFF | xlToggle<br />

Set( 35, "ON" | "OFF" | lToggle )<br />

SKIP [nRecords] [ALIAS idAlias|nWorkArea]<br />

[idAlias|nWorkArea -> ]( dbSkip([nRecords]) )<br />

STORE value TO variable


nanoBase 1997 user manual 305<br />

SUM<br />

variable := value<br />

SUM nExp1 [, nExp2...] TO idVar1 [, idVar2...] [FOR lForCondition]←↪<br />

↩→[WHILE lWhileCondition] [NEXT nNextRecords] [RECORD nRecord] [REST] [ALL]<br />

dbeval( {||idVar1:=idVar1+nExp1 [, idVar2:=idVar2+nExp2...] },←↪<br />

↩→{||lForCondition}, {||lWhileCondition}, nNextRecords, nRecord, lRest )<br />

UNLOCK<br />

USE<br />

UNLOCK<br />

dbUnlock()<br />

UNLOCK ALL<br />

dbUnlockAll()<br />

USE<br />

dbclosearea()<br />

USE [xcDatabase]←↪<br />

↩→[INDEX xc<strong>Index</strong>1 [, xc<strong>Index</strong>2...] [ALIAS xcAlias] [EXCLUSIVE|SHARED] [NEW] [READONLY<br />

] [VIA cDriver]]<br />

dbUseArea( [lNewArea], [cDriver], cDatabase, [cAlias], [lShared], [lReadOnly] )<br />

[dbSet<strong>Index</strong>( c<strong>Index</strong>1 )]<br />

[dbSet<strong>Index</strong>( c<strong>Index</strong>2 )]<br />

...<br />

368.17 nB command substitution functions<br />

Inside nB there are many functions made only in substitution to other Clipper commands.<br />

GET<br />

aGetList<br />

SAY<br />

@ nTop, nLeft GET Var<br />

[PICTURE cGetPicture]<br />

[COLOR cColorString]<br />

[WHEN lPreExpression]<br />

[VALID lPostExpression]<br />

Get( @aGetList,<br />

[nTop], [nLeft],<br />

{ |x| iif( pcount() > 0, Var := x, Var ) }<br />

[cGetPicture], [cColorString],<br />

[bPreExpression], [bValid] )<br />

@ nTop, nLeft SAY exp<br />

PICTURE cSayPicture<br />

[COLOR cColorString]<br />

Say( nTop, nLeft, cVar, [cSayPicture], [cColorString] )<br />

is the get list array that will be increased with this get().


306 volume VIII Argomenti avanzati e accessori<br />

APPEND FROM<br />

APPEND FROM xcFile<br />

[FIELDS idField_list]<br />

[scope]<br />

[WHILE lCondition]<br />

[FOR lCondition]<br />

[VIA xcDriver]<br />

dbApp( cFileName, [acFields],<br />

[bForCondition], [bWhileCondition],<br />

[nNextRecords],<br />

[nRecord],<br />

[lRest],<br />

[cDriver] )<br />

APPEND FROM xcFile<br />

[FIELDS idField_list]<br />

[scope]<br />

[WHILE lCondition]<br />

[FOR lCondition]<br />

DELIMITED xcDelimiter<br />

dbDelim( .f., cFileName, [cDelimiter], [acFields],<br />

[bForCondition], [bWhileCondition],<br />

[nNextRecords], [nRecord], [lRest] )<br />

APPEND FROM xcFile<br />

[FIELDS idField_list]<br />

[scope]<br />

[WHILE lCondition]<br />

[FOR lCondition]<br />

SDF<br />

dbSDF( .f., cFileName, [acFields],<br />

[bForCondition], [bWhileCondition],<br />

[nNextRecords], [nRecord], [lRest] )<br />

CONTINUE<br />

COPY<br />

CONTINUE<br />

dbContinue()<br />

COPY FILE xcSourceFile TO xcTargetFile|xcDevice<br />

CopyFile( cSourceFile, cTargetFile|cDevice )<br />

COPY STRUCTURE [FIELDS idField_list]<br />

TO xcDatabase<br />

dbCopyStruct( cDatabase, [acFields] )<br />

COPY STRUCTURE EXTENDED<br />

TO xcExtendedDatabase<br />

dbCopyXStruct( cExtendedDatabase )<br />

COPY TO xcFile<br />

[FIELDS idField_list]<br />

[scope]<br />

[WHILE lCondition]<br />

[FOR lCondition]<br />

[VIA xcDriver]


nanoBase 1997 user manual 307<br />

dbCopy( cFileName, [acFields],<br />

[bForCondition], [bWhileCondition],<br />

[nNextRecords],<br />

[nRecord],<br />

[lRest],<br />

[cDriver] )<br />

COPY TO xcFile<br />

[FIELDS idField_list]<br />

[scope]<br />

[WHILE lCondition]<br />

[FOR lCondition]<br />

DELIMITED xcDelimiter<br />

dbDelim( .t., cFileName, [cDelimiter], [acFields],<br />

[bForCondition], [bWhileCondition],<br />

[nNextRecords], [nRecord], [lRest] )<br />

COPY TO xcFile<br />

[FIELDS idField_list]<br />

[scope]<br />

[WHILE lCondition]<br />

[FOR lCondition]<br />

SDF<br />

dbSDF( .t., cFileName, [acFields],<br />

[bForCondition], [bWhileCondition],<br />

[nNextRecords], [nRecord], [lRest] )<br />

CREATE<br />

JOIN<br />

CREATE xcDatabase<br />

FROM xcExtendedDatabase<br />

[NEW]<br />

[ALIAS cAlias]<br />

[VIA cDriver]<br />

dbOldCreate( cDatabase, cExtendedDatabase,<br />

[cDriver], [lNew], [cAlias] )<br />

JOIN WITH xcAlias TO xcDatabase<br />

[FOR lCondition] [FIELDS idField_list]<br />

dbJoin( cAlias, cDatabase,<br />

[acFields], [bForCondition] )<br />

KEYBOARD<br />

KEYBOARD cString<br />

Keyboard( [cString] ) ⇒ NIL<br />

LABEL FORM<br />

LABEL FORM xcLabel<br />

[TO PRINTER]<br />

[TO FILE xcFile]<br />

[NOCONSOLE]<br />

[scope]<br />

[WHILE lCondition]<br />

[FOR lCondition]<br />

[SAMPLE]<br />

dbLabelForm( cLabel, [lToPrinter], [cFile],<br />

[lNoConsole], [bForCondition], [bWhileCondition],<br />

[nNextRecords], [nRecord], [lRest], [lSample] )


308 volume VIII Argomenti avanzati e accessori<br />

LIST<br />

LIST exp_list<br />

[TO PRINTER]<br />

[TO FILE xcFile]<br />

[scope]<br />

[WHILE lCondition]<br />

[FOR lCondition]<br />

[OFF]<br />

dbList( [lToDisplay], abListColumns,<br />

[lAll],<br />

[bForCondition], [bWhileCondition],<br />

[nNextRecords], [nRecord], [lRest],<br />

[lToPrinter], [cFileName] )<br />

LOCATE<br />

PACK<br />

LOCATE [scope] FOR lCondition<br />

[WHILE lCondition]<br />

dbLocate( [bForCondition], [bWhileCondition],<br />

[nNextRecords], [nRecord], [lRest] )<br />

PACK<br />

dbPack()<br />

PUBLIC<br />

QUIT<br />

PUBLIC idMemvar<br />

MemPublic( cMemvarName|acMemvarNames )<br />

QUIT<br />

Quit()<br />

RELEASE<br />

RELEASE idMemvar<br />

MemRelease( cMemvarName|acMemvarNames )<br />

REPORT FORM<br />

REPORT FORM xcReport<br />

[TO PRINTER]<br />

[TO FILE xcFile]<br />

[NOCONSOLE]<br />

[scope]<br />

[WHILE lCondition]<br />

[FOR lCondition]<br />

[PLAIN | HEADING cHeading]<br />

[NOEJECT] [SUMMARY]<br />

RF( cForm,<br />

[bForCondition], [bWhileCondition],<br />

[nNext], [nRecord], [lRest], [lPlain],<br />

[cbHeading], [lBeforeEject], [lSummary],<br />

[lDate], [acExtra] ) ⇒ NIL<br />

RESTORE FROM<br />

RESTORE FROM xcMemFile [ADDITIVE]<br />

MemRestore( cMemFileName, [lAdditive] )


nanoBase 1997 user manual 309<br />

RUN<br />

RUN xcCommandLine<br />

Run( cCommand )<br />

SAVE TO<br />

SAVE TO xcMemFile<br />

[ALL [LIKE|EXCEPT skeleton]]<br />

MemSave( cMemFileName, [cSkeleton], [lLike] )<br />

SET FUNCTION<br />

SORT<br />

TOTAL<br />

SET FUNCTION nFunctionKey TO cString<br />

SetFunction( nFunctionKey, cString )<br />

SORT TO xcDatabase<br />

ON idField1 [/[A|D][C]]<br />

[, idField2 [/[A|D][C]] ...]<br />

[scope]<br />

[WHILE lCondition]<br />

[FOR lCondition]<br />

dbSort( cDatabase, [acFields],<br />

[bForCondition], [bWhileCondition],<br />

[nNextRecords], [nRecord], [lRest] )<br />

TOTAL ON expKey<br />

[FIELDS idField_list] TO xcDatabase<br />

[scope]<br />

[WHILE lCondition]<br />

[FOR lCondition]<br />

dbTotal( cDatabase, bKey, [acFields,<br />

[bForCondition], [bWhileCondition],<br />

[nNextRecords], [nRecord]], [lRest] )<br />

UPDATE<br />

ZAP<br />

UPDATE FROM xcAlias<br />

ON expKey [RANDOM]<br />

REPLACE idField1 WITH exp<br />

[, idField2 WITH exp ...]<br />

dbUpdate( cAlias, bKey, [lRandom], [bReplacement] )<br />

Example:<br />

ZAP<br />

dbZap()<br />

dbUpdate( "INVOICE", {|| LAST}, .T.,;<br />

{|| FIELD->TOTAL1 := INVOICE->SUM1,;<br />

FIELD->TOTAL2 := INVOICE->SUM2 } )


310 volume VIII Argomenti avanzati e accessori<br />

368.18 RPT: the nB print function<br />

The function RPT() helps to print ASCII file containing Memvars, Fields and print commands.<br />

RPT() is accessible from the DOC() menu.<br />

368.18.1 Memvars and fields<br />

As usual with standard word processors, variables are written delimited with "" (Alt+175).<br />

Inside these delimiters can find place character Memvars, character Fields and functions giving<br />

a character result.<br />

The RPT() function generates a public variable n_Lines that contains the available lines inside<br />

the actual sheet. Every time a line is written, this value is reduced, until a new page is reached and<br />

then it will start again from the maximum value. It is useful to read this variable to determinate<br />

if there is enough space or it is better to change page.<br />

368.18.2 Commands<br />

The function RPT() recognise some print commands. These commands starts with the asterisk<br />

(*) symbol. This means that "*" is a print command prefix.<br />

It follows the command syntax.<br />

*COMMAND<br />

*COMMAND<br />

cStatement<br />

cStatement<br />

...<br />

*END<br />

The lines contained inside *COMMAND - *END are executed with the nB macro interpreter.<br />

*DBSKIP<br />

*DBSKIP [nSkip]<br />

It Executes a dbskip() on the active alias.<br />

*FOOT<br />

*FOOT<br />

cFooter<br />

cFooter<br />

...<br />

*END<br />

The lines contained inside *FOOT - *END are printed each time at the bottom <strong>of</strong> pages.<br />

*HEAD<br />

*HEAD<br />

cHeader<br />

cHeader<br />

...<br />

*END<br />

The lines contained inside *HEAD - *END are printed each time at the top <strong>of</strong> pages.


nanoBase 1997 user manual 311<br />

*IF<br />

*IF lCondition<br />

...<br />

...<br />

*END<br />

If the condition lCondition is true, the lines contained inside *IF - *END are printed.<br />

*INSERT<br />

*LEFT<br />

*LPP<br />

*INSERT cFileName<br />

Includes the text contained into the file cFileName.<br />

*LEFT nLeftBorder<br />

The nLeftBorder is the number <strong>of</strong> column to be left blank as a left border.<br />

*LPP nLinesPerPage<br />

It determinates the page length expressed in lines. After printing the nLinesPerPageth line,<br />

a form feed is sent.<br />

*NEED<br />

*PA<br />

*NEED nLinesNeeded<br />

If the available lines are less then nLinesNeeded, the follwing text will be printed on the<br />

next page.<br />

*PA<br />

*REM<br />

Jumps to a new page.<br />

*REM | *COMMENT [comment_line]<br />

It adds a comment that will not be printed.<br />

*WHILE<br />

*WHILE lCondition<br />

...<br />

...<br />

*END<br />

The lines contained inside *WHILE - *END are printed as long as lCondition is true.<br />

368.18.3 Examples<br />

It follows some example <strong>of</strong> text to be printed with the RPT() function. Example’s lines are numbered.<br />

Line numbers must not be part <strong>of</strong> a real RPT text files.<br />

PAGE DEFINITION<br />

Margins are defined with *HEAD, *FOOT and *LEFT commands. In the following example<br />

is defined:


312 volume VIII Argomenti avanzati e accessori<br />

Top 2 lines;<br />

Bottom 2 lines;<br />

Left 10 characters.<br />

The right margin is not defined as it depends on the lines length that will be printed.<br />

The only considered page dimension is the height, *LPP (lines per page):<br />

Page height 66 lines.<br />

Here starts the example:<br />

001 *lpp 66<br />

002 *head<br />

003<br />

004<br />

005 *end<br />

006 *foot<br />

007<br />

008<br />

009 *end<br />

010 *left 10<br />

011 ... text text text<br />

012 ... test text text<br />

...<br />

At line 001 is defined the page height in lines. At line 002 is defined the header; it contains<br />

two empty lines (003 and 004) which will be printed at the top <strong>of</strong> every page. At line 006<br />

starts the footer definition that contains two empty lines (007 and 008) that will be printed<br />

at the end <strong>of</strong> every page. At line 010 is defined the space on the left that will be added to<br />

every line printed. From line 011 starts the normal text.<br />

HEADER AND FOOTER<br />

The commands *HEAD and *FOOT are used to define the top and bottom border if they<br />

contains empty lines, it these lines are not empty, they became real head and foot.<br />

The dimensions are as it follows:<br />

Top 6 lines (should be one inch);<br />

Bottom 6 lines;<br />

Left 10 characters (should be an inch).<br />

Page height 66 lines (should be 11 inch).<br />

At position 0.5 in (after 3 lines) a one line header appears.


nanoBase 1997 user manual 313<br />

001 *lpp 66<br />

002 *head<br />

003<br />

004<br />

005<br />

006 ------------------- MYFILE.TXT -------------------<br />

007<br />

008<br />

009 *end<br />

010 *foot<br />

011<br />

012<br />

013<br />

014<br />

015<br />

016<br />

017 *end<br />

018 *left 10<br />

019 ... text text text<br />

020 ... test text text<br />

...<br />

At line 006 (the fourth header line) a text appears. It will be printed on every page at the<br />

absolute fourth page line.<br />

CODE INSERTION<br />

Pieces <strong>of</strong> code can be inserted inside *COMMAND - *END. It can be useful to make<br />

complicated reports.<br />

The following example declares a public variable used to number pages.<br />

001 *command<br />

002 mempublic("PageNo")<br />

003 PageNo := 0<br />

004 *end<br />

005 *lpp 66<br />

006 *head<br />

007 *command<br />

008 PageNo := PageNo +1<br />

009 *end<br />

010<br />

011<br />

012 *end<br />

013 *foot<br />

014<br />

015 Page <br />

016<br />

017 *end<br />

018 *left 10<br />

019 ... text text text<br />

020 ... test text text<br />

...<br />

At line 001 starts a *COMMAND definition: lines 002 and 003 will be interpreted from the<br />

function EX(), the nB interpreter. These lines define a public variable and initialize it at 0.<br />

This variable will be use to count pages.<br />

At line 007, inside the header (nested), start another *COMMAND definition that contains<br />

an increment for the "PageNo" variable. As the header is read and "executed" for every new<br />

page, and that before the footer, the variable "PageNo" will contain the right page number.<br />

At line 015, inside the footer, a reference to "PageNo" appears. Here will be printed the<br />

page number.<br />

A more complicated example can be found in ‘ADDRESS.TXT’ the RPT text file used for<br />

the ADDRESS.& macro examples.


314 volume VIII Argomenti avanzati e accessori<br />

368.19 How can I...<br />

nB is a little bit complicated as it may do many things. Here are some examples.<br />

Create a UDF function<br />

UDF means User Defined Function. Inside nB there isn’t the possibility to create functions,<br />

but there is an alternative: code blocks.<br />

Create a big code block<br />

A code block cannot be longer than 254 characters, as any other instruction inside nB.<br />

So, there is no way to make a bigger code block, but a code block can call another code<br />

block, and so on. For example:<br />

mempublic( { "first", "second", "third" } )<br />

first := {|| eval( second, "hello" ) }<br />

second := {|x| eval( third, x ) }<br />

third := {|x| alertbox( x ) }<br />

eval( first )<br />

This stupid example simply will show the alert box containing the word "hello".<br />

368.20 The source files<br />

The nB source is composed <strong>of</strong> four files:<br />

‘NB.PRG’ The main source file containing essentially the nB menu.<br />

‘REQUEST.PRG’ Contains a link to all Clipper standard functions.<br />

‘STANDARD.PRG’ Contains the most important standard functions.<br />

‘EXTRA.PRG’<br />

Contains some extra function not absolutely necessary during<br />

macro execution.<br />

The file ‘REQUEST.PRG’ source file generates some warnings because not all functions listed<br />

there are directly called form nB. Don’t worry about that warning message.<br />

Different ‘.RMK’ (rmake) files are included to compile nB differently, including/excluding some<br />

program parts, for example to obtain a runtime executor.<br />

Appunti di informatica libera 2003.06.29 --- Copyright © 2000-2003 Daniele Giacomini -- daniele @ swlibero.org


Parte lxxiii<br />

Braille<br />

369 Introduzione al sistema braille . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316<br />

369.1 Origini e caratteristiche del sistema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316<br />

369.2 Codifica semplificata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 317<br />

369.3 Braille a otto punti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318<br />

369.4 Tabelle di conversione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318<br />

369.5 Riferimenti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320<br />

370 Sistemi di interazione per non vedenti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321<br />

370.1 Lettura di ciò che appare sullo schermo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321<br />

370.2 Terminali braille . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321<br />

370.3 Brltty . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323<br />

370.4 Riferimenti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 324<br />

315


Introduzione al sistema braille<br />

Capitolo 369<br />

Lo scopo di questo capitolo è quello di descrivere brevemente il codice usato dal sistema braille,<br />

in modo di facilitare la comprensione del funzionamento di un terminale per non vedenti. Ciò<br />

potrebbe servire per verificare l’installazione e la configurazione corretta di un dispositivo del<br />

genere.<br />

369.1 Origini e caratteristiche del sistema<br />

Il sistema di scrittura per non vedenti è stato inventato da Louis Braille nel 1829, da cui viene<br />

il nome. Luis Braille era francese e in questo senso vanno viste le scelte che possono sembrare<br />

insolite, per chi è abituato a convenzioni derivanti dalla lingua inglese.<br />

Il sistema codifica 63 simboli rappresentati ognuno da una cella in cui possono essere collocati<br />

un massimo di sei punti in rilievo:<br />

.----.<br />

|* *|<br />

|* *|<br />

|* *|<br />

‘----’<br />

Tanto per fare un esempio, se si possono immaginare gli asterischi della figura seguente come<br />

altrettanti punti in rilievo, la parola «ciao!» si può rappresentare in questo modo:<br />

* * * * *<br />

* * * *<br />

* *<br />

Il sistema braille è fatto fondamentalmente per una scrittura manuale, che avviene attraverso<br />

l’uso di una tavoletta speciale, come si può vedere nelle figure 369.3 e 369.4.<br />

Figura 369.3. Tavoletta di scrittura braille normale.<br />

316


Introduzione al sistema braille 317<br />

La scrittura avviene su un foglio di cartoncino bloccato sulla tavoletta, con l’aiuto di un punteruolo,<br />

guidato dalle feritoie poste su un regolo mobile (nel caso della tavoletta tascabile, il regolo<br />

è fisso). Dal momento che la scrittura avviene attraverso l’incisione del cartoncino, la lettura tattile,<br />

in rilievo, potrà avvenire solo sull’altro lato; pertanto, per quanto riguarda le lingue latine, la<br />

scrittura avviene da destra verso sinistra e la lettura da sinistra a destra.<br />

Figura 369.4. Tavoletta di scrittura braille tascabile. La fase si scrittura avviene inciden-<br />

do il cartoncino. In questo caso, non si vede la fila superiore, perché rimane coperta<br />

dall’ombra.<br />

Figura 369.5. Testo inciso da destra verso sinistra.<br />

Figura 369.6. Testo in rilievo da sinistra verso destra.<br />

Il braille standard è composto da celle con un massimo di sei punti in rilievo, per un massimo<br />

di 2 6 -1 segni, più lo spazio; tuttavia, possono esistere anche dispositivi braille predisposti per<br />

un massimo di otto punti in rilievo.<br />

369.2 Codifica semplificata<br />

La disposizione classica della codifica del braille è sintetizzata dalle figure 369.9 e 369.10. Chi<br />

volesse studiare ed esercitarsi con il braille, farebbe bene a copiare le due figure in modo da<br />

mettere a sinistra quella dei segni in scrittura e a destra quella dei segni in lettura.<br />

Come si può comprendere a livello intuitivo, i 63 simboli non consentono di distinguere le lettere<br />

minuscole da quelle maiuscole; in generale si tratta sempre solo di lettere minuscole che<br />

diventano maiuscole se precedute dal segno relativo. Per esempio, «Daniele» si scrive così:<br />

* * * * * * * * * *<br />

* * * * * *<br />

* * *


318 volume VIII Argomenti avanzati e accessori<br />

Nello stesso modo, come si vede, non sono previsti i numeri. Questi si ottengono indicando il<br />

segno di numero davanti alle prime 10 lettere alfabetiche. Per esempio, 123 si indica così:<br />

* * * * *<br />

* *<br />

* *<br />

Pertanto, a differenza delle maiuscole, il segno di numero vale per tutta la sequenza di simboli<br />

successiva.<br />

Esistono naturalmente altri dettagli molto importanti per chi deve usare il braille, che però qui<br />

non vengono descritti. Purtroppo, i segni a disposizione sono molto pochi e di conseguenza, tutto<br />

quanto ha sempre un valore relativo al contesto.<br />

369.3 Braille a otto punti<br />

La codifica a otto punti consente di utilizzare una sola cella per indicare una lettera maiuscola o<br />

un numero. Ciò diventa particolarmente importante in un terminale braille, in cui è bene che si<br />

possa mantenere una corrispondenza tra celle e caratteri.<br />

369.4 Tabelle di conversione<br />

L’abbinamento del braille all’informatica, impone l’introduzione di tabelle di conversione dalla<br />

codifica dei caratteri al braille stesso. In pratica, in base alla nazionalità, si utilizza una codifica<br />

particolare; oltre a questo, deve essere definito un abbinamento tra codice binario (ASCII, ISO<br />

8859-n, ecc.) e rappresentazione braille. Quando si utilizza un terminale braille o un sistema di<br />

stampa braille, occorre stabilire anche questo tipo di conversione.


Introduzione al sistema braille 319<br />

Figura 369.9. La codifica braille per la scrittura da destra verso sinistra, secondo<br />

l’ordine classico.<br />

j i h g f e d c b a<br />

* . * . . * * * * * . * * * * * . * . *<br />

* * . * * * * * . * * . * . . . . * . .<br />

. . . . . . . . . . . . . . . . . . . .<br />

t s r q p o n m l k<br />

* . * . . * * * * * . * * * * * . * . *<br />

* * . * * * * * . * * . * . . . . * . .<br />

. * . * . * . * . * . * . * . * . * . *<br />

ù è à é ç z y x v u<br />

* . * . . * * * * * . * * * * * . * . *<br />

* * . * * * * * . * * . * . . . . * . .<br />

* * * * * * * * * * * * * * * * * * * *<br />

w æ ü ï ë û ô î ê â<br />

* . * . . * * * * * . * * * * * . * . *<br />

* * . * * * * * . * * . * . . . . * . .<br />

* . * . * . * . * . * . * . * . * . * .<br />

« * » () ! ? . : ; ,<br />

. . . . . . . . . . . . . . . . . . . .<br />

* . * . . * * * * * . * * * * * . * . *<br />

* * . * * * * * . * * . * . . . . * . .<br />

numero ò verso ì lineetta apostr<strong>of</strong>o<br />

* . * . * . * * . . . .<br />

* . . . * . . . . . . .<br />

* * * * . * * . * * . *<br />

maiuscole corsivo<br />

. . . . * . . . * . * . * .<br />

. . * . . . * . * . * . . .<br />

* . * . * . . . * . . . . .


320 volume VIII Argomenti avanzati e accessori<br />

Figura 369.10. La codifica braille per la lettura da sinistra verso destra, secondo<br />

l’ordine classico.<br />

a b c d e f g h i j<br />

* . * . * * * * * . * * * * * . . * . *<br />

. . * . . . . * . * * . * * * * * . * *<br />

. . . . . . . . . . . . . . . . . . . .<br />

k l m n o p q r s t<br />

* . * . * * * * * . * * * * * . . * . *<br />

. . * . . . . * . * * . * * * * * . * *<br />

* . * . * . * . * . * . * . * . * . * .<br />

u v x y z ç é à è ù<br />

* . * . * * * * * . * * * * * . . * . *<br />

. . * . . . . * . * * . * * * * * . * *<br />

* * * * * * * * * * * * * * * * * * * *<br />

â ê î ô û ë ï ü æ w<br />

* . * . * * * * * . * * * * * . . * . *<br />

. . * . . . . * . * * . * * * * * . * *<br />

. * . * . * . * . * . * . * . * . * . *<br />

, ; : . ? ! () « * »<br />

. . . . . . . . . . . . . . . . . . . .<br />

* . * . * * * * * . * * * * * . . * . *<br />

. . * . . . . * . * * . * * * * * . * *<br />

apostr<strong>of</strong>o lineetta ì verso ò numero<br />

. . . . * * . * . * . *<br />

. . . . . . . * . . . *<br />

* . * * . * * . * * * *<br />

corsivo maiuscole<br />

. * . * . * . . . * . . . .<br />

. . . * . * . * . . . * . .<br />

. . . . . * . . . * . * . *<br />

369.5 Riferimenti<br />

• Dottless braille<br />

<br />

• A (slightly) different introduction to braille<br />

<br />

Appunti di informatica libera 2003.06.29 --- Copyright © 2000-2003 Daniele Giacomini -- daniele @ swlibero.org


Sistemi di interazione per non vedenti<br />

Capitolo 370<br />

I sistemi standard di interazione per non vedenti con un elaboratore sono di due tipi, che si<br />

integrano convenientemente assieme: il terminale braille e la lettura automatica di ciò che appare<br />

sullo schermo. Il terminale braille è un’unità che si collega generalmente a una porta seriale o<br />

parallela e richiede la presenza di un programma in grado di leggere ciò che appare sullo schermo<br />

normale; il sistema di lettura automatica richiede naturalmente un programma simile.<br />

Un sistema di lettura automatico è apparentemente un problema semplice da risolvere; in pratica<br />

le cose non sono così, perché deve anche essere in grado di individuare la struttura del testo (si<br />

pensi a un’organizzazione in più colonne). Il sistema che sembra essere più efficace per la lettura<br />

sonora del testo è Emacspeack, che comunque qui non viene descritto. Si vedano eventualmente<br />

i riferimenti bibliografici alla fine del capitolo.<br />

370.1 Lettura di ciò che appare sullo schermo<br />

Qualunque sistema di lettura per non vedenti (braille o audio), deve avere un modo per accedere<br />

a ciò che appare sullo schermo. Esistono evidentemente due modi: ciò che appare sullo schermo<br />

è controllato direttamente dal programma di lettura, oppure questo programma deve interagire<br />

con il sistema operativo per ottenere una copia dello schermo.<br />

Un programma che controlla ciò che si deve leggere potrebbe essere una shell, oppure una sorta<br />

di navigatore ipertestuale, che eventualmente incorpori anche delle funzionalità per la creazione<br />

e la modifica dei documenti (dovrebbe essere questo il caso di Emacspeack).<br />

Nel caso di GNU/Linux si accede a una copia di ciò che appare sullo schermo della console<br />

attiva attraverso il file di dispositivo ‘/dev/vcsa’ o ‘/dev/vcsa0’. In particolare questi file di<br />

dispositivo consentono di ottenere informazioni anche sugli attributi del testo, cosa che permette<br />

di localizzare il cursore, per esempio; esistono infatti anche i file di dispositivo ‘/dev/vcs * ’<br />

che consentono di ottenere una pura copia testuale delle console virtuali.<br />

Questi file di dispositivo sono normalmente inaccessibili a utenti diversi dall’amministratore;<br />

pertanto, per consentire una lettura a programmi con privilegi limitati, occorre intervenire nei<br />

permessi. Supponendo di dover creare da zero il file di dispositivo ‘/dev/vcsa0’, probabilmente<br />

si dovrebbe agire come segue:<br />

# mknod /dev/vcsa0 c 7 128 [ Invio ]<br />

# chown root.tty /dev/vcsa0 [ Invio ]<br />

# chmod 660 /dev/vcsa0 [ Invio ]<br />

In pratica, si arriva alla fine a dare i permessi di lettura e scrittura anche al gruppo, supponendo<br />

che il programma che deve utilizzarlo funzioni, in questo caso, con i privilegi del gruppo ‘tty’<br />

(SGID-‘tty’). Ciò consente di mantenere un minimo di riservatezza, nonostante il problema<br />

contingente da risolvere.<br />

Volendo verificare che il file di dispositivo ‘/dev/vcsa0’ consente di accedere al contenuto<br />

dello schermo della console virtuale attuale, si può inviarne una copia in una console inattiva o<br />

comunque non utilizzata. Per esempio:<br />

# cat /dev/vcsa0 > /dev/tty12 [ Invio ]<br />

321


322 volume VIII Argomenti avanzati e accessori<br />

Manda una copia alla 12-esima console virtuale e si può verificare facilmente con la<br />

combinazione di tasti [ Alt+F12 ].<br />

370.2 Terminali braille<br />

Il terminale braille è solitamente un componente da collocare al di sotto della tastiera normale,<br />

attraverso il quale è possibile leggere una riga di testo, senza allontanare troppo le dita dalla<br />

tastiera stessa.<br />

Vicino alla barra contenente le celle braille, devono essere disponibili alcuni tasti per il controllo<br />

del terminale stesso, per poter navigare sulla superficie di quello che appare sullo schermo. Infatti,<br />

di solito si dispone di una sola riga di celle braille, ma anche se potessero essere disponibili più di<br />

una, non sarebbero mai quante le righe che si vedono su uno schermo normale; inoltre, anche la<br />

quantità di celle è limitata, per cui diventa necessario poter spostare la «visualizzazione» braille<br />

anche in orizzontale.<br />

Figura 370.1. Collocazione tipica di un terminale braille, sotto la tastiera o sotto un<br />

elaboratore portatile.<br />

La figura 370.2 mostra in particolare un dettaglio delle celle di un terminale braille a otto punti.<br />

Figura 370.2. Dettaglio delle celle di un terminale braille.


Sistemi di interazione per non vedenti 323<br />

370.3 Brltty<br />

Brltty 1 è un programma in grado di comunicare con un terminale braille, allo scopo di inviargli<br />

ciò che accade sullo schermo della console virtuale attiva di un sistema GNU/Linux.<br />

Il funzionamento del programma è relativamente semplice: si tratta di un demone, ‘brltty’, da<br />

avviare e fermare automaticamente attraverso la procedura di inizializzazione del sistema, che<br />

comunica con il terminale braille seguendo la configurazione relativa al terminale stesso e la<br />

configurazione della conversione braille.<br />

In pratica, Brltty deve conoscere il tipo di terminale braille utilizzato e deve disporre di una tabella<br />

di conversione adatta per quanto riguarda la codifica usata e l’alfabeto braille dell’utilizzatore.<br />

370.3.1 Configurazione<br />

Brltty è accompagnato da una serie di file di libreria, per l’adattamento alle caratteristiche del<br />

terminale braille e per la conversione finale della codifica in braille. Questi file dovrebbero essere<br />

collocati nella directory ‘/etc/brltty/’.<br />

Il file di configurazione in cui occorre intervenire dovrebbe essere ‘/etc/brltty.conf’, il cui<br />

contenuto potrebbe apparire come nell’esempio seguente:<br />

braille-device /dev/ttyS0<br />

braille-driver ec<br />

dot-translation text.us.tbl<br />

Si comprende intuitivamente il significato delle direttive: il terminale viene connesso alla prima<br />

porta seriale, corrispondente al file di dispositivo ‘/dev/ttyS0’; il terminale braille viene gestito<br />

attraverso un protocollo definito dalla sigla ‘ec’; la trasformazione della codifica in braille<br />

avviene secondo il modello denominato ‘text.us.tbl’, che in pratica è un file contenuto nella<br />

directory ‘/etc/brltty/’, facente riferimento a un adattamento per la lingua inglese.<br />

Tabella 370.1. Sigle attraverso le quali si abbinano le librerie di controllo del terminale<br />

braille.<br />

Sigla Terminale corrispondente<br />

al<br />

Alva (ABT3xx/Delphi)<br />

b1 BrailleLite 18<br />

b4 BrailleLite 40<br />

cb Tieman CombiBraille<br />

ec EcoBraille<br />

eu EuroBraille<br />

md<br />

pm<br />

MDV braille<br />

Papenmeier<br />

ts TSI (PowerBraille/Navigator)<br />

va BAUM Vario<br />

Brltty è anche in grado di gestire terminali parlanti. Eventualmente, si può usare la direttiva<br />

‘speech-driver’ per il controllo della sintesi vocale. Di solito, la funzionalità è disabilitata,<br />

con la direttiva seguente:<br />

speech-driver no<br />

1 Brltty GNU GPL


324 volume VIII Argomenti avanzati e accessori<br />

Tabella 370.2. Sigle relative alla gestione della sintesi vocale.<br />

Sigla Sintesi vocale<br />

no No Speech<br />

al<br />

Alva (Delphi)<br />

bl BrailleLite<br />

cb Tieman CombiBraille<br />

fv<br />

Festival Text to Speech Package<br />

gs Generic Say (invia allo standard input di ‘/usr/local/bin/say’)<br />

tv Televox Speech Interface<br />

370.3.2 Accesso alla copia della console virtuale corrente<br />

Come già spiegato in precedenza, anche Brltty deve poter accedere a una copia della console<br />

virtuale corrente. Ciò avviene precisamente attraverso il file di dispositivo ‘/dev/vcsa0’, che in<br />

alcuni sistemi GNU/Linux potrebbe anche essere assente, perché equivalente a ‘/dev/vcsa’. Si<br />

riepilogano nuovamente i passi necessari a creare il file e ad attribuirgli i permessi necessari per<br />

il funzionamento corretto con Brltty.<br />

# mknod /dev/vcsa0 c 7 128 [ Invio ]<br />

# chown root.root /dev/vcsa0 [ Invio ]<br />

# chmod 600 /dev/vcsa0 [ Invio ]<br />

In questo caso, si presume che il demone ‘brltty’ sia avviato attraverso la procedura di inizializzazione<br />

del sistema, in modo tale da funzionare con tutti i privilegi dell’utente ‘root’.<br />

Diversamente, occorre cambiare strategia per quanto riguarda i permessi e la proprietà del file di<br />

dispositivo.<br />

370.4 Riferimenti<br />

• BRLTTY - <strong>of</strong>ficial home<br />

<br />

• BRLSPEAK: a braille and speech mini-distribution <strong>of</strong> GNU/Linux<br />

<br />

• Blind + Linux = BLINUX<br />


Parte lxxiv<br />

Aspetti umani<br />

371 Manifesto GNU . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331<br />

372 Il progetto GNU . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339<br />

372.1 La prima comunità di condivisione del s<strong>of</strong>tware . . . . . . . . . . . . . . . . . . . . . . . . . . . 339<br />

372.2 La comunità si dissolve . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339<br />

372.3 Una difficile scelta morale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341<br />

372.4 "Free" come libero . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 342<br />

372.5 S<strong>of</strong>tware GNU e il sistema GNU . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 342<br />

372.6 L’inizio del progetto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343<br />

372.7 I primi passi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343<br />

372.8 GNU Emacs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343<br />

372.9 Un programma è libero per tutti? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344<br />

372.10 Il permesso d’autore (copyleft) e la GNU GPL . . . . . . . . . . . . . . . . . . . . . . . . . . . 344<br />

372.11 La Free S<strong>of</strong>tware Foundation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345<br />

372.12 Il supporto per il s<strong>of</strong>tware libero . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .346<br />

372.13 Obiettivi tecnici . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346<br />

372.14 Donazioni di computer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 347<br />

372.15 L’elenco dei compiti GNU . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .347<br />

372.16 La licenza GNU per le librerie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 347<br />

372.17 Togliersi il prurito? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 348<br />

372.18 Sviluppi inattesi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349<br />

372.19 GNU-Hurd . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349<br />

372.20 Alix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349<br />

372.21 Linux e GNU/Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349<br />

372.22 Le sfide che ci aspettano . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 350<br />

372.23 Hardware segreto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 350<br />

372.24 Librerie non libere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 350<br />

372.25 Brevetti sul s<strong>of</strong>tware . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351<br />

372.26 Documentazione libera . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352<br />

372.27 Dobbiamo parlare di libertà . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 353<br />

372.28 "Open Source" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 353<br />

372.29 Prova! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 353<br />

373 La rivoluzione del Mimete, ovvero s<strong>of</strong>tware e copyright . . . . . . . . . . . . . . . . . . . . . . . . . .355<br />

373.1 Che cosa è il s<strong>of</strong>tware? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 355<br />

373.2 Cenni di storia del s<strong>of</strong>tware . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 356<br />

325


374 Il copyright/diritto d’autore del s<strong>of</strong>tware . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358<br />

374.1 Una breve storia del diritto d’autore . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358<br />

374.2 I sei punti della rivoluzione digitale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 359<br />

374.3 I vantaggi del «free s<strong>of</strong>tware» . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 360<br />

Indice analitico del volume . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 361<br />

375 Configurazione di una distribuzione Debian . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6<br />

375.1 Procedura di inizializzazione del sistema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6<br />

375.2 Configurazione del sistema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .13<br />

375.3 Configurazione di shell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14<br />

375.4 Utenti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15<br />

375.5 Stampa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .15<br />

375.6 Configurazione del nome del sistema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .16<br />

375.7 Configurazione dei permessi degli eseguibili . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16<br />

375.8 Riferimenti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16<br />

376 Accorgimenti per una distribuzione Debian . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18<br />

376.1 Raccogliere gli aggiornamenti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18<br />

376.2 Realizzazione di una copia personale della distribuzione . . . . . . . . . . . . . . . . . . . . 23<br />

376.3 Riferimenti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28<br />

377 nanoRouter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29<br />

377.1 Kernel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29<br />

377.2 Lo stretto indispensabile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29<br />

377.3 Dimensioni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32<br />

377.4 File system in sola lettura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32<br />

378 nanoLinux II . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .33<br />

378.1 Lavoro di cesello . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33<br />

378.2 Dischi più grandi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .37<br />

378.3 Provare nanoLinux II . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37<br />

378.4 Organizzazione di nanoLinux II . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40<br />

378.5 Personalizzazione della procedura di inizializzazione del sistema . . . . . . . . . . . . 46<br />

379 Dischetti di emergenza delle distribuzioni GNU/Linux e di altre fonti . . . . . . . . . . . . . . . 48<br />

379.1 SuSE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48<br />

380 Accorgimenti per usare GNU/Linux nella didattica di massa . . . . . . . . . . . . . . . . . . . . . . . 52<br />

380.1 Utente speciale per uno scopo speciale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52<br />

380.2 Spegnimento da parte di utenti comuni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53<br />

380.3 Autorizzare chiunque ad aggiungersi come nuovo utente . . . . . . . . . . . . . . . . . . . . 55<br />

326


380.4 Sincronizzazione di file di configurazione senza NIS . . . . . . . . . . . . . . . . . . . . . . . .56<br />

381 Introduzione a SMB con GNU/Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .59<br />

381.1 Nomi NetBIOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59<br />

381.2 Trasporto TCP/IP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60<br />

381.3 Servente SMB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60<br />

381.4 Cliente SMB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65<br />

381.5 Connessione con una rete NetBIOS-TCP/IP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67<br />

381.6 Stampa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .71<br />

381.7 Montare una risorsa di condivisione dei file nel file system GNU/Linux . . . . . . .72<br />

381.8 Riferimenti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73<br />

382 DOSEMU: l’emulatore di hardware DOS compatibile . . . . . . . . . . . . . . . . . . . . . . . . . . . . .75<br />

382.1 Predisporre un ambiente adatto al Dos all’interno di DOSEMU . . . . . . . . . . . . . . 75<br />

382.2 La configurazione di DOSEMU . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76<br />

382.3 Installare e utilizzare il Dos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78<br />

383 LeeOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83<br />

Indice analitico del volume . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85<br />

384 Amministrazione del sistema. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .26<br />

384.1 Installazione di GNU/Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26<br />

384.2 Avvio e arresto del sistema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29<br />

384.3 Tipi di file system e partizioni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37<br />

384.4 Swap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39<br />

384.5 Montare i dischi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40<br />

384.6 LILO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44<br />

384.7 Kernel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52<br />

384.8 Moduli . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .64<br />

384.9 Configurazione del sistema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .65<br />

384.10 Utenti e password . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75<br />

384.11 Permessi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79<br />

384.12 File, collegamenti (link) e directory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82<br />

384.13 Configurazione della console . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91<br />

384.14 File di log . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98<br />

384.15 Cron e At . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101<br />

384.16 Processi ed utilizzo delle risorse del sistema. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .104<br />

384.17 Risoluzione di problemi di una certa entità . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107<br />

384.18 Caratteri speciali . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109<br />

384.19 Documentazione di aiuto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110<br />

385 Reti e rete Internet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112<br />

327


385.1 Generalità e configurazione di una rete locale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112<br />

385.2 FTP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116<br />

385.3 Telnet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .118<br />

385.4 SSH. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .122<br />

385.5 La condivisione delle risorse in rete . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122<br />

385.6 Terminali . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124<br />

385.7 Server web (Apache, ecc.) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125<br />

385.8 Samba . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127<br />

385.9 Plip . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130<br />

385.10 Connessione al provider - ppp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130<br />

385.11 Connessione remota tra macchine . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143<br />

385.12 Posta elettronica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145<br />

385.13 Navigatori (browser) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153<br />

385.14 Gruppi di discussione (newsgroup) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154<br />

385.15 IRC - chat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .157<br />

385.16 Scaricamento file - riproduzione (mirror) di siti . . . . . . . . . . . . . . . . . . . . . . . . . . 157<br />

386 Programmi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160<br />

386.1 Installazione ed esecuzione programmi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160<br />

386.2 File ‘core’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162<br />

386.3 I pacchetti - generalità . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163<br />

386.4 I pacchetti - ‘tar.gz’, ‘tgz’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164<br />

386.5 I pacchetti - ‘deb’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166<br />

386.6 I pacchetti - RPM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171<br />

386.7 La compressione - ‘.gz’ ‘.bz2’ ‘.zip’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172<br />

387 X . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174<br />

387.1 Avvio, conclusione, configurazione e risoluzione . . . . . . . . . . . . . . . . . . . . . . . . . . 174<br />

387.2 Gnome . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .181<br />

387.3 X - Gestori delle finestre (window manager) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182<br />

387.4 Terminale grafico (‘xterm’) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .184<br />

388 Applicazioni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187<br />

388.1 Editor di testi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187<br />

388.2 Applicazioni per comunicazioni telefoniche . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188<br />

388.3 Applicazioni fax. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .188<br />

388.4 Applicazioni grafica bidimensionale non vettoriale . . . . . . . . . . . . . . . . . . . . . . . . . 188<br />

388.5 Applicazioni matematiche . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189<br />

388.6 Applicazioni base di dati (database) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .189<br />

389 Stampa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192<br />

328


389.1 Impostazioni generali . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192<br />

389.2 Stampa da riga di comando . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194<br />

390 Editoria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195<br />

390.1 Conversione tra formati . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195<br />

390.2 I file PostScript (‘.ps’) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195<br />

390.3 I file ‘.pdf’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195<br />

390.4 I File ‘.sgml’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195<br />

390.5 I file ‘.doc’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196<br />

390.6 Compatibilità formati GNU/Linux-Dos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196<br />

391 I Caratteri (font) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197<br />

391.1 Impostazioni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197<br />

392 Immagini . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200<br />

392.1 Generalità . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200<br />

393 Masterizzazione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202<br />

393.1 Masterizzare da riga di comando . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202<br />

394 Audio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208<br />

394.1 Impostazioni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208<br />

394.2 CD audio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210<br />

394.3 mp3, wav, ecc. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210<br />

395 Programmazione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218<br />

395.1 Script per la shell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218<br />

395.2 Linguaggio C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227<br />

395.3 Pascal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227<br />

395.4 Cobol . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228<br />

395.5 Perl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .228<br />

396 Computer portatili . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230<br />

396.1 Problematiche di installazione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230<br />

397 Hardware . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232<br />

397.1 Processori . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232<br />

397.2 Monitor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232<br />

397.3 Dispositivi video (scheda, telecamera, ecc.) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232<br />

397.4 Tastiera . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233<br />

397.5 Mouse. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .237<br />

397.6 Disco fisso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238<br />

397.7 CD-ROM e masterizzatori . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238<br />

329


397.8 Schede di rete . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243<br />

397.9 Scheda audio. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .244<br />

397.10 Lettori ZIP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250<br />

397.11 Nastri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252<br />

397.12 RAM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253<br />

397.13 Scanner . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254<br />

397.14 Modem. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .254<br />

397.15 Stampante . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255<br />

397.16 Periferiche SCSI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257<br />

397.17 Unità disco . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260<br />

397.18 Periferiche Plug & Play . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260<br />

397.19 Porte (seriale, parallela, PS/2, USB, ecc.) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .261<br />

Indice analitico del volume . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264<br />

398 Licenza GNU GPL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6<br />

399 Traduzione della licenza GNU GPL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12<br />

400 Licenza GNU LGPL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .18<br />

401 Licenza GNU FDL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26<br />

402 Licenza Artistic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32<br />

403 Licenza UCB BSD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34<br />

404 Licenza MIT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .35<br />

405 Licenza LPPL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36<br />

406 Licenza QPL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39<br />

407 Licenza SSLeay . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41<br />

408 Licenza DSL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43<br />

409 Licenza Open publication license . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46<br />

410 Licenze e altri dettagli sul s<strong>of</strong>tware citato . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49<br />

330


Manifesto GNU<br />

Testo originale: <br />

Traduzione originale: <br />

Copyright (C) 1985, 1993 Free S<strong>of</strong>tware Foundation, Inc.<br />

Capitolo 371<br />

non modificabile<br />

Chiunque è autorizzato a fare o distribuire copie letterali di questo documento, con qualsiasi<br />

mezzo, a condizione che siano riprodotte la nota sul copyright e la nota di autorizzazione, e che<br />

il distributore conceda al destinatario l’autorizzazione di ridistribuirlo a sua volta alle condizioni<br />

permesse da questa nota.<br />

Non sono consentite versioni modificate.<br />

Il Manifesto GNU<br />

Il Manifesto GNU (che appare sotto) venne scritto da Richard Stallman all’inizio<br />

del progetto GNU, per sollecitare sostegno e partecipazione. Durante i primi anni il<br />

manifesto venne lievemente aggiornato per tener conto degli sviluppi, ma adesso la<br />

scelta migliore sembra essere quella di lasciarlo immutato nella forma in cui molti lo<br />

hanno visto.<br />

Da allora abbiamo preso atto di alcuni fraintendimenti che si potrebbero evitare con<br />

una diversa scelta di termini. Le note in calce aggiunte nel 1993 aiutano a chiarire<br />

questi punti.<br />

Per informazioni aggiornate sulla disponibilità del s<strong>of</strong>tware GNU, consultate quelle<br />

disponibili sul nostro server web, in particolare il nostro elenco del s<strong>of</strong>tware.<br />

Cos’è GNU? Gnu Non è Unix!<br />

GNU, che sta per "Gnu’s Not Unix" (Gnu Non è Unix), è il nome del sistema s<strong>of</strong>tware completo<br />

e Unix-compatibile che sto scrivendo per distribuirlo liberamente a chiunque lo possa utilizzare. 1<br />

Molti altri volontari mi stanno aiutando. Abbiamo gran necessità di contributi in tempo, denaro,<br />

programmi e macchine.<br />

Fino ad ora abbiamo un editor Emacs fornito di Lisp per espanderne i comandi, un debugger simbolico,<br />

un generatore di parser compatibile con yacc, un linker e circa 35 utility. È quasi pronta<br />

una shell (interprete di comandi). Un nuovo compilatore C portabile e ottimizzante ha compilato<br />

se stesso e potrebbe essere pubblicato quest’anno. Esiste un inizio di kernel, ma mancano molte<br />

delle caratteristiche necessarie per emulare Unix. Una volta terminati il kernel e il compilatore<br />

sarà possibile distribuire un sistema GNU utilizzabile per lo sviluppo di programmi. Useremo<br />

1 La scelta delle parole è stata poco accurata. L’intenzione era che nessuno dovesse pagare per il *permesso* di usare<br />

il sistema GNU. Ma le parole non lo esprimono chiaramente, e la gente le interpreta spesso come asserzione che GNU<br />

debba sempre essere distribuito in forma gratuita o a basso prezzo. Non è mai stato questo l’intento; più oltre il manifesto<br />

parla della possibile esistenza di aziende che forniscano il servizio di distribuzione a scopo di lucro. Di conseguenza ho<br />

imparato a distinguere tra "free" nel senso di libero e "free" nel senso di gratuito. Il s<strong>of</strong>tware libero è il s<strong>of</strong>tware che gli<br />

utenti sono liberi di distribuire e modificare. Alcuni lo avranno gratuitamente, altri dovranno pagare per ottenere le loro<br />

copie, e se dei finanziamenti aiutano a migliorare il s<strong>of</strong>tware tanto meglio. La cosa importante è che chiunque ne abbia<br />

una copia sia libero di cooperare con altri nell’usarlo.<br />

331


332 volume VIII Argomenti avanzati e accessori<br />

TeX come formattatore di testi, ma lavoriamo anche su un nr<strong>of</strong>f. Useremo inoltre il sistema a<br />

finestre portabile libero X. Dopo di che aggiungeremo un Common Lisp portabile, il gioco Empire,<br />

un foglio elettronico e centinaia di altre cose, oltre alla documentazione in linea. Speriamo<br />

di fornire, col tempo, tutte le cose utili che normalmente si trovano in un sistema Unix, ed anche<br />

di più.<br />

GNU sarà in grado di far girare programmi Unix, ma non sarà identico a Unix. Apporteremo tutti<br />

i miglioramenti che sarà ragionevole fare basandoci sull’esperienza maturata con altri sistemi<br />

operativi. In particolare abbiamo in programma nomi più lunghi per i file, numeri di versione per<br />

i file, un filesystem a prova di crash, forse completamento automatico dei nomi dei file, supporto<br />

indipendente dal terminale per la visualizzazione e forse col tempo un sistema a finestre basato<br />

sul Lisp, attraverso il quale più programmi Lisp e normali programmi Unix siano in grado di<br />

condividere lo schermo. Sia C che Lisp saranno linguaggi per la programmazione di sistema. Per<br />

le comunicazioni vedremo di supportare UUCP, Chaosnet del MIT ed i protocolli di Internet.<br />

GNU è inizialmente orientato alle macchine della classe 68000/16000 con memoria virtuale,<br />

perché sono quelle su cui è più facile farlo girare. Lasceremo agli interessati il lavoro necessario<br />

a farlo girare su macchine più piccole.<br />

Vi preghiamo, per evitare confusioni, di pronunciare la ’G’ nella parola ’GNU’ quando indica il<br />

nome di questo progetto. [N.d.T.: questa avvertenza serve ad evitare che in inglese "GNU" sia<br />

pronunciato come la parola "new"].<br />

Perché devo scrivere GNU<br />

Io credo che il punto fondamentale sia che, se a me piace un programma, io debba condividerlo<br />

con altre persone a cui piace. I venditori di s<strong>of</strong>tware usano il criterio "divide et impera" con<br />

gli utenti, facendo sì che non condividano il s<strong>of</strong>tware con altri. Io mi rifiuto di spezzare così<br />

la solidarietà con gli altri utenti. La mia coscienza non mi consente di firmare un accordo per<br />

non rivelare informazioni o per una licenza d’uso del s<strong>of</strong>tware. Ho lavorato per anni presso<br />

il laboratorio di intelligenza artificiale per resistere a queste tendenze e ad altri atteggiamenti<br />

sgradevoli, ma col tempo queste sono andate troppo oltre: non potevo rimanere in una istituzione<br />

dove ciò viene fatto a mio nome contro la mia volontà.<br />

Per poter continuare ad usare i computer senza disonore, ho deciso di raccogliere un corpus di<br />

s<strong>of</strong>tware libero in modo da andare avanti senza l’uso di alcun s<strong>of</strong>tware che non sia libero. Mi<br />

sono dimesso dal laboratorio di Intelligenza Artificiale per togliere al MIT ogni scusa legale che<br />

mi impedisca di distribuire GNU.<br />

Perché GNU sarà compatibile con Unix<br />

Unix non è il mio sistema ideale, ma non è poi così male. Le caratteristiche essenziali di Unix<br />

paiono essere buone e penso di poter colmare le lacune di Unix senza rovinarne le caratteristiche.<br />

E adottare un sistema compatibile con Unix può risultare pratico anche per molti altri.<br />

Come sarà reso disponibile GNU<br />

GNU non è di pubblico dominio. A tutti sarà permesso di modificare e ridistribuire GNU, ma a<br />

nessun distributore sarà concesso di porre restrizioni sulla sua ridistribuzione. Questo vuol dire<br />

che non saranno permesse modifiche proprietarie. Voglio essere sicuro che tutte le versioni di<br />

GNU rimangano libere.<br />

Perché molti altri programmatori desiderano essere d’aiuto<br />

Ho trovato molti altri programmatori molto interessati a GNU che vogliono dare una mano.<br />

Molti programmatori sono scontenti della commercializzazione del s<strong>of</strong>tware di sistema. Li può<br />

aiutare a far soldi, ma li costringe in generale a sentirsi in conflitto con gli altri programmatori,


Manifesto GNU 333<br />

invece che solidali. L’atto di amicizia fondamentale tra programmatori è condividere programmi;<br />

le politiche di commercializzazione attualmente in uso essenzialmente proibiscono ai programmatori<br />

di trattare gli altri come amici. Gli acquirenti del s<strong>of</strong>tware devono decidere tra l’amicizia<br />

e l’obbedienza alle leggi. Naturalmente molti decidono che l’amicizia è più importante. Ma quelli<br />

che credono nella legge non si sentono a proprio agio con queste scelte. Diventano cinici e<br />

pensano che programmare sia solo un modo per fare soldi.<br />

Lavorando e utilizzando GNU invece che programmi proprietari, possiamo comportarci amichevolmente<br />

con tutti e insieme rispettare la legge. Inoltre GNU è un esempio che ispira gli altri e<br />

una bandiera che li chiama a raccolta perché si uniscano a noi nel condividere il s<strong>of</strong>tware. Questo<br />

ci può dare una sensazione di armonia che sarebbe irraggiungibile se usassimo s<strong>of</strong>tware che non<br />

sia libero. Per circa la metà dei programmatori che conosco è una soddisfazione importante, che<br />

il denaro non può sostituire.<br />

Come si può contribuire<br />

Chiedo ai produttori di computer donazioni in denaro e macchine, ed ai privati donazioni in<br />

programmi e lavoro.<br />

Donare delle macchine può far sì che su di esse giri ben presto GNU. Le macchine devono<br />

essere sistemi completi e pronti all’uso approvati per l’utilizzo in aree residenziali e non devono<br />

richiedere raffreddamento o alimentazione di tipo s<strong>of</strong>isticato.<br />

Ho conosciuto moltissimi programmatori desiderosi di contribuire a GNU part-time. Per la gran<br />

parte dei progetti, un lavoro part-time distribuito risulterebbe troppo difficile da coordinare, perché<br />

le varie parti scritte indipendentemente non funzionerebbero insieme. Ma per scrivere un<br />

sostituto di Unix questo problema non si pone, perché un sistema Unix completo contiene centinaia<br />

di programmi di servizio, ognuno con la propria documentazione separata, e con gran parte<br />

delle specifiche di interfaccia date dalla compatibilità con Unix. Se ogni partecipante scrive un<br />

solo programma da usare al posto di una utility di Unix, il quale funzioni correttamente al posto<br />

dell’originale su un sistema Unix, allora questi programmi funzioneranno bene una volta messi<br />

assieme. Anche considerando qualche imprevisto dovuto a Murphy, assemblare tali componenti<br />

è un lavoro fattibile. Il kernel invece richiederà una più stretta cooperazione, e verrà sviluppato<br />

da un gruppo piccolo e affiatato.<br />

Donazioni in denaro possono mettermi in grado di assumere alcune persone a tempo pieno o parttime.<br />

Lo stipendio non sarà alto rispetto agli standard dei programmatori, ma io cerco persone<br />

per le quali lo spirito della comunità GNU sia importante quanto il denaro. Io lo vedo come un<br />

modo di permettere a degli appassionati di dedicare tutte le loro energie al lavoro su GNU senza<br />

essere costretti a guadagnarsi da vivere in un altro modo.<br />

Perché tutti gli utenti dei computer ne trarranno beneficio<br />

Una volta scritto GNU, ognuno potrà avere liberamente del buon s<strong>of</strong>tware di sistema, così come<br />

può avere l’aria. 2<br />

Questo significa molto di più che far risparmiare a ciascuno il costo di una licenza Unix: vuol dire<br />

evitare l’inutile spreco di ripetere ogni volta lo sforzo della programmazione di sistema. Queste<br />

energie possono essere invece impiegate ad avanzare lo stato dell’arte.<br />

I sorgenti completi del sistema saranno a disposizione di tutti. Di conseguenza, un utente che<br />

abbia necessità di apportare dei cambiamenti al sistema sarà sempre in grado di farlo da solo o<br />

di commissionare i cambiamenti ad un programmatore o ad un’impresa. Gli utenti non saranno<br />

2 Questo è un altro punto dove non sono riuscito a distinguere chiaramente tra i due significati di "free". La frase, così<br />

com’è, non è falsa, si possono ottenere gratuitamente copie del s<strong>of</strong>tware GNU, o dagli amici o attraverso la rete. Ma in<br />

effetti suggerisce un’idea sbagliata.


334 volume VIII Argomenti avanzati e accessori<br />

più in balia di un solo programmatore o di una impresa che, avendo la proprietà esclusiva dei<br />

sorgenti, sia la sola a poter fare le modifiche.<br />

Le scuole avranno la possibilità di fornire un ambiente molto più educativo, incoraggiando gli<br />

studenti a studiare e migliorare il s<strong>of</strong>tware di sistema. I laboratori di informatica di Harvard avevano<br />

una politica per cui nessun programma poteva essere installato nel sistema senza che i sorgenti<br />

fossero pubblicamente consultabili, e la praticarono rifiutandosi effettivamente di installare<br />

alcuni programmi. Questo comportamento mi è stato di grande ispirazione.<br />

Infine, scompariranno le necessità burocratiche di tener conto di chi sia il proprietario del<br />

s<strong>of</strong>tware di sistema e di chi abbia il diritto di farci cosa.<br />

Ogni sistema per imporre tariffe d’uso di un programma, comprese le licenze d’uso per le copie,<br />

è sempre estremamente costoso in termini sociali a causa del complesso meccanismo necessario<br />

per decidere quanto (cioè per quali programmi) ognuno debba pagare, e solo uno stato di polizia<br />

può costringere tutti all’obbedienza. Immaginate una stazione spaziale dove l’aria deve essere<br />

prodotta artificialmente ad un costo elevato: far pagare ogni litro d’aria consumato può essere<br />

giusto, ma indossare la maschera col contatore tutto il giorno e tutta la notte è intollerabile,<br />

anche se tutti possono permettersi di pagare la bolletta. E le videocamere poste in ogni dove per<br />

controllare che nessuno si tolga mai la maschera sono <strong>of</strong>fensive. Meglio finanziare l’impianto di<br />

ossigenazione con una tassa pro capite e buttar via le maschere.<br />

Copiare un programma in tutto o in parte è tanto naturale per un programmatore quanto respirare<br />

ed è altrettanto produttivo. Dovrebbe essere altrettanto libero.<br />

Alcune obiezioni facilmente confutabili agli obiettivi GNU<br />

«La gente non lo userà se è gratuito, perché non potrà avere l’assistenza.»<br />

«Un programma deve essere a pagamento, per poter fornire supporto adeguato.»<br />

Se la gente preferisse pagare per GNU più l’assistenza piuttosto che avere GNU gratis senza<br />

assistenza, allora un’impresa che fornisse assistenza a chi si è procurato GNU gratis potrebbe<br />

operare con pr<strong>of</strong>itto. 3<br />

Si deve distinguere tra il supporto sotto forma di lavoro di programmazione e la semplice gestione.<br />

Il primo non è ottenibile da un venditore di s<strong>of</strong>tware. Se il problema non è sentito da un<br />

numero sufficiente di clienti allora il venditore dirà al cliente di arrangiarsi.<br />

Per chi deve poter contare su questo tipo di supporto l’unica soluzione è di disporre dei sorgenti<br />

e degli strumenti necessari, in modo da poter commissionare il lavoro a chi sia disposto a farlo,<br />

invece che rimanere in balia di qualcuno. Con Unix il prezzo dei sorgenti rende ciò improponibile<br />

per la maggior parte delle imprese. Con GNU questo sarà invece facile. Si darà sempre il caso<br />

che non siano disponibili persone competenti, ma questo non potrà essere imputato al sistema di<br />

distribuzione. GNU non elimina tutti i problemi del mondo, solo alcuni.<br />

Allo stesso tempo, gli utenti che non sanno nulla di computer hanno bisogno di manutenzione,<br />

cioè di cose che potrebbero fare facilmente da soli ma che non sono in grado di fare.<br />

Servizi di questo genere potrebbero essere forniti da aziende che vendono solo gestione e manutenzione.<br />

Se è vero che gli utenti sono disposti a pagare per un prodotto con servizio, allora<br />

saranno anche disposti a pagare per il servizio avendo avuto il prodotto gratuitamente. Le aziende<br />

di servizi si faranno concorrenza sul prezzo e sulla qualità; gli utenti d’altra parte non saranno<br />

legati a nessuna di esse in particolare. Nel frattempo, coloro che non avranno bisogno del servizio<br />

saranno sempre in grado di usare il programma senza pagare il servizio.<br />

3 Adesso esistono effettivamente molte ditte di questo tipo.


Manifesto GNU 335<br />

«Non si può raggiungere molta gente senza pubblicità, e per finanziarla si deve far pagare il<br />

programma.»<br />

«È inutile reclamizzare un programma gratuito.»<br />

Ci sono molte forme di pubblicità gratuita o a basso costo che possono essere usate per informare<br />

un gran numero di utenti di computer riguardo a cose come GNU. Ma può essere vero che la<br />

pubblicità può raggiungere molti più utenti di microcomputer. Se fosse veramente così, una ditta<br />

che reclamizzasse il servizio di copia e spedizione per posta di GNU a pagamento dovrebbe<br />

aver abbastanza successo commerciale da rientrare dai costi della pubblicità e da guadagnarci. In<br />

questo modo, pagano la pubblicità solo gli utenti che ne beneficiano.<br />

D’altro canto, se molta gente ottiene GNU da amici e queste aziende non hanno successo, vorrà<br />

dire che la pubblicità non era necessaria per diffondere GNU. Perché tutti questi difensori del<br />

libero mercato non vogliono lasciare che sia il libero mercato a decidere? 4<br />

«La mia azienda ha bisogno di un sistema operativo proprietario per essere più avanti della<br />

concorrenza.»<br />

Con GNU, i sistemi operativi non rientreranno più fra gli elementi di concorrenza. La vostra<br />

azienda non potrà essere concorrenziale in quest’area, ma egualmente non potranno esserlo i<br />

concorrenti. Vi farete concorrenza in altre aree, mentre in questa godrete di mutui benefici. Se<br />

vendete sistemi operativi non apprezzerete GNU, ma è un problema vostro. Se avete un’attività<br />

di altro tipo, GNU vi può evitare di essere spinti nel costoso campo della vendita di sistemi<br />

operativi.<br />

Mi piacerebbe che lo sviluppo di GNU fosse sostenuto da donazioni da parte di numerosi<br />

produttori ed utenti, riducendo così la spesa per tutti. 5<br />

«Ma i programmatori non meritano una ricompensa per la loro creatività?»<br />

Se qualcosa merita una ricompensa questo è il contribuire al bene sociale. La creatività può essere<br />

un contributo al bene sociale, ma solo nella misura in cui la società è libera di usarne i risultati.<br />

Se i programmatori meritano una ricompensa per la creazione di programmi innovativi, allora<br />

con la stessa logica meritano una punizione se pongono restrizioni all’uso di questi programmi.<br />

«Un programmatore non dovrebbe poter chiedere una ricompensa per la sua creatività?»<br />

Non c’è niente di male nel chiedere di esser pagati per il proprio lavoro, o mirare ad incrementare<br />

le proprie entrate, fintanto che non si utilizzino metodi che siano distruttivi. Ma i metodi comuni<br />

nel campo del s<strong>of</strong>tware, al giorno d’oggi, sono distruttivi.<br />

Spremere denaro dagli utenti di un programma imponendo restrizioni sull’uso è distruttivo perché<br />

riduce i modi in cui il programma può essere usato. Questo diminuisce la quantità di ricchezza<br />

che l’umanità ricava dal programma. Quando c’è una scelta deliberata di porre restrizioni, le<br />

conseguenze dannose sono distruzione deliberata.<br />

La ragione per cui un buon cittadino non usa questi metodi distruttivi per diventare più ricco è che,<br />

se lo facessero tutti, diventeremmo tutti più poveri a causa delle distruzioni reciproche. Questa<br />

è etica kantiana, la Regola Aurea: poiché non mi piacciono le conseguenze che risulterebbero<br />

se tutti impedissero l’accesso alle informazioni, devo considerare sbagliato che uno lo faccia.<br />

4 La Free S<strong>of</strong>tware Foundation raccoglie la maggior parte dei suoi fondi da un servizio di distribuzione, anche se<br />

è più un ente senza fini di lucro che un’azienda. Se *nessuno* sceglie di ottenere copie del s<strong>of</strong>tware ordinandole alla<br />

FSF, questa sarà impossibilitata a proseguire la propria opera. Ma questo non vuole dire che siano giustificate restrizioni<br />

proprietarie per costringere gli utenti a pagare. Se una piccola frazione degli utenti ordina le sue copie dalla FSF, questo<br />

sarà sufficiente per tenerla a galla. Quindi chiediamo agli utenti di aiutarci in questo modo. Hai fatto la tua parte?<br />

5 Un gruppo di imprese di s<strong>of</strong>tware ha recentemente costituito dei finanziamenti per sostenere la manutenzione del<br />

nostro compilatore C.


336 volume VIII Argomenti avanzati e accessori<br />

In particolare, il desiderio di una ricompensa per la propria creatività non giustifica il privare il<br />

mondo nel suo insieme di tutta o parte di questa creatività.<br />

«Ma i programmatori non moriranno di fame?»<br />

Potrei rispondere che nessuno è obbligato a fare il programmatore. La maggior parte di noi non è<br />

in grado di andare per strada a fare il mimo, ma ciò non vuol dire che siamo condannati a passare<br />

la vita per strada a fare i mimi, e morire di fame. Facciamo un altro lavoro.<br />

Ma è la risposta sbagliata, perché accetta l’assunzione implicita di chi pone la domanda, e cioè<br />

che senza proprietà del s<strong>of</strong>tware non è possibile pagare ai programmatori il becco di un quattrino.<br />

Un’assunzione del tipo tutto o niente.<br />

La vera ragione per cui i programmatori non moriranno di fame è che sarà per loro egualmente<br />

possibile essere pagati per programmare, solo non pagati così tanto come ora.<br />

Porre restrizioni sulle copie non è l’unico modello di affari nel campo del s<strong>of</strong>tware. È il modello<br />

più comune perché è il più redditizio. Se fosse vietato, o rifiutato dagli utenti, l’industria del<br />

s<strong>of</strong>tware si sposterebbe su altri modelli organizzativi, adottandone altri ora meno comuni. Ci<br />

sono sempre numerosi modi per organizzare un qualunque tipo di affari.<br />

Probabilmente programmare nel nuovo modello organizzativo non sarà più così redditizio come<br />

lo è ora. Ma questo non è un argomento contro il cambiamento. Che gli addetti alle vendite<br />

ricevano i salari che ora ricevono non è considerata un’ingiustizia. Se i programmatori avessero<br />

gli stessi stipendi (in pratica guadagnerebbero molto di più), non sarebbe nemmeno quella<br />

un’ingiustizia.<br />

«Ma le persone non hanno diritto di controllare come la loro creatività viene usata?»<br />

Il "controllo sull’uso delle proprie idee" in realtà costituisce un controllo sulle vite degli altri; e<br />

di solito viene usato per rendere più difficili le loro vite.<br />

Le persone che hanno studiato con cura i vari aspetti del diritto alla proprietà intellettuale (come<br />

gli avvocati) dicono che non c’è alcun diritto intrinseco alla proprietà intellettuale. I tipi dei<br />

supposti diritti alla proprietà intellettuale riconosciuti dal governo furono creati da specifici atti<br />

legislativi per scopi specifici.<br />

Per esempio la legislazione sui brevetti fu introdotta per incoraggiare gli inventori a rivelare i<br />

dettagli delle loro invenzioni. Lo scopo era avvantaggiare la società più che avvantaggiare gli<br />

inventori. A quel tempo la validità di 17 anni per un brevetto era breve se confrontata con la<br />

velocità di avanzamento dello stato dell’arte. Poiché i brevetti riguardano solo i produttori, per<br />

i quali il costo e lo sforzo degli accordi di licenza sono piccoli in confronto all’organizzazione<br />

della produzione, spesso i brevetti non costituiscono un gran danno. E non ostacolano la gran<br />

parte degli individui che usano prodotti coperti da brevetto.<br />

L’idea del copyright non esisteva in tempi antichi, quando gli autori copiavano estesamente altri<br />

autori in opere non narrative. Questa pratica era utile, ed è il solo modo attraverso cui almeno<br />

parte del lavoro di alcuni autori è sopravvissuto. La legislazione sul copyright fu creata espressamente<br />

per incoraggiare l’originalità. Nel campo per cui fu inventata, cioè i libri, che potevano<br />

essere copiati a basso costo solo con apparecchiature tipografiche, non fece molto danno e non<br />

pose ostacoli alla maggior parte dei lettori.<br />

Tutti i diritti di proprietà intellettuale sono solo licenze concesse dalla società perché si riteneva,<br />

correttamente o meno, che concederle avrebbe giovato alla società nel suo complesso. Ma data<br />

una situazione particolare dobbiamo chiederci: facciamo realmente bene a concedere queste<br />

licenze? Che atti permettiamo di compiere con esse?


Manifesto GNU 337<br />

Il caso dei programmi ai giorni nostri differisce enormemente da quello dei libri un secolo fa. Il<br />

fatto che la via più facile per passare una copia di un programma sia da persona a persona, che il<br />

programma abbia un codice sorgente ed un codice oggetto che sono cose distinte, ed infine il fatto<br />

che un programma venga usato più che letto e gustato, combinandosi creano una situazione in<br />

cui qualcuno che impone un copyright minaccia la società nel suo insieme, sia materialmente che<br />

spiritualmente, una situazione in cui quel qualcuno non dovrebbe farlo, che la legge lo permetta<br />

o no.<br />

«La competizione fa sì che le cose siano fatte meglio.»<br />

Il paradigma della competizione è la gara: premiando il vincitore incoraggia ognuno a correre più<br />

veloce. Quando veramente il capitalismo funziona in questo modo, fa un buon lavoro; ma chi lo<br />

difende ha torto nell’asserire che agisce sempre così. Se i corridori dimenticano il motivo per cui<br />

è <strong>of</strong>ferto il premio e si concentrano solo sul vincere non curandosi di come, possono trovare altre<br />

strategie, come ad esempio attaccare gli altri concorrenti. Se i corridori si azzuffano, arrivano<br />

tutti in ritardo al traguardo.<br />

Il s<strong>of</strong>tware proprietario e segreto è l’equivalente morale dei corridori che si azzuffano. Triste a<br />

dirsi, l’unico arbitro che abbiamo pare non muovere alcuna obiezione alle zuffe, al più le regolamenta<br />

("ogni dieci metri puoi tirare un pugno"). Dovrebbe invece dividerli e penalizzarli anche<br />

se solo provassero a combattere.<br />

«Ma senza un incentivo economico non smetterebbero tutti di programmare?»<br />

In realtà molta gente programmerebbe senza alcun incentivo economico. Programmare ha un<br />

fascino irresistibile per alcune persone, solitamente per quelli che ci riescono meglio. Non mancano<br />

certo i musicisti pr<strong>of</strong>essionisti che insistono pur non avendo speranza di guadagnarsi da<br />

vivere suonando.<br />

Ma in realtà questa domanda, benché posta spesso, non è appropriata. La paga per i programmatori<br />

non sparirà, semplicemente diminuirà. Quindi la domanda corretta è: «qualcuno si metterà<br />

mai a programmare per un minore incentivo economico?». La mia esperienza dice che sì, ci si<br />

metterà.<br />

Per più di dieci anni molti tra i migliori programmatori del mondo hanno lavorato nel laboratorio<br />

di Intelligenza Artificiale per molti meno soldi di quanti ne avrebbero potuti ricevere in ogni<br />

altro posto. Hanno avuto soddisfazioni non economiche di moltissimi tipi, ad esempio fama e<br />

riconoscenza. E la creatività è anche divertente, un premio di per sé.<br />

Poi molti se ne sono andati quando hanno avuto la possibilità di fare lo stesso interessante lavoro<br />

per un mucchio di soldi.<br />

Ciò che i fatti mostrano è che la gente programma per altre ragioni che non siano il denaro; ma<br />

se viene data la possibilità di fare la stessa cosa per un mucchio di soldi, allora cominceranno ad<br />

aspettarseli e a richiederli. Le organizzazioni che pagano poco sono svantaggiate in confronto a<br />

quelle che pagano molto, ma non sarebbero necessariamente in questa posizione se quelle che<br />

pagano molto fossero bandite.<br />

«Abbiamo un disperato bisogno dei programmatori. Se ci chiedono di smettere di aiutare i nostri<br />

vicini dobbiamo obbedire.»<br />

Non si è mai così disperati da dover obbedire a questo genere di pretese. Ricorda: milioni in<br />

difesa, ma non un centesimo in tributi [N.d.T.: è una famosa frase di George Washington].<br />

«I programmatori devono guadagnarsi da vivere in qualche modo.»<br />

A breve termine è vero. Ma ci sono un’infinità di modi in cui i programmatori possono guadagnarsi<br />

da vivere senza vendere i diritti d’uso dei programmi. Questo metodo è comune ai giorni


338 volume VIII Argomenti avanzati e accessori<br />

nostri perché porta la maggior quantità di denaro a programmatori e aziende, non perché sia l’unica<br />

strada per guadagnarsi da vivere. È facile trovarne altre se se ne vogliono trovare. Ecco una<br />

serie di esempi.<br />

Un produttore che immette sul mercato un nuovo computer pagherà per il porting dei sistemi<br />

operativi sul nuovo hardware.<br />

I servizi a pagamento di insegnamento, gestione e manutenzione possono impiegare dei<br />

programmatori.<br />

Persone con idee nuove possono distribuire i programmi gratuitamente, chiedendo donazioni<br />

agli utenti soddisfatti, o vendendo servizi di gestione. Ho incontrato persone che già lavorano<br />

con successo in questo modo.<br />

Utenti con necessità simili possono formare gruppi e pagare. Un gruppo potrebbe stipulare un<br />

contratto con un’impresa di programmazione per scrivere i programmi che i membri del gruppo<br />

vorrebbero usare.<br />

Tutti i tipi di sviluppo possono essere finanziati da una Tassa per il S<strong>of</strong>tware:<br />

Supponiamo che chiunque compri un computer debba pagare un x per cento del costo del computer<br />

come tassa per il s<strong>of</strong>tware. Il governo girerebbe questi fondi ad un’agenzia come la NSF<br />

[N.d.T.: più o meno l’equivalente del nostro CNR] per impiegarli nello sviluppo del s<strong>of</strong>tware.<br />

Ma se l’acquirente fa lui stesso una donazione per lo sviluppo del s<strong>of</strong>tware, potrebbe ottenere<br />

un credito nei confronti di queste tasse. Potrebbe fare una donazione ad un progetto di sua scelta<br />

-- tipicamente scelto perché spera di usarne i risultati quando questo verrà completato. Potrebbe<br />

ottenere un credito per ogni donazione fatta, fino al valore totale della tassa che dovrebbe pagare.<br />

Il gettito complessivo di questa tassa potrebbe essere deciso dal voto di chi la paga, pesato<br />

secondo l’ammontare pagato.<br />

Le conseguenze:<br />

• La comunità degli utenti di computer sosterrebbe lo sviluppo del s<strong>of</strong>tware.<br />

• La comunità sceglierebbe il livello di sostegno necessario.<br />

• Gli utenti che fossero interessati a sapere su che progetto vengano spesi i loro soldi<br />

avrebbero la possibilità di gestire personalmente la cosa.<br />

Nel lungo periodo, rendere liberi i programmi è un passo verso l’epoca della fine del bisogno,<br />

quando nessuno sarà obbligato a lavorare molto duramente solo per guadagnarsi di che vivere.<br />

La gente sarà libera di dedicarsi ad attività divertenti, come programmare, dopo aver passato le<br />

dieci ore settimanali necessarie in compiti come legiferare, fare consulenza familiare, riparare i<br />

robot e prevedere il moto degli asteroidi. Non ci sarà bisogno di guadagnarsi da vivere con la<br />

programmazione.<br />

Abbiamo già ridotto moltissimo la quantità di lavoro che la società nel suo complesso deve fare<br />

per ottenere la sua produttività attuale, ma poco di questo si è tradotto in benessere per i lavoratori<br />

perché è necessario accompagnare l’attività produttiva con molta attività non produttiva. Le cause<br />

principali sono la burocrazia e gli sforzi a tutto campo contro la concorrenza. Il s<strong>of</strong>tware libero<br />

ridurrà di molto questo drenaggio di risorse nell’area della produzione del s<strong>of</strong>tware. Dobbiamo<br />

farlo affinché i guadagni tecnici in produttività si traducano in meno lavoro per noi.


Il progetto GNU<br />

Testo originale: <br />

Traduzione originale: <br />

Copyright © 1998 Richard Stallman<br />

Capitolo 372<br />

non modificabile<br />

La copia letterale e la distribuzione di questo articolo nella sua integrità sono permesse con ogni<br />

mezzo, a patto che questa nota sia riprodotta.<br />

1<br />

372.1 La prima comunità di condivisione del s<strong>of</strong>tware<br />

Quando cominciai a lavorare nel laboratorio di Intelligenza Artificiale del MIT [N.d.T. Massachusset<br />

Institute <strong>of</strong> Technology] nel 1971, entrai a far parte di una comunità in cui ci si scambiavano<br />

i programmi, che esisteva già da molti anni. La condivisione del s<strong>of</strong>tware non si limitava<br />

alla nostra comunità; è un cosa vecchia quanto i computer, proprio come condividere le ricette è<br />

antico come il cucinare. Ma noi lo facevamo più di quasi chiunque altro.<br />

Il laboratorio di Intelligenza Artificiale usava un sistema operativo a partizione di tempo (timesharing)<br />

chiamato ITS (Incompatible Timesharing System) che il gruppo di hacker 2 del laboratorio<br />

aveva progettato e scritto in linguaggio assembler per il Digital PDP-10, uno dei grossi<br />

elaboratori di quel periodo. Come membro di questa comunità, hacker di sistema nel gruppo<br />

laboratorio, il mio compito era migliorare questo sistema.<br />

Non chiamavamo il nostro s<strong>of</strong>tware "s<strong>of</strong>tware libero", poiché questa espressione ancora non esisteva,<br />

ma si trattava proprio di questo. Quando persone di altre università o di qualche società volevano<br />

convertire il nostro programma per il proprio sistema ed utilizzarlo, erano le benvenute. Se<br />

si vedeva qualcuno usare un programma sconosciuto ed interessante, si poteva sempre chiedere di<br />

vederne il codice sorgente, in modo da poterlo leggere, modificare, o prenderne (cannibalizzarne)<br />

alcune parti per creare un nuovo programma.<br />

372.2 La comunità si dissolve<br />

La situazione cambiò drasticamente all’inizio degli anni ’80 quando la Digital smise di produrre<br />

la serie PDP-10. La sua architettura, elegante e potente negli anni ’60, non poteva essere estesa in<br />

modo naturale ai più grandi spazi di indirizzamento che si stavano rendendo possibili negli anni<br />

’80. Questo significò che quasi tutti i programmi che formavano ITS divennero obsoleti.<br />

La comunità di hacker del laboratorio di Intelligenza Artificiale si era già dissolta non molto<br />

tempo prima. Nel 1981 la Symbolics, nata da una costola del laboratorio stesso, gli aveva sottratto<br />

quasi tutti gli hacker; l’ormai esiguo gruppo rimasto fu dunque incapace di sostenersi (il libro<br />

1 [NdR: il testo che appare qui è stato ritoccato leggermente per correggere alcuni errori evidenti.]<br />

2 L’uso del termine "hacker" nel senso di "pirata" è una confusione di termini creata dai mezzi di informazione. Noi<br />

hacker ci rifiutiamo di riconoscere questo significato, e continuiamo ad utilizzare la parola nel senso di "uno che ami<br />

programmare, e a cui piaccia essere bravo a farlo".<br />

339


340 volume VIII Argomenti avanzati e accessori<br />

"Hackers" di Steve Levy narra questi eventi, oltre a fornire una fedele ricostruzione di questa<br />

comunità ai suoi inizi). Quando il laboratorio di Intelligenza Artificiale nel 1982 acquistò un<br />

nuovo PDP-10, i sistemisti decisero di utilizzare il sistema timesharing non libero della Digital<br />

piuttosto che ITS.<br />

I moderni elaboratori di quell’epoca, come il VAX o il 68020, avevano il proprio sistema operativo,<br />

ma nessuno di questi era libero: si doveva firmare un accordo di non-diffusione persino per<br />

ottenerne una copia eseguibile.<br />

Questo significava che il primo passo per usare un computer era promettere di negare aiuto al<br />

proprio vicino. Una comunità cooperante era vietata. La regola creata dai proprietari di s<strong>of</strong>tware<br />

proprietario era: «se condividi il s<strong>of</strong>tware col tuo vicino sei un pirata. Se vuoi modifiche, pregaci<br />

di farle».<br />

L’idea che la concezione sociale di s<strong>of</strong>tware proprietario -- cioè il sistema che impone che il<br />

s<strong>of</strong>tware non possa essere condiviso o modificato -- sia antisociale, contraria all’etica, semplicemente<br />

sbagliata, può apparire sorprendente a qualche lettore. Ma che altro possiamo dire di un<br />

sistema che si basa sul dividere utenti e lasciarli senza aiuto? Quei lettori che trovano sorprendente<br />

l’idea possono aver data per scontata la concezione sociale di s<strong>of</strong>tware proprietario, o averla<br />

giudicata utilizzando lo stesso metro suggerito dal mercato del s<strong>of</strong>tware proprietario. I produttori<br />

di s<strong>of</strong>tware hanno lavorato a lungo e attivamente per diffondere la convinzione che c’è un solo<br />

modo di vedere la cosa.<br />

Quando i produttori di s<strong>of</strong>tware parlano di "difendere" i propri "diritti" o di "fermare la pirateria",<br />

quello che dicono è in realtà secondario. Il vero messaggio in quelle affermazioni sta nelle<br />

assunzioni inespresse, che essi danno per scontate; vogliono che siano accettate acriticamente.<br />

Esaminiamole, dunque.<br />

Una prima assunzione è che le aziende produttrici di s<strong>of</strong>tware abbiano il diritto naturale indiscutibile<br />

di proprietà sul s<strong>of</strong>tware, e di conseguenza, abbiano controllo su tutti i suoi utenti. Se<br />

questo fosse un diritto naturale, non potremmo sollevare obiezioni, indipendentemente dal danno<br />

che possa recare ad altri. È interessante notare che, negli Stati Uniti, sia la costituzione che<br />

la giurisprudenza rifiutano questa posizione: il diritto d’autore non è un diritto naturale, ma un<br />

monopolio imposto dal governo che limita il diritto naturale degli utenti ad effettuare delle copie.<br />

Un’altra assunzione inespressa è che la sola cosa importante del s<strong>of</strong>tware sia il lavoro che consente<br />

di fare -- vale a dire che noi utenti non dobbiamo preoccuparci del tipo di società in cui ci<br />

è permesso vivere.<br />

Una terza assunzione è che non avremmo s<strong>of</strong>tware utilizzabile (o meglio, che non potremmo mai<br />

avere un programma per fare questo o quell’altro particolare lavoro) se non riconoscessimo ai<br />

produttori il controllo sugli utenti di quei programmi. Questa assunzione avrebbe potuto sembrare<br />

plausibile, prima che il movimento del s<strong>of</strong>tware libero dimostrasse che possiamo scrivere quantità<br />

di programmi utili senza bisogno di metterci dei catenacci.<br />

Se rifiutiamo di accettare queste assunzioni, giudicando queste questioni con comuni criteri di<br />

moralità e di buon senso dopo aver messo al primo posto gli interessi degli utenti, tenendo conto<br />

che gli utenti vengono prima di tutto, arriviamo a conclusioni del tutto differenti. Chi usa un<br />

calcolatore dovrebbe essere libero di modificare i programmi per adattarli alle proprie necessità,<br />

ed essere libero di condividere il s<strong>of</strong>tware, poiché aiutare gli altri è alla base della società.<br />

Non c’è modo in questa sede di trattare appr<strong>of</strong>onditamente i ragionamenti che portano a questa<br />

conclusione; il lettore interessato può cercare le informazioni in rete a questo indirizzo: .


Il progetto GNU 341<br />

372.3 Una difficile scelta morale<br />

Una volta che il mio gruppo si fu sciolto, continuare come prima fu impossible. Mi trovai di<br />

fronte ad una difficile scelta morale.<br />

La scelta facile sarebbe stata quella di unirsi al mondo del s<strong>of</strong>tware proprietario, firmando accordi<br />

di non-diffusione e promettendo di non aiutare i miei compagni hacker. Con ogni probabilità<br />

avrei anche sviluppato s<strong>of</strong>tware che sarebbe stato distribuito secondo accordi di non-diffusione,<br />

contribuendo così alla pressione su altri perché a loro volta tradissero i propri compagni.<br />

In questo modo avrei potuto guadagnare, e forse mi sarei divertito a programmare. Ma sapevo<br />

che al termine della mia carriera mi sarei voltato a guardare indietro, avrei visto anni spesi a<br />

costruire muri per dividere le persone, e avrei compreso di aver contribuito a rendere il mondo<br />

peggiore.<br />

Avevo già sperimentato cosa significasse un accordo di non diffusione per chi lo firmava, quando<br />

qualcuno rifiutò a me e al laboratorio AI del MIT il codice sorgente del programma di controllo<br />

della nostra stampante; l’assenza di alcune funzionalità nel programma rendeva oltremodo frustrante<br />

l’uso della stampante. Per cui non mi potevo dire che gli accordi di non-diffusione fossero<br />

innocenti. Ero molto arrabbiato quando quella persona si rifiutò di condividere il programma con<br />

noi; non potevo far finta di niente e fare lo stesso con tutti gli altri.<br />

Un’altra possibile scelta, semplice ma spiacevole, sarebbe stata quella di abbandonare l’informatica.<br />

In tal modo le mie capacità non sarebbero state mal utilizzate, tuttavia sarebbero state<br />

sprecate. Non sarei mai stato colpevole di dividere o imporre restrizioni agli utenti di calcolatori,<br />

ma queste cose sarebbero comunque successe.<br />

Allora cercai un modo in cui un programmatore potesse fare qualcosa di buono. Mi chiesi dunque:<br />

c’erano un programma o dei programmi che io potessi scrivere, per rendere nuovamente possibile<br />

l’esistenza di una comunità?<br />

La risposta era semplice: innanzitutto serviva un sistema operativo. Questo è difatti il s<strong>of</strong>tware<br />

fondamentale per iniziare ad usare un computer. Con un sistema operativo si possono fare molte<br />

cose; senza, non è proprio possibile far funzionare il computer. Con un sistema operativo libero,<br />

avremmo potuto avere nuovamente una comunità in cui hacker possono cooperare, e invitare<br />

chiunque ad unirsi al gruppo. E chiunque sarebbe stato in grado di usare un calcolatore, senza<br />

dover cospirare fin dall’inizio per sottrarre qualcosa ai propri amici.<br />

Essendo un programmatore di sistemi, possedevo le competenze adeguate per questo lavoro.<br />

Così, anche se non davo il successo per scontato, mi resi conto di essere la persona giusta per<br />

farlo. Scelsi di rendere il sistema compatibile con Unix, in modo che fosse portabile, e che gli<br />

utenti Unix potessero passare facilmente ad esso. Il nome GNU fu scelto secondo una tradizione<br />

hacker, come acronimo ricorsivo che significa "GNU’s Not Unix" [N.d.T. GNU non è Unix].<br />

Un sistema operativo non si limita solo al suo nucleo, che è proprio il minimo per eseguire<br />

altri programmi. Negli anni ’70, qualsiasi sistema operativo degno di questo nome includeva<br />

interpreti di comandi, assemblatori, compilatori, interpreti di linguaggi, debugger, editor di testo,<br />

programmi per la posta e molto altro. ITS li aveva, Multics li aveva, VMS li aveva e Unix li<br />

aveva. Anche il sistema operativo GNU li avrebbe avuti.<br />

Tempo dopo venni a conoscenza di questa massima, attribuita a Hillel:<br />

3<br />

detto.<br />

3 Essendo ateo, non seguo alcuna guida religiosa, ma a volte mi trovo ad ammirare qualcosa che qualcuno di loro ha


342 volume VIII Argomenti avanzati e accessori<br />

Se non sono per me stesso, chi sarà per me?<br />

E se sono solo per me stesso, che cosa sono?<br />

E se non ora, quando?<br />

La decisione di iniziare il progetto GNU si basò su uno spirito simile.<br />

372.4 "Free" come libero<br />

Il termine "free s<strong>of</strong>tware" [N.d.T. il termine free in inglese significa sia gratuito che libero] a volte<br />

è mal interpretato: non ha niente a che vedere col prezzo del s<strong>of</strong>tware; si tratta di libertà. Ecco,<br />

dunque, la definizione di s<strong>of</strong>tware libero: un programma è s<strong>of</strong>tware libero per un dato utente se:<br />

• l’utente ha la libertà di eseguire il programma per qualsiasi scopo;<br />

• l’utente ha la libertà di modificare il programma secondo i propri bisogni (perché questa<br />

libertà abbia qualche effetto in pratica, è necessario avere accesso al codice sorgente<br />

del programma, poiché apportare modifiche ad un programma senza disporre del codice<br />

sorgente è estremamente difficile);<br />

• l’utente ha la libertà di distribuire copie del programma, gratuitamente o dietro compenso;<br />

• l’utente ha la libertà di distribuire versioni modificate del programma, così che la comunità<br />

possa fruire dei miglioramenti apportati.<br />

Poiché "free" si riferisce alla libertà e non al prezzo, vendere copie di un programma non contraddice<br />

il concetto di s<strong>of</strong>tware libero. In effetti, la libertà di vendere copie di programmi è essenziale:<br />

raccolte di s<strong>of</strong>tware libero vendute su CD-ROM sono importanti per la comunità, e la loro vendita<br />

è un modo di raccogliere fondi importante per lo sviluppo del s<strong>of</strong>tware libero. Di conseguenza,<br />

un programma che non può essere liberamente incluso in tali raccolte non è s<strong>of</strong>tware libero.<br />

A causa dell’ambiguità del termine "free", si è cercata a lungo un’alternativa, ma nessuno ne ha<br />

trovata una valida. La lingua inglese ha più termini e sfumature di ogni altra, ma non ha una<br />

parola semplice e non ambigua che significhi libero; "unfettered" è la parola più vicina come<br />

significato [NdT: unfettered è una parola di tono aulico o arcaico che significa libero da ceppi,<br />

vincoli o inibizioni]. Alternative come "liberated", "freedom" e "open" hanno altri significati o<br />

non sono adatte per altri motivi [NdT: rispettivamente, liberato, libertà, aperto].<br />

372.5 S<strong>of</strong>tware GNU e il sistema GNU<br />

Sviluppare un intero sistema è un progetto considerevole. Per raggiungere l’obiettivo decisi di<br />

adattare e usare parti di s<strong>of</strong>tware libero tutte le volte che fosse possibile. Per esempio, decisi fin<br />

dall’inizio di usare TeX come il principale programma di formattazione di testo; qualche anno<br />

più tardi, decisi di usare l’X Window System piuttosto che scrivere un altro sistema a finestre per<br />

GNU.<br />

A causa di questa decisione, il sistema GNU e la raccolta di tutto il s<strong>of</strong>tware GNU non sono<br />

la stessa cosa. Il sistema GNU comprende programmi che non sono GNU, sviluppati da altre<br />

persone o gruppi di progetto per i propri scopi, ma che possiamo usare in quanto s<strong>of</strong>tware libero.


Il progetto GNU 343<br />

372.6 L’inizio del progetto<br />

Nel gennaio 1984 lasciai il mio posto al MIT e cominciai a scrivere s<strong>of</strong>tware GNU. Dovetti<br />

lasciare il MIT, per evitare che potesse interferire con la distribuzione di GNU come s<strong>of</strong>tware<br />

libero. Se fossi rimasto, il MIT avrebbe potuto rivendicare la proprietà del lavoro, ed avrebbe<br />

potuto imporre i propri termini di distribuzione, o anche farne un pacchetto proprietario. Non<br />

avevo alcuna intenzione di fare tanto lavoro solo per vederlo reso inutilizzabile per il suo scopo<br />

originario: creare una nuova comunità di condivisione di s<strong>of</strong>tware. Ad ogni buon conto, il pr<strong>of</strong>essor<br />

Winston -- allora responsabile del laboratorio AI del MIT -- mi propose gentilmente di<br />

continuare ad utilizzare le attrezzature del laboratorio stesso.<br />

372.7 I primi passi<br />

Poco dopo aver iniziato il progetto GNU, venni a sapere del Free University Compiler Kit, noto<br />

anche come VUCK (la parola olandese che sta per "free" inizia con la V). Era un compilatore<br />

progettato per trattare più linguaggi, fra cui C e Pascal, e per generare codice binario per diverse<br />

architetture. Scrissi al suo autore chiedendo se GNU avesse potuto usarlo. Rispose in modo<br />

canzonatorio, dicendo che l’università era sì libera, ma non il compilatore. Decisi allora che<br />

il mio primo programma per il progetto GNU sarebbe stato un compilatore multilinguaggio e<br />

multipiattaforma.<br />

Sperando di evitare di dover scrivere da me l’intero compilatore, ottenni il codice sorgente del<br />

Pastel, un compilatore multipiattaforma sviluppato ai Laboratori Lawrence Livermore. Il linguaggio<br />

supportato da Pastel, in cui il Pastel stesso era scritto, era una versione estesa del Pascal,<br />

pensata come linguaggio di programmazione di sistemi. Io vi aggiunsi un frontend per il C, e cominciai<br />

il porting per il processore Motorola 68000, ma fui costretto a rinunciare quando scoprii<br />

che il compilatore richiedeva diversi megabyte di memoria sullo stack, mentre il sistema Unix<br />

disponibile per il processore 68000 ne permetteva solo 64K.<br />

Mi resi conto allora che il compilatore Pastel interpretava tutto il file di ingresso creandone un<br />

albero sintattico, convertiva questo in una catena di "istruzioni", e quindi generava l’intero file di<br />

uscita senza mai liberare memoria. A questo punto, conclusi che avei dovuto scrivere un nuovo<br />

compilatore da zero. Quel nuovo compilatore è ora noto come Gcc; non utilizza niente del compilatore<br />

Pastel, ma riuscii ad adattare e riutilizzare il frontend per il C che avevo scritto. Questo<br />

però avvenne qualche anno dopo; prima, lavorai su GNU Emacs.<br />

372.8 GNU Emacs<br />

Cominciai a lavorare su GNU Emacs nel settembre 1984, e all’inizio del 1985 cominciava ad<br />

essere utilizzabile. Così potei iniziare ad usare sistemi Unix per scrivere; fino ad allora, avevo<br />

scritto sempre su altri tipi di macchine, non avendo nessun interesse ad imparare vi né ed.<br />

A questo punto alcuni cominciarono a voler usare GNU Emacs, il che pose il problema di come<br />

distribuirlo. Naturalmente lo misi sul server ftp anonimo del computer che usavo al MIT (questo<br />

computer, prep.ai.mit.edu, divenne così il sito ftp primario di distribuzione di GNU; quando<br />

alcuni anni dopo andò fuori servizio, trasferimmo il nome sul nostro nuovo ftp server). Ma allora<br />

molte delle persone interessate non erano su Internet e non potevano ottenere una copia via ftp,<br />

così mi si pose il problema di cosa dir loro.<br />

Avrei potuto dire: «trova un amico che è in rete disposto a farti una copia». Oppure avrei potuto<br />

fare quel che feci con l’originario Emacs su PDP-10, e cioè dir loro: «spediscimi una busta


344 volume VIII Argomenti avanzati e accessori<br />

affrancata ed un nastro, ed io te lo rispedisco con sopra Emacs». Ma ero senza lavoro, e cercavo<br />

un modo di far soldi con il s<strong>of</strong>tware libero. E così feci sapere che avrei spedito un nastro a chi lo<br />

voleva per 150 dollari. In questo modo, creai un’impresa di distribuzione di s<strong>of</strong>tware libero, che<br />

anticipava le compagnie che oggi distribuiscono interi sistemi GNU basati su Linux.<br />

372.9 Un programma è libero per tutti?<br />

Se un programma è s<strong>of</strong>tware libero quando esce dalle mani del suo autore, non significa necessariamente<br />

che sarà s<strong>of</strong>tware libero per chiunque ne abbia una copia. Per esempio, il s<strong>of</strong>tware<br />

di pubblico dominio (s<strong>of</strong>tware senza copyright) è s<strong>of</strong>tware libero, ma chiunque può farne una<br />

versione modificata proprietaria. Analogamente, molti programmi liberi sono protetti da diritto<br />

d’autore, ma vengono distribuiti con semplici licenze permissive che permettono di farne versioni<br />

modificate proprietarie.<br />

L’esempio emblematico della questione è l’X Window System. Sviluppato al MIT, e pubblicato<br />

come s<strong>of</strong>tware libero con una licenza permissiva, fu rapidamente adottato da diverse società informatiche.<br />

Queste aggiunsero X ai loro sistemi Unix proprietari, solo in forma binaria, e coperto<br />

dello stesso accordo di non-diffusione. Queste copie di X non erano s<strong>of</strong>tware più libero di quanto<br />

lo fosse Unix.<br />

Gli autori dell’X Window System non ritenevano che questo fosse un problema, anzi se lo aspettavano<br />

ed era loro intenzione che accadesse. Il loro scopo non era la libertà, ma semplicemente<br />

il "successo", definito come "avere tanti utenti". Non erano interessati che questi utenti fossero<br />

liberi, ma solo che fossero numerosi.<br />

Questo sfociò in una situazione paradossale, in cui due modi diversi di misurare la quantità di<br />

libertà risultavano in risposte diverse alla domanda «questo programma è libero»? Giudicando<br />

sulla base della libertà <strong>of</strong>ferta dai termini distributivi usati dal MIT, si sarebbe dovuto dire che X<br />

era s<strong>of</strong>tware libero. Ma misurando la libertà dell’utente medio di X, si sarebbe dovuto dire che<br />

X era s<strong>of</strong>tware proprietario. La maggior parte degli utenti di X usavano le versioni proprietarie<br />

fornite con i sistemi Unix, non la versione libera.<br />

372.10 Il permesso d’autore (copyleft) e la GNU GPL<br />

Lo scopo di GNU consisteva nell’<strong>of</strong>frire libertà agli utenti, non solo nell’ottenere ampia diffusione.<br />

Avevamo quindi bisogno di termini di distribuzione che evitassero che il s<strong>of</strong>tware GNU fosse<br />

trasformato in s<strong>of</strong>tware proprietario. Il metodo che usammo si chiama "permesso d’autore".<br />

4<br />

Il permesso d’autore (copyleft) 5 usa le leggi sul diritto d’autore (copyright), ma le capovolge per<br />

ottenere lo scopo opposto: invece che un metodo per privatizzare il s<strong>of</strong>tware, diventa infatti un<br />

mezzo per mantenerlo libero.<br />

Il succo dell’idea di permesso d’autore consiste nel dare a chiunque il permesso di eseguire il programma,<br />

copiare il programma, modificare il programma, e distribuirne versioni modificate, ma<br />

senza dare il permesso di aggiungere restrizioni. In tal modo, le libertà essenziali che definiscono<br />

il "free s<strong>of</strong>tware" (s<strong>of</strong>tware libero) sono garantite a chiunque ne abbia una copia, e diventano<br />

diritti inalienabili.<br />

4 Nel 1984 o 1985, Don Hopkins, persona molto creativa, mi mandò una lettera. Sulla busta aveva scritto diverse<br />

frasi argute, fra cui questa: "Permesso d’autore--tutti i diritti rovesciati". Utilizzai l’espressione "permesso d’autore" per<br />

battezzare il concetto di distribuzione che allora andavo elaborando.<br />

5 [NdT: si tratta di un gioco di parole, che qui viene reso con "permesso di autore": copyright (diritto di autore) è<br />

formato dalle parola "copy" (copia) e "right" (diritto, ma anche destra), opposto di "left" (sinistra, ma anche lasciato).]


Il progetto GNU 345<br />

Perché un permesso d’autore sia efficace, anche le versioni modificate devono essere libere. Ciò<br />

assicura che ogni lavoro basato sul nostro sia reso disponibile per la nostra comunità, se pubblicato.<br />

Quando dei programmatori pr<strong>of</strong>essionisti lavorano su s<strong>of</strong>tware GNU come volontari, è<br />

il permesso d’autore che impedisce ai loro datori di lavoro di dire: «non puoi distribuire quei<br />

cambiamenti, perché abbiamo intenzione di usarli per creare la nostra versione proprietaria del<br />

programma».<br />

La clausola che i cambiamenti debbano essere liberi è essenziale se vogliamo garantire libertà<br />

a tutti gli utenti del programma. Le aziende che privatizzarono l’X Window System di solito<br />

avevano apportato qualche modifica per per portare il programma sui loro sistemi e sulle loro<br />

macchine. Si trattava di modifiche piccole rispetto alla mole di X, ma non banali. Se apportare<br />

modifiche fosse una scusa per negare libertà agli utenti, sarebbe facile per chiunque appr<strong>of</strong>ittare<br />

di questa scusa.<br />

Una problematica correlata è quella della combinazione di un programma libero con codice non<br />

libero. Una tale combinazione sarebbe inevitabilmente non libera; ogni libertà che manchi dalla<br />

parte non libera mancherebbe anche dall’intero programma. Permettere tali combinazioni aprirebbe<br />

non uno spiraglio, ma un buco grosso come una casa. Quindi un requisito essenziale per il<br />

permesso d’autore è tappare il buco: tutto ciò che venga aggiunto o combinato con un programma<br />

protetto da permesso d’autore dev’essere tale che il programma risultante sia anch’esso libero e<br />

protetto da permesso d’autore.<br />

La specifica implementazione di permesso d’autore che utilizziamo per la maggior parte del<br />

s<strong>of</strong>tware GNU è la GNU General Public License (licenza pubblica generica GNU), abbreviata in<br />

GNU GPL. Abbiamo altri tipi di permesso d’autore che sono utilizzati in circostanze specifiche.<br />

I manuali GNU sono anch’essi protetti da permesso d’autore, ma ne usano una versione molto<br />

più semplice, perché per i manuali non è necessaria la complessità della GPL.<br />

372.11 La Free S<strong>of</strong>tware Foundation<br />

Man mano che l’interesse per Emacs aumentava, altre persone parteciparono al progetto GNU,<br />

e decidemmo che era di nuovo ora di cercare finanziamenti. Così nel 1985 fondammo la Free<br />

S<strong>of</strong>tware Foundation (Fondazione per il s<strong>of</strong>tware libero), una organizzazione senza fini di lucro<br />

per lo sviluppo di s<strong>of</strong>tware libero. La FSF fra l’altro si prese carico della distribuzione dei nastri<br />

di Emacs; più tardi estese l’attività aggiungendo sul nastro altro s<strong>of</strong>tware libero (sia GNU che<br />

non GNU) e vendendo manuali liberi.<br />

La FSF accetta donazioni, ma gran parte delle sue entrate è sempre stata costituita dalle vendite:<br />

copie di s<strong>of</strong>tware libero e servizi correlati. Oggi vende CD-ROM di codice sorgente, CD-ROM<br />

di programmi compilati, manuali stampati pr<strong>of</strong>essionalmente (tutti con libertà di ridistribuzione<br />

e modifica), e distribuzioni Deluxe (nelle quali compiliamo l’intera scelta di s<strong>of</strong>tware per una<br />

piattaforma a richiesta).<br />

I dipendenti della Free S<strong>of</strong>tware Foundation hanno scritto e curato la manutenzione di diversi<br />

pacchetti GNU. Fra questi spiccano la libreria C e la shell. La libreria C di GNU è utilizzata da<br />

ogni programma che gira su sistemi GNU/Linux per comunicare con Linux. È stata sviluppata da<br />

un membro della squadra della Free S<strong>of</strong>tware Foundation, Roland McGrath. La shell usata sulla<br />

maggior parte dei sistemi GNU/Linux è Bash, la Bourne Again Shell 6 , che è stata sviluppata da<br />

Brian Fox, dipendente della FSF.<br />

6 "Bourne Again Shell" è un gioco di parole sul nome "Bourne Shell", che era la normale shell di Unix [NdT: "Bourne<br />

again" richiama l’espressione cristiana "born again", "rinato" (in Cristo)].


346 volume VIII Argomenti avanzati e accessori<br />

Finanziammo lo sviluppo di questi programmi perché il progetto GNU non riguardava solo strumenti<br />

di lavoro o un ambiente di sviluppo: il nostro obiettivo era un sistema operativo completo,<br />

e questi programmi erano necessari per raggiungere quell’obiettivo.<br />

372.12 Il supporto per il s<strong>of</strong>tware libero<br />

La filos<strong>of</strong>ia del s<strong>of</strong>tware libero rigetta una diffusa pratica commerciale in particolare, ma non<br />

è contro il commercio. Quando un’impresa rispetta la libertà dell’utente, c’è da augurarle ogni<br />

successo.<br />

La vendita di copie di Emacs esemplifica un modo di condurre affari col s<strong>of</strong>tware libero. Quando<br />

la FSF prese in carico quest’attività, dovetti trovare un’altra fonte di sostentamento. La trovai<br />

nella vendita di servizi relativi al s<strong>of</strong>tware libero che avevo sviluppato, come insegnare argomenti<br />

quali programmazione di Emacs e personalizzazione di GCC, oppure sviluppare s<strong>of</strong>tware,<br />

soprattutto adattamento di GCC a nuove architetture.<br />

Oggi tutte queste attività collegate al s<strong>of</strong>tware libero sono esercitate da svariate aziende. Alcune<br />

distribuiscono raccolte di s<strong>of</strong>tware libero su CD-ROM, altre <strong>of</strong>frono consulenza a diversi livelli,<br />

dall’aiutare gli utenti in difficoltà, alla correzione di errori, all’aggiunta di funzionalità non banali.<br />

Si cominciano anche a vedere aziende di s<strong>of</strong>tware che si fondano sul lancio di nuovi programmi<br />

liberi.<br />

Attenzione, però: diverse aziende che si fregiano del marchio "open source" (s<strong>of</strong>tware aperto) in<br />

realtà fondano le loro attività su s<strong>of</strong>tware non libero che funziona insieme con s<strong>of</strong>tware libero.<br />

Queste non sono aziende di s<strong>of</strong>tware libero, sono aziende di s<strong>of</strong>tware proprietario i cui prodotti<br />

attirano gli utenti lontano dalla libertà. Loro li chiamano "a valore aggiunto", il che riflette i valori<br />

che a loro farebbe comodo che adottassimo: la convenienza prima della libertà. Se noi riteniamo<br />

che la libertà abbia più valore, li dovremmo chiamare prodotti "a libertà sottratta".<br />

372.13 Obiettivi tecnici<br />

L’obiettivo principale di GNU era essere s<strong>of</strong>tware libero. Anche se GNU non avesse avuto alcun<br />

vantaggio tecnico su Unix, avrebbe avuto sia un vantaggio sociale, permettendo agli utenti di<br />

cooperare, sia un vantaggio etico, rispettando la loro libertà.<br />

Tuttavia risultò naturale applicare al lavoro le regole classiche di buona programmazione;<br />

per esempio, allocare le strutture dati dinamicamente per evitare limitazioni arbitrarie sulla<br />

dimensione dei dati, o gestire tutti i possibili codici a 8 bit in tutti i casi ragionevoli.<br />

Inoltre, al contrario di Unix che era pensato per piccole dimensioni di memoria, decidemmo di<br />

non supportare le macchine a 16 bit (era chiaro che le macchine a 32 bit sarebbero state la norma<br />

quando il sistema GNU sarebbe stato completo), e di non preoccuparci di ridurre l’occupazione<br />

di memoria a meno che eccedesse il megabyte. In programmi per i quali non era essenziale la<br />

gestione di file molto grandi, spingemmo i programmatori a leggere in memoria l’intero file di<br />

ingresso per poi analizzare il file senza doversi preoccupare delle operazioni di I/O.<br />

Queste decisioni fecero sì che molti programmi GNU superassero i loro equivalenti Unix sia in<br />

affidabilità che in velocità di esecuzione.


Il progetto GNU 347<br />

372.14 Donazioni di computer<br />

Man mano che la reputazione del progetto GNU andava crescendo, alcune persone iniziarono<br />

a donare macchine su cui girava Unix. Queste macchine erano molto utili, perché il modo più<br />

semplice di sviluppare componenti per GNU era di farlo su di un sistema Unix così da sostituire<br />

pezzo per pezzo i componenti di quel sistema. Ma queste macchine sollevavano anche una<br />

questione etica: se fosse giusto per noi anche solo possedere una copia di Unix.<br />

Unix era (ed è) s<strong>of</strong>tware proprietario, e la filos<strong>of</strong>ia del progetto GNU diceva che non avremmo<br />

dovuto usare s<strong>of</strong>tware proprietario. Ma, applicando lo stesso ragionamento per cui la violenza<br />

è ammessa per autodifesa, conclusi che fosse legittimo usare un pacchetto proprietario, se ciò<br />

fosse stato importante nel crearne un sostituto libero che permettesse ad altri di smettere di usare<br />

quello proprietario.<br />

Tuttavia, benchè fosse un male giustificabile, era pur sempre un male. Oggi non abbiamo più<br />

alcuna copia di Unix, perché le abbiamo sostituite con sistemi operativi liberi. Quando non fu<br />

possibile sostituire il sistema operativo di una macchina con uno libero, sostituimmo la macchina.<br />

372.15 L’elenco dei compiti GNU<br />

Mentre il progetto GNU avanzava, ed un numero sempre maggiore di componenti di sistema<br />

venivano trovati o sviluppati, diventò utile stilare un elenco delle parti ancora mancanti. Usammo<br />

questo elenco per ingaggiare programmatori che scrivessero tali parti, e l’elenco prese il nome<br />

di elenco dei compiti GNU. In aggiunta ai componenti Unix mancanti inserimmo nell’elenco<br />

svariati progetti utili di programmazione o di documentazione che a nostro parere non dovrebbero<br />

mancare in un sistema operativo veramente completo.<br />

Oggi non compare quasi nessun componente Unix nell’elenco dei compiti GNU; tutti questi<br />

lavori, a parte qualcuno non essenziale, sono già stati svolti. D’altro canto l’elenco è pieno di<br />

quei progetti che qualcuno chiamerebbe "applicazioni": ogni programma che interessi ad una<br />

fetta non trascurabile di utenti sarebbe un’utile aggiunta ad un sistema operativo.<br />

L’elenco comprende anche dei giochi, e così è stato fin dall’inizio: Unix comprendeva dei giochi,<br />

perciò era naturale che così fosse anche per GNU. Ma poiché non c’erano esigenze di compatibilità<br />

per i giochi, non ci attenemmo alla scelta di giochi presenti in Unix, preferendo piuttosto<br />

fornire un elenco di diversi tipi di giochi potenzialmente graditi agli utenti.<br />

372.16 La licenza GNU per le librerie<br />

La libreria C del sistema GNU utilizza un tipo speciale di permesso d’autore, la "Licenza Pubblica<br />

GNU per le Librerie" 7 , che permette l’uso della libreria da parte di s<strong>of</strong>tware proprietario.<br />

Perché quest’eccezione?<br />

Non si tratta di questioni di principio: non c’è nessun principio che dica che i prodotti s<strong>of</strong>tware<br />

proprietari abbiano il diritto di includere il nostro codice (perché contribuire ad un progetto<br />

fondato sul rifiuto di condividere con noi?). L’uso della licenza LGPL per la libreria C, o per<br />

qualsiasi altra libreria, è una questione di strategia.<br />

La libreria C svolge una funzione generica: ogni sistema operativo proprietario ed ogni compilatore<br />

includono una libreria C. Di conseguenza, rendere disponibile la nostra libreria C solo<br />

7 [NdT: nel 1999 la FSF ha cambiato nome alla licenza LGPL che ora si chiama "Lesser GPL", GPL attenuata, per<br />

non suggerire che si tratti della forma di licenza preferenziale per le librerie.]


348 volume VIII Argomenti avanzati e accessori<br />

per i programmi liberi non avrebbe dato nessun vantaggio a tali programmi liberi, avrebbe solo<br />

disincentivato l’uso della nostra libreria.<br />

C’è un’eccezione a questa situazione: sul sistema GNU (termine che include GNU/Linux) l’unica<br />

libreria C disponibile è quella GNU. Quindi i termini di distribuzione della nostra libreria C<br />

determinano se sia possibile o meno compilare un programma proprietario per il sistema GNU.<br />

Non ci sono ragioni etiche per permettere l’uso di applicazioni proprietarie sul sistema GNU, ma<br />

strategicamente sembra che impedirne l’uso servirebbe più a scoraggiare l’uso del sistema GNU<br />

che non a incoraggiare lo sviluppo di applicazioni libere.<br />

Ecco perché l’uso della licenza LGPL è una buona scelta strategica per la libreria C, mentre per<br />

le altre librerie la strategia va valutata caso per caso. Quando una libreria svolge una funzione<br />

particolare che può aiutare a scrivere certi tipi di programmi, distribuirla secondo la GPL, quindi<br />

limitandone l’uso ai soli programmi liberi, è un modo per aiutare gli altri autori di s<strong>of</strong>tware libero,<br />

dando loro un vantaggio nei confronti del s<strong>of</strong>tware proprietario.<br />

Prendiamo come esempio GNU-Readline, una libreria scritta per fornire a Bash la modificabilità<br />

della linea di comando: Readline è distribuita secondo la normale licenza GPL, non la LGPL. Ciò<br />

probabilmente riduce l’uso di Readline, ma questo non rappresenta una perdita per noi; d’altra<br />

parte almeno una applicazione utile è stata resa s<strong>of</strong>tware libero proprio al fine di usare Readline,<br />

e questo è un guadagno tangibile per la comunità.<br />

Chi sviluppa s<strong>of</strong>tware proprietario ha vantaggi economici, gli autori di programmi liberi hanno<br />

bisogno di avvantaggiarsi a vicenda. Spero che un giorno possiamo avere una grande raccolta di<br />

librerie coperte dalla licenza GPL senza che esista una raccolta equivalente per chi scrive s<strong>of</strong>tware<br />

proprietario. Tale libreria fornirebbe utili moduli da usare come i mattoni per costruire nuovi<br />

programmi liberi, e costituendo un sostanziale vantaggio per la scrittura di ulteriori programmi<br />

liberi.<br />

372.17 Togliersi il prurito?<br />

Eric Raymond afferma che «ogni buon programma nasce dall’iniziativa di un programmatore<br />

che si vuole togliere un suo personale prurito». È probabile che talvolta succeda così, ma molte<br />

parti essenziali del s<strong>of</strong>tware GNU sono state sviluppate al fine di completare un sistema operativo<br />

libero. Derivano quindi da una idea e da un progetto, non da una necessità contingente.<br />

Per esempio, abbiamo sviluppato la libreria C di GNU perché un sistema di tipo Unix ha bisogno<br />

di una libreria C, la Bourne-Again Shell (bash) perché un sistema di tipo Unix ha bisogno di una<br />

shell, e GNU tar perché un sistema di tipo Unix ha bisogno un programma tar. Lo stesso vale per<br />

i miei programmi: il compilatore GNU, GNU Emacs, GDB, GNU Make.<br />

Alcuni programmi GNU sono stati sviluppati per fronteggiare specifiche minacce alla nostra<br />

libertà: ecco perché abbiamo sviluppato gzip come sostituto per il programma Compress, che la<br />

comunità aveva perduto a causa dei brevetti sull’algoritmo LZW. Abbiamo trovato persone che<br />

sviluppassero LessTif, e più recentemente abbiamo dato vita ai progetti GNOME e Harmony per<br />

affrontare i problemi causati da alcune librerie proprietarie (come descritto più avanti). Stiamo<br />

sviluppando la GNU Privacy Guard per sostituire i diffusi programmi di crittografia non liberi,<br />

perché gli utenti non siano costretti a scegliere tra riservatezza e libertà.<br />

Naturalmente, i redattori di questi programmi sono coinvolti nel loro lavoro, e varie persone<br />

vi hanno aggiunto diverse funzionalità secondo le loro personali necessità ed i loro interessi.<br />

Tuttavia non è questa la ragione dell’esistenza di tali programmi.


Il progetto GNU 349<br />

372.18 Sviluppi inattesi<br />

All’inizio del progetto GNU pensavo che avremmo sviluppato l’intero sistema GNU e poi lo<br />

avremmo reso disponibile tutto insieme, ma le cose non andarono così.<br />

Poiché i componenti del sistema GNU sono stati implementati su un sistema Unix, ognuno di<br />

essi poteva girare su sistemi Unix molto prima che esistesse un sistema GNU completo. Alcuni<br />

di questi programmi divennero diffusi e gli utenti iniziarono ad estenderli e a renderli utilizzabili<br />

su nuovi sistemi: sulle varie versioni di Unix, incompatibili tra loro, e talvolta anche su altri<br />

sistemi.<br />

Questo processo rese tali programmi molto più potenti e attirò finanziamenti e collaboratori al<br />

progetto GNU; tuttavia probabilmente ritardò di alcuni anni la realizzazione di un sistema minimo<br />

funzionante, perché il tempo degli autori GNU veniva impiegato a curare la compatibilità di<br />

questi programmi con altri sistemi e ad aggiungere nuove funzionalità ai componenti esistenti,<br />

piuttosto che a proseguire nella scrittura di nuovi componenti.<br />

372.19 GNU-Hurd<br />

Nel 1990 il sistema GNU era quasi completo, l’unica parte significativa ancora mancante era il<br />

kernel. Avevamo deciso di implementare il nostro kernel come un gruppo di processi server che<br />

girassero sul sistema Mach. Mach è un microkernel sviluppato alla Carnegie Mellon University<br />

e successivamente all’Università dello Utah; GNU Hurd è un gruppo di server (o "herd <strong>of</strong> gnus":<br />

mandria di gnu) che gira su Mach svolgendo le funzioni del kernel Unix. L’inizio dello sviluppo<br />

fu ritardato nell’attesa che Mach fosse reso disponibile come s<strong>of</strong>tware libero, come era stato<br />

promesso.<br />

Una ragione di questa scelta progettuale fu di evitare quella che sembrava la parte più complessa<br />

del lavoro: effettuare il debugging del kernel senza un debugger a livello sorgente. Questo lavoro<br />

era già stato fatto, appunto in Mach, e avevamo previsto di effettuare il debugging dei server<br />

Hurd come programmi utente, con GDB. Ma questa fase si rivelò molto lunga, ed il debugging<br />

dei server multi-thread che si scambiano messaggi si è rivelato estremamente complesso. Per<br />

rendere Hurd robusto furono così necessari molti anni.<br />

372.20 Alix<br />

Originariamente il kernel GNU non avrebbe dovuto chiamarsi Hurd; il suo nome originale era<br />

Alix, come la donna di cui ero innamorato in quel periodo. Alix, che era amministratrice di<br />

sistemi Unix, aveva sottolineato come il suo nome corrispondesse ad un comune schema usato per<br />

battezzare le versioni del sistema Unix: scherzosamente diceva ai suoi amici: «qualcuno dovrebbe<br />

chiamare un kernel come me». Io non dissi nulla ma decisi di farle una sorpresa scrivendo un<br />

kernel chiamato Alix.<br />

Le cose non andarono così. Michael Bushnell (ora Thomas), principale autore del kernel, preferì<br />

il nome Hurd, e chiamò Alix una parte del kernel, quella che serviva a intercettare le chiamate di<br />

sistema e a gestirle inviando messaggi ai server che compongono HURD.<br />

Infine io e Alix ci lasciammo e lei cambiò nome; contemporaneamente la struttura di Hurd veniva<br />

cambiata in modo che la libreria C mandasse messaggi direttamente ai server, e così il componente<br />

Alix scomparve dal progetto. Prima che questo accadesse, però, un amico di Alix si accorse<br />

della presenza del suo nome nel codice sorgente di Hurd e glielo disse. Così il nome raggiunse il<br />

suo scopo.


350 volume VIII Argomenti avanzati e accessori<br />

372.21 Linux e GNU/Linux<br />

GNU Hurd non è pronto per un uso non sperimentale, ma per fortuna è disponibile un altro<br />

kernel: nel 1991 Linus Torvalds sviluppò un Kernel compatibile con Unix e lo chiamò Linux.<br />

Attorno al 1992, la combinazione di Linux con il sistema GNU ancora incompleto produsse un<br />

sistema operativo libero completo (naturalmente combinarli fu un notevole lavoro di per sé). È<br />

grazie a Linux che oggi possiamo utilizzare una versione del sistema GNU.<br />

Chiamiamo GNU/Linux questa versione del sistema, per indicare la sua composizione come una<br />

combinazione del sistema GNU col kernel Linux.<br />

372.22 Le sfide che ci aspettano<br />

Abbiamo dimostrato la nostra capacità di sviluppare un’ampia gamma di s<strong>of</strong>tware libero, ma<br />

questo non significa che siamo invincibili e inarrestabili. Diverse sfide rendono incerto il futuro<br />

del s<strong>of</strong>tware libero, e affrontarle richiederà perseveranza e sforzi costanti, talvolta per anni. Sarà<br />

necessaria quella determinazione che le persone sanno dimostrare quando danno valore alla<br />

propria libertà e non permettono a nessuno di sottrargliela. Le quattro sezioni seguenti parlano di<br />

queste sfide.<br />

372.23 Hardware segreto<br />

Sempre più spesso, i costruttori di hardware tendono a mantenere segrete le specifiche delle<br />

loro apparecchiature; questo rende difficile la scrittura di driver liberi che permettano a Linux e<br />

XFree86 di supportare nuove periferiche. Anche se oggi abbiamo sistemi completamente liberi,<br />

potremmo non averli domani se non saremo in grado di supportare i calcolatori di domani.<br />

Esistono due modi per affrontare il problema. Un programmatore può ricostruire le specifiche<br />

dell’hardware usando tecniche di reverse engineering. Oppure si può scegliere hardware supportato<br />

dai programmi liberi: man mano che il nostro numero aumenta, la segretezza delle specifiche<br />

diventerà una pratica controproducente.<br />

Il reverse engineering è difficile: avremo programmatori sufficientemente determinati da dedicarvisi?<br />

Sì, se avremo costruito una forte consapevolezza che avere programmi liberi sia una<br />

questione di principio e che i driver non liberi non sono accettabili. E succederà che molti di noi<br />

accettino di spendere un po’ di più o perdere un po’ più di tempo per poter usare driver liberi?<br />

Sì, se il desiderio di libertà e la determinazione ad ottenerla saranno diffusi.<br />

372.24 Librerie non libere<br />

Una libreria non libera che giri su sistemi operativi liberi funziona come una trappola per i creatori<br />

di programmi liberi. Le funzionalità attraenti della libreria fungono da esca; chi usa la libreria<br />

cade nella trappola, perché il programma che crea è inutile come parte di un sistema operativo<br />

libero (a rigore, il programma potrebbe esservi incluso, ma non funzionerebbe, visto che manca<br />

la libreria). Peggio ancora, se un programma che usa la libreria proprietaria diventa diffuso, può<br />

attirare altri ignari programmatori nella trappola.<br />

Il problema si concretizzò per la prima volta con la libreria Motif, negli anni ’80. Sebbene non<br />

ci fossero ancora sistemi operativi liberi, i problemi che Motif avrebbe causato loro erano già<br />

chiari. Il progetto GNU reagì in due modi: interessandosi presso diversi progetti di s<strong>of</strong>tware<br />

libero perché supportassero gli strumenti grafici X liberi in aggiunta a Motif, e cercando qualcuno


Il progetto GNU 351<br />

che scrivesse un sostituto libero di Motif. Il lavoro richiese molti anni: solo nel 1997 LessTif,<br />

sviluppato dagli "Hungry Programmers", divenne abbastanza potente da supportare la maggior<br />

parte delle applicazioni Motif.<br />

Tra il 1996 e il 1998 un’altra libreria non libera di strumenti grafici, chiamata Qt, veniva usata in<br />

una significativa raccolta di s<strong>of</strong>tware libero: l’ambiente grafico KDE.<br />

I sistemi liberi GNU/Linux non potevano usare KDE, perché non potevamo usare la libreria;<br />

tuttavia, alcuni distributori commerciali di sistemi GNU/Linux, non scrupolosi nell’attenersi solo<br />

ai programmi liberi, aggiunsero KDE ai loro sistemi, ottenendo così sistemi che <strong>of</strong>frivano più<br />

funzionalità, ma meno libertà. Il gruppo che sviluppava KDE incoraggiava esplicitamente altri<br />

programmatori ad usare Qt, e milioni di nuovi "utenti Linux" non sospettavano minimamente che<br />

questo potesse costituire un problema. La situazione si faceva pericolosa.<br />

La comunità del s<strong>of</strong>tware libero affrontò il problema in due modi: GNOME e Harmony.<br />

GNOME (GNU Network Object Model Environment, modello di ambiente per oggetti di rete)<br />

è il progetto GNU per l’ambiente grafico (desktop). Intrapreso nel 1997 da Miguel de Icaza e<br />

sviluppato con il supporto di Red Hat S<strong>of</strong>tware, GNOME si ripromise di fornire funzionalità<br />

grafiche simili a quelle di KDE, ma usando esclusivamente s<strong>of</strong>tware libero. GNOME <strong>of</strong>fre anche<br />

dei vantaggi tecnici, come il supporto per svariati linguaggi di programmazione, non solo il C++.<br />

Ma il suo scopo principale era la libertà: non richiedere l’uso di alcun programma che non fosse<br />

libero.<br />

Harmony è una libreria compatibile con Qt, progettata per rendere possibile l’uso del s<strong>of</strong>tware<br />

KDE senza dover usare Qt.<br />

Nel novembre 1998 gli autori di Qt annunciarono un cambiamento di licenza che, una volta<br />

operativo, avrebbe reso Qt s<strong>of</strong>tware libero. Non c’è modo di esserne certi, ma credo che questo<br />

fu in parte dovuto alla decisa risposta della comunità al problema posto da Qt quando non era<br />

libero (la nuova licenza è scomoda ed iniqua, per cui rimane comunque preferibile evitare l’uso<br />

di Qt).<br />

Come risponderemo alla prossima allettante libreria non libera? Riuscirà la comunità in toto a<br />

comprendere l’importanza di evitare la trappola? Oppure molti di noi preferiranno la convenienza<br />

alla libertà, creando così ancora un grave problema? Il nostro futuro dipende dalla nostra filos<strong>of</strong>ia.<br />

372.25 Brevetti sul s<strong>of</strong>tware<br />

Il maggior pericolo a cui ci troviamo di fronte è quello dei brevetti sul s<strong>of</strong>tware, che possono<br />

rendere inaccessibili al s<strong>of</strong>tware libero algoritmi e funzionalità per un tempo che può estendersi<br />

fino a vent’anni. I brevetti sugli algoritmi di compressione LZW furono depositati nel 1983, e<br />

ancor oggi non possiamo distribuire programmi liberi che producano immagini GIF compresse.<br />

Nel 1998 un programma libero per produrre audio compresso MP3 venne ritirato sotto minaccia<br />

di una causa per violazione di brevetto.<br />

Ci sono modi per affrontare la questione brevetti: possiamo cercare prove che un brevetto non sia<br />

valido oppure possiamo cercare modi alternativi per ottenere lo stesso risultato. Ognuna di queste<br />

tecniche, però, funziona solo in certe circostanze; quando entrambe falliscono un brevetto può<br />

obbligare tutto il s<strong>of</strong>tware libero a rinunciare a qualche funzionalità che gli utenti desiderano.<br />

Cosa dobbiamo fare quando ciò accade?<br />

Chi fra noi apprezza il s<strong>of</strong>tware libero per il valore della libertà rimarrà comunque dalla parte dei<br />

programmi liberi; saremo in grado di svolgere il nostro lavoro senza le funzionalità coperte da<br />

brevetto. Ma coloro che apprezzano il s<strong>of</strong>tware libero perché si aspettano che sia tecnicamente


352 volume VIII Argomenti avanzati e accessori<br />

superiore probabilmente grideranno al fallimento quando un brevetto ne impedisce lo sviluppo.<br />

Perciò, nonostante sia utile parlare dell’efficacia pratica del modello di sviluppo "a cattedrale",<br />

e dell’affidabilità e della potenza di un dato programma libero, non ci dobbiamo fermare qui;<br />

dobbiamo parlare di libertà e di principi.<br />

372.26 Documentazione libera<br />

La più grande carenza nei nostri sistemi operativi liberi non è nel s<strong>of</strong>tware, quanto nella carenza<br />

di buoni manuali liberi da includere nei nostri sistemi. La documentazione è una parte<br />

essenziale di qualunque pacchetto s<strong>of</strong>tware; quando un importante pacchetto s<strong>of</strong>tware libero non<br />

viene accompagnato da un buon manuale libero si tratta di una grossa lacuna. E di queste lacune<br />

attualmente ne abbiamo molte.<br />

La documentazione libera, come il s<strong>of</strong>tware libero, è una questione di libertà, non di prezzo. Il<br />

criterio per definire libero un manuale è fondamentalmente lo stesso che per definire libero un<br />

programma: si tratta di <strong>of</strong>frire certe libertà a tutti gli utenti. Deve essere permessa la redistribuzione<br />

(compresa la vendita commerciale), sia in formato elettronico che cartaceo, in modo che il<br />

manuale possa accompagnare ogni copia del programma.<br />

Autorizzare la modifica è anch’esso un aspetto cruciale; in generale, non credo sia essenziale<br />

permettere alle persone di modificare articoli e libri di qualsiasi tipo. Per esempio, non credo che<br />

voi o io dobbiamo sentirci in dovere di autorizzare la modifica di articoli come questo, articoli<br />

che descrivono le nostre azioni e il nostro punto di vista.<br />

Ma c’è una ragione particolare per cui la libertà di modifica è cruciale per la documentazione<br />

dei programmi liberi. Quando qualcuno esercita il proprio diritto di modificare il programma,<br />

aumentandone o alterandone le funzionalità, se è coscienzioso modificherà anche il manuale,<br />

in modo da poter fornire una documentazione utile e accurata insieme al programma modificato.<br />

Un manuale che non permetta ai programmatori di essere coscienziosi e completare il loro lavoro<br />

non soddisfa i bisogni della nostra comunità.<br />

Alcuni limiti sulla modificabilità non pongono alcun problema; per esempio, le richieste di conservare<br />

la nota di copyright dell’autore originale, i termini di distribuzione e la lista degli autori<br />

vanno bene. Non ci sono problemi nemmeno nel richiedere che le versioni modificate dichiarino<br />

esplicitamente di essere tali, così pure che intere sezioni non possano essere rimosse o modificate,<br />

finché queste sezioni vertono su questioni non tecniche. Restrizioni di questo tipo non creano<br />

problemi perché non impediscono al programmatore coscienzioso di adattare il manuale perché<br />

rispecchi il programma modificato. In altre parole, non impediscono alla comunità del s<strong>of</strong>tware<br />

libero di beneficiare appieno dal manuale.<br />

D’altro canto, deve essere possibile modificare tutto il contenuto tecnico del manuale e poter<br />

distribuire il risultato in tutti i formati usuali, attraverso tutti i normali canali di distribuzione;<br />

diversamente, le restrizioni creerebbero un ostacolo per la comunità, il manuale non sarebbe<br />

libero e avremmo bisogno di un altro manuale.<br />

Gli sviluppatori di s<strong>of</strong>tware libero avranno la consapevolezza e la determinazione necessarie a<br />

produrre un’intera gamma di manuali liberi? Ancora una volta, il nostro futuro dipende dalla<br />

nostra filos<strong>of</strong>ia.


Il progetto GNU 353<br />

372.27 Dobbiamo parlare di libertà<br />

Stime recenti valutano in dieci milioni il numero di utenti di sistemi GNU/Linux quali Debian<br />

GNU/Linux e Red Hat Linux. Il s<strong>of</strong>tware libero ha creato tali vantaggi pratici che gli utenti<br />

stanno approdando ad esso per pure ragioni pratiche.<br />

Gli effetti positivi di questa situazione sono evidenti: maggior interesse a sviluppare s<strong>of</strong>tware<br />

libero, più clienti per le imprese di s<strong>of</strong>tware libero e una migliore capacità di incoraggiare le<br />

aziende a sviluppare s<strong>of</strong>tware commerciale libero invece che prodotti s<strong>of</strong>tware proprietari.<br />

L’interesse per il s<strong>of</strong>tware, però, sta crescendo più in fretta della coscienza della filos<strong>of</strong>ia su cui è<br />

basato, e questa disparità causa problemi. La nostra capacità di fronteggiare le sfide e le minacce<br />

descritte in precedenza dipende dalla determinazione nell’essere impegnati per la libertà. Per<br />

essere sicuri che la nostra comunità abbia tale determinazione, dobbiamo diffondere l’idea presso<br />

i nuovi utenti man mano che entrano a far parte della comunità.<br />

Ma in questo stiamo fallendo: gli sforzi per attrarre nuovi utenti nella comunità sono di gran lunga<br />

maggiori degli sforzi per l’educazione civica della comunità stessa. Dobbiamo fare entrambe le<br />

cose, e dobbiamo mantenere un equilibrio fra i due impegni.<br />

372.28 "Open Source"<br />

Parlare di libertà ai nuovi utenti è diventato più difficile dal 1998, quando una parte della<br />

comunità decise di smettere di usare il termine "free s<strong>of</strong>tware" e usare al suo posto "open source".<br />

Alcune delle persone che suggerirono questo termine intendevano evitare che si confondesse<br />

"free" con "gratis", un valido obiettivo. D’altra parte, altre persone intendevano mettere da parte<br />

lo spirito del principio che aveva dato la spinta al movimento del s<strong>of</strong>tware libero e al progetto<br />

GNU, puntando invece ad attrarre i dirigenti e gli utenti commerciali, molti dei quali afferiscono<br />

ad una ideologia che pone il pr<strong>of</strong>itto al di sopra della libertà, della comunità, dei principi. Perciò la<br />

retorica di "open source" si focalizza sul possibilità di creare s<strong>of</strong>tware di buona qualità e potente<br />

ma evita deliberatamente le idee di libertà, comunità, principio.<br />

Le riviste che si chiamano "Linux..." sono un chiaro esempio di ciò: sono piene di pubblicità di<br />

s<strong>of</strong>tware proprietario che gira sotto GNU/Linux; quando ci sarà il prossimo Motif o Qt, queste<br />

riviste avvertiranno i programmatori di starne lontano o accetteranno la sua pubblicità?<br />

L’appoggio delle aziende può contribuire alla comunità in molti modi; a parità di tutto il resto è<br />

una cosa utile. Ma ottenere questo appoggio parlando ancor meno di libertà e principi può essere<br />

disastroso; rende ancora peggiore lo sbilanciamento descritto tra diffusione ed educazione civica.<br />

"S<strong>of</strong>tware libero" (free s<strong>of</strong>tware) e "sorgente aperto" (open source) descrivono più o meno la<br />

stessa categoria di s<strong>of</strong>tware, ma dicono cose differenti sul s<strong>of</strong>tware e sui valori. Il progetto GNU<br />

continua ad usare il termine "s<strong>of</strong>tware libero" per esprimere l’idea che la libertà sia importante,<br />

non solo la tecnologia.<br />

372.29 Prova!<br />

La filos<strong>of</strong>ia di Yoda ("Non c’è provare") suona bene, ma per me non funziona. Ho fatto la maggior<br />

parte del mio lavoro angustiato dal timore di non essere in grado di svolgere il mio compito e nel<br />

dubbio, se fossi riuscito, che non fosse sufficiente per raggiungere l’obiettivo. Ma ci ho provato<br />

in ogni caso perché nessuno tranne me si poneva tra il nemico e la mia città. Sorprendendo me<br />

stesso, qualche volta sono riuscito.


354 volume VIII Argomenti avanzati e accessori<br />

A volte ho fallito, alcune delle mie città sono cadute; poi ho trovato un’altra città minacciata e<br />

mi sono preparato ad un’altra battaglia. Con l’andar del tempo ho imparato a cercare le possibili<br />

minacce e a mettermi tra loro e la mia città, facendo appello ad altri hacker perché venissero e si<br />

unissero a me.<br />

Oggigiorno spesso non sono da solo. È un sollievo ed una gioia quando vedo un reggimento di<br />

hacker che scavano trincee per difendere il confine e quando mi rendo conto che questa città<br />

può sopravvivere; per ora. Ma i pericoli diventano più grandi ogni anno, ed ora Micros<strong>of</strong>t ha<br />

esplicitamente preso di mira la nostra comunità. Non possiamo dare per scontato il futuro della<br />

libertà; non diamolo per scontato! Se volete mantenere la vostra libertà dovete essere pronti a<br />

difenderla.


Capitolo 373<br />

La rivoluzione del Mimete, ovvero s<strong>of</strong>tware e<br />

copyright<br />

di Giulio Mazzolini giuliomazzolini @ libero.it<br />

non modificabile<br />

Lo scrittore Primo Levi immaginò un particolare tipo di duplicatore, chiamato Mimete, capace<br />

di duplicare un oggetto esattamente come l’originale. Da una parte si poteva inserire un foglio<br />

protocollo con i bolli, dall’altra usciva un foglio protocollo identico all’originale con su dei bolli<br />

veri, un clone perfetto. Il protagonista prova quindi a duplicare vari oggetti, dapprima una banconota,<br />

poi un diamante, poi ancora una salamandra, riuscendoci felicemente. Alla fine duplicò<br />

la moglie e poi, stanco di avere due mogli, duplicò se stesso, per riformare due coppie uguali...<br />

Ci sono delle macchine per fotocopie che fanno delle copie «quasi» uguali all’originale, ma non<br />

perfettamente uguali, se continuiamo a fare una copia dall’ultima copia, la qualità degraderà più<br />

o meno velocemente in funzione della qualità della macchina. Quindi non è un Mimete.<br />

Eppure la fantasia dello scrittore ha visto giusto, esiste un Mimete e molti di voi l’hanno in casa.<br />

Non duplica tutto, ma duplica perfettamente il s<strong>of</strong>tware. La copia di un dischetto è esattamente<br />

uguale all’originale, non c’è modo di capire quale è l’originale e quale la copia, posso fare quante<br />

copie voglio, l’ultima copia sarà esattamente uguale all’originale. Il vostro PC è un Mimete<br />

limitato al s<strong>of</strong>tware e la sua capacità di duplicazione è un fatto assolutamente rivoluzionario.<br />

Non solo è possibile duplicare, ma costa poco e lo possono fare tutti.<br />

Si tratta di una rivoluzione importante, che mi piace chiamare «La rivoluzione del Mimete» in<br />

onore di Primo Levi.<br />

373.1 Che cosa è il s<strong>of</strong>tware?<br />

Il s<strong>of</strong>tware non è altro che una ricetta, un qualcosa di immateriale che congiungendosi a qualcosa<br />

di materiale, l’hardware, trasfigura la materia informe facendola diventare qualcosa di ordinato e<br />

utilizzabile.<br />

Dire che il s<strong>of</strong>tware sia un’opera d’autore assimilabile a un’opera letteraria è sicuramente forzato,<br />

inoltre la legislazione corrente che protegge il s<strong>of</strong>tware è molto più severa di quella per esempio<br />

che protegge i libri.<br />

Un libro posso leggerlo così com’è, ne godo direttamente, non devo metterlo in una macchina<br />

per goderne. Un libro posso imprestarlo a un amico, in ufficio posso tenere una sola copia del<br />

«Manuale dell’ingegnere» anche se ci sono dieci ingegneri che lo usano. Esistono biblioteche<br />

pubbliche dove posso prendere in prestito un libro e leggerlo, quindi «usarlo» completamente<br />

per quello che è stato fatto, con poca spesa.<br />

È vero che gli editori che fanno libri per la scuola si lamentano del fenomeno delle fotocopie, in<br />

Francia si è trovata una soluzione che prevede il versamento da parte delle scuole di un tantum<br />

a un fondo che risarcisce autori ed editori. Non so se sia il sistema migliore, ma sicuramente<br />

prende atto della facilità di copiare e cerca una soluzione non poliziesca.<br />

355


356 volume VIII Argomenti avanzati e accessori<br />

373.2 Cenni di storia del s<strong>of</strong>tware<br />

La prima macchina che si considera essere un calcolatore digitale moderno è l’ENIAC, costruito<br />

nei laboratori della Moore School <strong>of</strong> Electrical Engineering dell’Università della Pensilvania,<br />

agli inizi degli anni 1940. Iniziò a lavorare nel 1946, venne spento nel 1955. Vale la pena di<br />

accennare all’architettura della macchina: 18000 valvole, memoria a flip-flop, quindi ancora a<br />

valvole, 20 parole di memoria.<br />

Usa un sistema digitale e non più analogico, ma non ancora il sistema binario. La macchina<br />

seguente, progettata da Von Neumann mentre l’ENIAC era in fase di realizzazione, usa ancora<br />

valvole, ma per la memoria usa una linea di ritardo acustica a mercurio, ed è una delle prime a<br />

usare il codice binario, propugnato appunto da Von Neumann ed ha un’architettura che sarà poi<br />

seguita da quasi tutte le macchine seguenti. La macchina di Von Neumann entra in operatività<br />

nel 1950.<br />

Entrambe le macchine furono costruite su iniziativa dell’Esercito Americano e furono usate per<br />

fare calcoli balistici, le usò anche Fermi per i laboratori di Los Alamos. Infatti i calcolatori<br />

precedenti a relè elettromeccanici erano molto più lenti, circa un secondo per una moltiplicazione,<br />

mentre l’ENIAC impiegava circa un millisecondo.<br />

In quegli anni i dati venivano inseriti tramite schede perforate, i programmi venivano scritti in<br />

linguaggio mnemonico e poi tradotti da umani in codice binario. Solo nel 1949 si iniziò a tradurre<br />

il codice mnemonico in binario con uno speciale s<strong>of</strong>tware di traduzione.<br />

Il primo vero linguaggio per calcolatori fu il Fortran, che iniziato nel 1954 e ultimato nel 1957,<br />

divenne il linguaggio più diffuso al mondo.<br />

I calcolatori conobbero una prima rivoluzione con l’introduzione dei transistor, nel 1956 infatti<br />

appare il TX-0, una macchina molto più piccola, che non usava schede per l’input ma la flexowriter,<br />

un perforatore di nastro cartaceo che molti di voi avranno visto sui telex in uso fino a qualche<br />

anno fa.<br />

La seconda rivoluzione delle macchine è l’introduzione dei circuiti integrati, 1961. Le macchine<br />

diventano ancora più piccole e potenti, la memoria aumenta e la velocità di calcolo pure.<br />

Nel 1959 John McCarty, padre dell’intelligenza artificiale, inizia a scrivere il linguaggio LISP,<br />

nel 1961 arriva al MIT il PDP-1, una macchina con schermo ma con input ancora a nastro,<br />

prototipo di una lunga serie di macchine PDP che assomigliano sempre di più ai calcolatori<br />

moderni. Nei primi anni 1960 John Kemeny scrive il Basic, nello stesso periodo l’agenzia ARPA<br />

(Advanced Research Project Agency) dell’Esercito USA inizia a finanziare progetti di ricerca sui<br />

calcolatori.<br />

Siamo ancora nella preistoria del s<strong>of</strong>tware, ci sono pochi programmi o non ce ne sono affatto, i<br />

ricercatori si scrivono i codici di cui hanno bisogno e se lo passano liberamente senza problemi.<br />

Le macchine vengono vendute con un po’ di s<strong>of</strong>tware come fosse un set di istruzioni per l’uso.<br />

Ogni anno segna un progresso importante, nel 1967, appare il primo floppy disk, nel 1969 inizia<br />

lo sviluppo di UNIX nel laboratori Bell di AT&T, nel 1970 Intel, con la collaborazione di<br />

Federico Faggin, produce il 4004.<br />

Se vogliamo dare una data alla fine del periodo iniziale questa potrebbe essere il 1968, per il nuovo<br />

atteggiamento dell’opinione pubblica e in particolare degli studenti che vedono con sospetto<br />

le ricerche fatte al MIT con i finanziamenti dell’Esercito, che culmina con l’assalto ai laboratori.<br />

In quel periodo nasce e si sviluppa un movimento per portare il computer al popolo, di cui un<br />

protagonista è stato Lee Feldestain.


La rivoluzione del Mimete, ovvero s<strong>of</strong>tware e copyright 357<br />

L’IBM, pressata dagli ambienti antimonopolisti, decise una mossa che voleva dimostrare come<br />

essa monopolista non fosse: scorporò il prezzo delle macchine, l’hardware, da quello del<br />

s<strong>of</strong>tware, il famoso «unbundling», dando così spazio a potenziali concorrenti nel settore del<br />

s<strong>of</strong>tware.<br />

Gli anni seguenti sono di grande fermento, nasce il primo PC, una macchina venduta in kit che si<br />

chiamava Altair. Molti di voi l’avranno vista nel film «War Games», era una scatola con dentro<br />

un microprocessore, sul fronte delle levette per l’input e delle lucine per l’output! Costava 300 $<br />

e aveva 256 byte di memoria. Non serviva praticamente a niente, eppure ebbe un grandissimo<br />

successo, portava finalmente in casa di tutti un computer, quello che Lee Feldstein e i suoi amici<br />

predicavano da tempo.<br />

Poi ancora una accelerazione incredibile: nel 1976 appare il Sol, progettato da Lee e nel 1977<br />

l’Apple. Nel 1974 Brian Kernighan e Dennis Ritchie sviluppano il linguaggio C.<br />

Il 1976 segna la nascita del s<strong>of</strong>tware proprietario e dei principi che ci stanno dietro. Infatti assieme<br />

all’Altair si poteva comperare una versione del Basic, a circa 150 $. L’autore del programma<br />

che chiedeva un prezzo così esoso era Bill Gates.<br />

A causa del prezzo elevato qualcuno fece delle copie del programma (allora i programmi stavano<br />

su nastro perforato), Gates molto irritato, scrisse una storica lettera sui giornali letti allora dagli<br />

hacker, nella quale lamentava il fatto che si copiasse senza pagare e che questo fatto avrebbe<br />

ostacolato che del buon s<strong>of</strong>tware venisse scritto.<br />

Chi può affrontare di fare del lavoro pr<strong>of</strong>essionale per nulla? Quale hobbista può<br />

mettere tre anni di tempo-uomo nella programmazione, trovare tutti i bug, documentare<br />

il suo prodotto, il tutto gratuitamente?... Francamente, la cosa che voi fate è<br />

rubare.<br />

1<br />

Argomentazioni sulle quali Bill ha costruito i suo impero, certamente con buona parte di verità ma<br />

che non tengono conto che già allora esisteva un Mimete con il quale si poteva copiare facilmente,<br />

velocemente e a basso costo.<br />

Sul Doctor Dobbs Journal, Jim Warren scriveva:<br />

Quando sarà gratis o così poco costoso che sarà più facile pagarlo che duplicarlo,<br />

non verrà più rubato<br />

Tim Pittman scrisse allora un Tiny Basic e lo mise in vendita a 5 $! Lo fa teorizzando che il<br />

s<strong>of</strong>tware deve essere disponibile per tutti e che quindi deve costare il meno possibile.<br />

Oggi questo discorso è stato ripreso dal mondo del s<strong>of</strong>tware libero, con ben maggior<br />

consapevolezza e forza.<br />

1 Bill Gates, lettera aperta sulla pirateria, apparsa sul bollettino dell’Homebrew Computer Club, il 03/02/1976.


Il copyright/diritto d’autore del s<strong>of</strong>tware<br />

di Giulio Mazzolini giuliomazzolini @ libero.it<br />

Capitolo 374<br />

non modificabile<br />

Fino a circa la fine degli anni 1980, il s<strong>of</strong>tware veniva ceduto assieme alla macchina o veniva<br />

scritto su richiesta dei clienti. Se c’erano dei pacchetti pronti, questi dovevano venir personalizzati<br />

per ogni utente. È solo con la nascita del PC IBM, 1981, quando il computer si diffonde in<br />

milioni di pezzi che nasce una richiesta di massa di s<strong>of</strong>tware.<br />

Su come proteggere i diritti dell’autore del programma s<strong>of</strong>tware esisteva un dibattito teorico sin<br />

da una decina d’ anni. In Francia nel 1968, una legge dichiarava il s<strong>of</strong>tware non brevettabile.<br />

Negli USA, con approccio più pragmatico, semplicemente il s<strong>of</strong>tware non veniva brevettato in<br />

quanto l’ufficio brevetti non aveva la più pallida idea di come si potesse brevettare un programma<br />

s<strong>of</strong>tware. Il problema esiste tuttora, essendo quasi impossibile riconoscere se un s<strong>of</strong>tware in<br />

qualche modo lede i diritti brevettuali di un altro.<br />

Oggi tuttavia si brevettano gli algoritmi, una assurdità totale, che però sull’onda del successo<br />

del concetto di «proprietà intellettuale», riesce a venir accettata dai legislatori e dall’opinione<br />

comune.<br />

Sarebbe come se oggi la tabellina del nove o i logaritmi fossero brevettati e doveste pagare per<br />

usarli.<br />

Nel 1980, su pressione dei produttori di s<strong>of</strong>tware, il governo americano promulgò il Copyright<br />

Act con il quale si riconosceva al s<strong>of</strong>tware lo stesso tipo di protezione accordata sino ad allora ai<br />

testi letterari o alle altre forme d’opere d’arte.<br />

Non sfuggiva ai legislatori l’effetto «Mimete», ma venne semplicemente risolto vietando severamente<br />

ogni forma di duplicazione del s<strong>of</strong>tware. Tanto era rigida la legge che un po’ dopo<br />

dovettero chiarire che la duplicazione per uso di «back-up» era lecita!<br />

Senza entrare nei dettagli, voglio solo far notare con l’ultima legge anti pirateria italiana, la copia<br />

illecita di s<strong>of</strong>tware è punita con reclusione da tre mesi a tre anni, come mi ha ricordato vigorosamente<br />

un immenso cartellone a Malpensa: ci vogliono 3 minuti per copiare un programma ma ti<br />

può costare 3 anni...<br />

374.1 Una breve storia del diritto d’autore<br />

L’idea del copyright e la legislazione relativa nasce dopo l’invenzione della macchina da stampa<br />

di Gutemberg (1400-1468). L’idea era semplice: stampare un libro costa, quindi se lo stampo io<br />

non deve poterlo stampare un altro.<br />

La legislazione originale quindi proteggeva gli interessi degli stampatori, l’autore non veniva<br />

preso in considerazione, anche perché spesso poi i libri contenevano testi di antichi scrittori<br />

morti da secoli. Il libro più stampato infatti era la Bibbia.<br />

Il monopolio librario relegava gli autori in una posizione palesemente subalterna o inesistente.<br />

Dal 1709 (data che segna la nascita del diritto positivo del copyright) fino al 1774, la proprietà<br />

letteraria dell’autore fu affermata a beneficio degli editori più che degli autori.<br />

Solo nel 1777 gli Editti che precedono la Rivoluzione Francese, riconoscono il diritto d’autore,<br />

nasce allora la concezione moderna della tutela dell’autore. Il diritto d’autore è la proprietà più<br />

sacra in quanto si considera «inscindibile l’oggetto dal soggetto».<br />

358


Il copyright/diritto d’autore del s<strong>of</strong>tware 359<br />

Si riconosce finalmente la figura dell’autore e se ne tutelano i suoi diritti. L’autore dell’opera<br />

possiede tutti i diritti per il semplice fatto di esserne l’autore. Può poi cedere alcuni o tutti i diritti<br />

a terzi con un patto esplicito. Se si tratta di un testo, in genere i diritti sono quelli di riprodurre il<br />

testo in un libro, eventualmente di farne delle traduzione e ancora delle ristampe. Se l’opera è un<br />

quadro, l’ autore può cedere il diritto di copia dell’opera, per esempio per farne una copertina, o<br />

un manifesto.<br />

Un autore può tutelare la sua opera anche se ha ceduto i diritti di riproduzione. Può esigere che<br />

l’opera resti integra, o che non subisca modificazioni. Hanno quindi ragione quei registi che<br />

pretendono che il loro film passi senza interruzioni pubblicitarie, o che rifiutano che vengano<br />

tagliati (o censurati) in alcune scene.<br />

In via generale se comperate un quadro, con il possesso e la proprietà legittima dello stesso, non<br />

avete automaticamente il diritto di riprodurlo.<br />

I diritti d’autore sono ereditari, per cui gli eredi potranno godere dei vantaggi economici derivanti<br />

dai diritti d’autore anche dopo la morte dell’autore.<br />

Le multinazionali (americane) del cinema, della musica e del s<strong>of</strong>tware sono riuscite a ottenere il<br />

prolungamento della durata dei diritti d’autore fino a 70 anni dopo la morte.<br />

Quando il legislatore dice «autore» intende «di opera artistica». Negli USA il concetto di copyright<br />

tende a venir esteso anche in mancanza di un’opera d’arte. Persino una lettera commerciale<br />

è stata considerata soggetta a copyright.<br />

Negli USA è oramai prassi brevettare anche il s<strong>of</strong>tware, non programmi interi, ma pezzi particolari.<br />

È brevettato il cestino dell’Apple, l’algoritmo di compressione usato dal programma Zip,<br />

l’XOR del puntatore grafico. La Micros<strong>of</strong>t deposita migliaia di brevetti e così pure le altre grandi<br />

case.<br />

Oggi nessuno può seriamente pensare di scrivere del s<strong>of</strong>tware commerciale se non ha capitali<br />

molto grandi. Infatti deve utilizzare schiere di avvocati a tempo pieno per assistere chi scrive,<br />

affinché non incorra in infrazioni di brevetti o di copyright. Poiché è impossibile che uno studio<br />

di avvocati conosca tutti i brevetti di s<strong>of</strong>tware, c’è la quasi certezza di incappare in qualche azione<br />

legale. Cause che costano molto o si chiudono con costosi accordi.<br />

Avevamo detto che la legislazione USA ha assimilato il s<strong>of</strong>tware a una opera d’autore e quindi<br />

ha utilizzato il corpo legislativo del copyright per proteggerlo.<br />

374.2 I sei punti della rivoluzione digitale<br />

L’avvocato Pamela Samuelson ha così voluto riassumere i sei punti principali della rivoluzione<br />

digitale:<br />

1. È molto facile duplicare, si fa presto, costa poco, le copie sono dei duplicati<br />

perfetti dell’originale.<br />

2. È facilissimo trasmettere i dati e si trasmettono quasi istantaneamente,<br />

si trasmettono in tutto il mondo scavalcando barriere nazionali, doganali,<br />

censorie.<br />

3. Il dato digitale è malleabile, si può trasformare quanto si vuole, si può deformare<br />

quanto si vuole, un originale ne diventa facilmente un altro deformato o<br />

rielaborato.<br />

4. Le opere letterarie, sonore, grafiche, una volta messe in forma digitale, subiscono<br />

la stessa sorte, sono indistinguibili nel supporto, non sono più differenziate


360 volume VIII Argomenti avanzati e accessori<br />

tra libro, quadro e disco, mettendo in crisi i concetti di copyright che sono diversi<br />

se si tratta di quadro disco o libro.<br />

5. Il dato digitale è compatto, è piccolo si trasporta facilmente.<br />

6. Il dato digitale non si visita più linearmente come si fa con un libro, ma in modo<br />

non lineare. Tutti coloro che hanno visitato un sito in Internet non hanno letto<br />

pagina dopo pagina, ma sono saltati da una pagina a un altro sito, creandosi<br />

percorsi individuali.<br />

Ciascuna delle caratteristiche precedenti è sufficiente a causare una rottura delle dottrine<br />

esistenti sulla proprietà intellettuale. Tutte e sei assieme sicuramente costringeranno<br />

i legislatori a una modifica radicale del concetto di copyright e di proprietà<br />

intellettuale.<br />

374.3 I vantaggi del «free s<strong>of</strong>tware»<br />

Le sei caratteristiche citate da Pamela Samuelson riguardano in generale tutti gli aspetti del digitale,<br />

per quanto riguarda in particolare il s<strong>of</strong>tware, invece questi sono gli argomenti principali a<br />

favore del s<strong>of</strong>tware libero.<br />

1. La diffusione del sapere funziona se l’informazione circola: Jefferson ha detto che se alla<br />

luce di una candela studiano due persone, il sapere dell’umanità aumenta, senza che per<br />

questo si consumino più candele.<br />

2. Le limitazioni imposte dai «privilegi» del copyright e dei brevetti impediscono lo sviluppo<br />

tecnologico e scientifico, facilitando i monopoli.<br />

3. Il mantenimento delle licenze con le quali il s<strong>of</strong>tware oggi è venduto, e la relativa assimilazione<br />

del s<strong>of</strong>tware a una opera letteraria, induce una legislazione oppressiva assurda e la<br />

sua applicazione necessita di un sistema poliziesco da grande fratello.<br />

4. Il s<strong>of</strong>tware libero è meglio per gli utilizzatori, in quanto hanno a disposizione il codice sorgente<br />

per fare delle modifiche a degli adattamenti alle loro necessità specifiche rapidamente<br />

e senza esborsi onerosi.<br />

5. Il s<strong>of</strong>tware libero è meglio per i programmatori, che non devono avere un avvocato alle<br />

spalle e in quanto hanno a disposizione dei codici che possono utilizzare per scrivere altro<br />

codice, in un processo di accumulazione e di sviluppo del sapere.<br />

6. Il «free» è meglio per gli utenti perché non ci sono licenze da pagare.<br />

Ci sono al mondo 6 miliardi di individui e 100 milioni di PC, il «free» è il futuro, non soppianterà<br />

il s<strong>of</strong>tware proprietario per quanto riguarda applicazioni complesse pr<strong>of</strong>essionali, ma lo costringerà<br />

a tornare a prendere in considerazione la vecchia affermazione di Jim Warren: «quando<br />

costerà così poco che sarà più facile comperarlo che copiarlo»...


Indice analitico del volume<br />

/dev/vcsa*, 321<br />

/dev/vcs*, 321<br />

/etc/brltty.conf, 323<br />

/etc/brltty/, 323<br />

backup, 8<br />

Barcode, 46<br />

braille, 316<br />

Code 128, 39<br />

Code 3 <strong>of</strong> 9, 37<br />

Code 39, 37<br />

Code 39 esteso, 39<br />

codice a barre, 30, 46<br />

copia di sicurezza, 8<br />

copia di sicurezza: generazioni, 10<br />

copia di sicurezza: livelli, 11<br />

dischetto di emergenza, 19<br />

dischetto di emergenza: Slackware, 22<br />

DOS: APPEND, 105<br />

DOS: ASSIGN, 103<br />

DOS: ATTRIB, 105<br />

DOS: AUTOEXEC.BAT, 112<br />

DOS: BREAK, 108<br />

DOS: BUFFERS, 108<br />

DOS: CALL, 115<br />

DOS: CH, 92<br />

DOS: CHCP, 113<br />

DOS: CHDIR, 92<br />

DOS: CHKDSK, 101<br />

DOS: CHOICE, 119<br />

DOS: CLS, 119<br />

DOS: COMP, 107<br />

DOS: CONFIG.SYS, 108<br />

DOS: COPY, 94<br />

DOS: COUNTRY, 109<br />

DOS: DATE, 114<br />

DOS: DEL, 95<br />

DOS: DELTREE, 106<br />

DOS: DEVICE, 110<br />

DOS: DEVICEHIGH, 110<br />

DOS: DIR, 94<br />

DOS: DISKCOPY, 102<br />

DOS: DOS, 110<br />

DOS: DRIVEPARM, 110<br />

DOS: ECHO, 118<br />

DOS: ERASE, 95<br />

DOS: FC, 107<br />

DOS: FCBS, 111<br />

DOS: FILES, 111<br />

DOS: FIND, 106<br />

361


DOS: FOR, 116<br />

DOS: FORMAT, 100<br />

DOS: GOTO, 117<br />

DOS: GRAFTABL, 114<br />

DOS: IF, 116<br />

DOS: INSTALL, 111<br />

DOS: JOIN, 104<br />

DOS: KEYB, 113<br />

DOS: LABEL, 101<br />

DOS: LASTDRIVE, 111<br />

DOS: LH, 121<br />

DOS: LOADHIGH, 121<br />

DOS: MD, 92<br />

DOS: MEM, 121<br />

DOS: MKDIR, 92<br />

DOS: MORE, 98<br />

DOS: MOVE, 106<br />

DOS: PAUSE, 119<br />

DOS: REM, 118<br />

DOS: REN, 96<br />

DOS: RENAME, 96<br />

DOS: RM, 93<br />

DOS: RMDIR, 93<br />

DOS: SET, 96<br />

DOS: SHELL, 111<br />

DOS: SHIFT, 115<br />

DOS: SORT, 98<br />

DOS: STACK, 111<br />

DOS: SUBST, 104<br />

DOS: SYS, 103<br />

DOS: TIME, 114<br />

DOS: TREE, 107<br />

DOS: TYPE, 97<br />

DOS: VERIFY, 105<br />

DOS: VOL, 101<br />

DOS: XCOPY, 102<br />

EAN: European Article Number, 33<br />

EAN-13, 31<br />

EAN-8, 31<br />

ELKS, 80<br />

Ethernet, 26<br />

GNU: Manifesto, 331<br />

Gnuplot, 126<br />

i25, 43<br />

interfogliata due su cinque, 43<br />

Interleaved two <strong>of</strong> five, 43<br />

ISBN, 36<br />

Ispell, 127<br />

ISSN, 37<br />

ITF, 43<br />

Minix, 58<br />

362


Perl, 127<br />

PLIP, 26<br />

SC, 127<br />

Slackware, 22<br />

Spreadsheet Calculator, 127<br />

UCC/EAN 128, 43<br />

UCC: Uniform Code Council, 33<br />

UPC-A, 31<br />

UPC-E, 31<br />

USS Code 128, 39<br />

363


364

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!