Trasformazione in lettere 3985
Transcript
Trasformazione in lettere 3985
Trasformazione in lettere THEN RETURN "LX" ELSE IF DIGIT = 6 THEN RETURN "LXX" ELSE IF DIGIT = 8 THEN RETURN "LXXX" ELSE IF DIGIT = 9 THEN RETURN "XC" END IF END DIGIT_2_TO_ROMAN DIGIT_3_TO_ROMAN (DIGIT) IF DIGIT = 0 THEN RETURN "" ELSE IF DIGIT = 1 THEN RETURN "C" ELSE IF DIGIT = 2 THEN RETURN "CC" ELSE IF DIGIT = 3 THEN RETURN "CCC" ELSE IF DIGIT = 4 THEN RETURN "CD" ELSE IF DIGIT = 5 THEN RETURN "D" ELSE IF DIGIT = 6 THEN RETURN "DC" ELSE IF DIGIT = 7 THEN RETURN "DCC" ELSE IF DIGIT = 8 THEN RETURN "DCCC" ELSE IF DIGIT = 9 THEN RETURN "CM" END IF END DIGIT_3_TO_ROMAN DIGIT_4_TO_ROMAN (DIGIT) IF DIGIT = 0 THEN RETURN "" ELSE IF DIGIT = 1 THEN RETURN "M" ELSE IF DIGIT = 2 THEN RETURN "MM" ELSE IF DIGIT = 3 THEN RETURN "MMM" ELSE IF DIGIT = 4 THEN RETURN "MMMM" ELSE IF DIGIT = 5 THEN RETURN "MMMMM" 3985 Trasformazione in lettere 3986 ELSE IF DIGIT = 6 THEN RETURN "MMMMMM" ELSE IF DIGIT = 7 THEN RETURN "MMMMMMM" ELSE IF DIGIT = 8 THEN RETURN "MMMMMMMM" ELSE IF DIGIT = 9 THEN RETURN "MMMMMMMMM" END IF END DIGIT_4_TO_ROMAN DIGIT_4 := int (N/1000) N := N - (DIGIT_4 * 1000) DIGIT_3 := int (N/100) N := N - (DIGIT_3 * 100) DIGIT_2 := int (N/10) N := N - (DIGIT_2 * 10) DIGIT_1 := N RETURN DIGIT_4_TO_ROMAN DIGIT_3_TO_ROMAN DIGIT_2_TO_ROMAN DIGIT_1_TO_ROMAN (DIGIT_4) (DIGIT_3) (DIGIT_2) (DIGIT_2) END INTEGER_TO_ROMAN Come si vede, dopo aver scomposto il valore in quattro fasce, si utilizzano quattro funzioni distinte per ottenere la porzione di stringa che traduce il valore relativo. L’istruzione ‘RETURN’ finale intende concatenare tutte le stringhe risultanti. 354.3 Da numero a lettere, nel senso verbale Quando si trasforma un numero in lettere, per esempio quando si vuole trasformare 123 in «centoventitre», l’algoritmo di conversione deve tenere conto delle convenzioni linguistiche e non esiste una soluzione generale per tutte le lingue. Per quanto riguarda la lingua italiana, esistono nomi diversi fino al 19, poi ci sono delle particolarità per i plurali o i singolari. La pseudocodifica seguente risolve il problema in una sola funzione ricorsiva. Le omissioni dovrebbero essere sufficientemente intuitive. INTEGER_TO_ITALIAN (N) LOCAL X INTEGER LOCAL Y INTEGER IF N = 0 THEN RETURN ELSE IF N = 1 THEN RETURN ELSE IF N = 2 THEN RETURN ELSE IF N = 3 THEN RETURN "" "UNO" "DUE" "TRE" Trasformazione in lettere ELSE IF N = 4 THEN RETURN "QUATTRO" ELSE IF N = 5 THEN RETURN "CINQUE" ELSE IF N = 6 THEN RETURN "SEI" ELSE IF N = 7 THEN RETURN "SETTE" ELSE IF N = 8 THEN RETURN "OTTO" ELSE IF N = 9 THEN RETURN "NOVE" ELSE IF N = 10 THEN RETURN "DIECI" ELSE IF N = 11 THEN RETURN "UNDICI" ELSE IF N = 12 THEN RETURN "DODICI" ELSE IF N = 13 THEN RETURN "TREDICI" ELSE IF N = 14 THEN RETURN "QUATTORDICI" ELSE IF N = 15 THEN RETURN "QUINDICI" ELSE IF N = 16 THEN RETURN "SEDICI" ELSE IF N = 17 THEN RETURN "DICIASSETTE" ELSE IF N = 18 THEN RETURN "DICIOTTO" ELSE IF N = 19 THEN RETURN "DICIANNOVE" ELSE IF N = 20 THEN RETURN "VENTI" ELSE IF N = 21 THEN RETURN "VENTUNO" ELSE IF (N >= 22) AND (N <= 29) THEN RETURN "VENTI" INTEGER_TO_ITALIAN (N-20) ELSE IF N = 30 THEN RETURN "TRENTA" ELSE IF N = 31 THEN RETURN "TRENTUNO" ELSE IF (N >= 32) AND (N <= 39) THEN RETURN "TRENTA" INTEGER_TO_ITALIAN (N-30) 3987 3988 Trasformazione in lettere ... ... ELSE IF N = 90 THEN RETURN "NOVANTA" ELSE IF N = 91 THEN RETURN "NOVANTUNO" ELSE IF (N >= 92) AND (N <= 99) THEN RETURN "NOVANTA" INTEGER_TO_ITALIAN (N-90) ELSE IF (N >= 100) AND (N <= 199) THEN RETURN "CENTO" INTEGER_TO_ITALIAN (N-100) ELSE IF (N >= 200) AND (N <= 999) THEN X := int (N / 100) Y := N - (X * 100) RETURN INTEGER_TO_ITALIAN (X) "CENTO" INTEGER_TO_ITALIAN (Y) ELSE IF (N >= 1000) AND (N <= 1999) THEN RETURN "MILLE" INTEGER_TO_ITALIAN (N-1000) ELSE IF (N >= 2000) AND (N <= 999999) THEN X := int (N / 1000) Y := N - (X * 1000) RETURN INTEGER_TO_ITALIAN (X) "MILA" INTEGER_TO_ITALIAN (Y) ELSE IF (N >= 1000000) AND (N <= 1999999) THEN RETURN "UNMILIONE" INTEGER_TO_ITALIAN (N-1000000) ELSE IF (N >= 2000000) AND (N <= 999999999) THEN X := int (N / 1000000) Y := N - (X * 1000000) RETURN INTEGER_TO_ITALIAN (X) "MILIONI" INTEGER_TO_ITALIAN (Y) ELSE IF (N >= 1000000000) AND (N <= 1999999999) THEN RETURN "UNMILIARDO" INTEGER_TO_ITALIAN (N-1000000000) ELSE IF (N >= 2000000000) AND (N <= 999999999999) THEN X := int (N / 1000000000) Y := N - (X * 1000000000) RETURN INTEGER_TO_ITALIAN (X) "MILIARDI" INTEGER_TO_ITALIAN (Y) ELSE "##ERROR##" END IF END INTEGER_TO_ITALIAN Appunti di informatica libera 2003.01.01 --- Copyright © 2000-2003 Daniele Giacomini -- daniele @ swlibero.org Parte lxxiv Annotazioni sulla distribuzione Debian 355 Configurazione di una distribuzione Debian . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3990 355.1 Procedura di inizializzazione del sistema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3990 355.2 Configurazione del sistema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3996 355.3 Configurazione di shell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3998 355.4 Utenti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3998 355.5 Stampa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3998 355.6 Configurazione del nome del sistema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3999 355.7 Configurazione dei permessi degli eseguibili . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4000 355.8 Riferimenti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4000 356 Accorgimenti per una distribuzione Debian . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4001 356.1 Raccogliere gli aggiornamenti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4001 356.2 Realizzazione di una copia personale della distribuzione . . . . . . . . . . . . . . . . . . 4005 356.3 Riferimenti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4011 3989 Capitolo 355 Configurazione di una distribuzione Debian Dal punto di vista della configurazione, la distribuzione Debian cerca di interferire il meno possibile con le convenzioni acquisite nei sistemi Unix. In questo senso, sono poche le particolarità da tenere in considerazione per amministrare un sistema GNU/Linux Debian. 355.1 Procedura di inizializzazione del sistema La procedura di inizializzazione del sistema è attivata dal’eseguibile ‘init’ attraverso le indicazioni di ‘/etc/inittab’. I livelli di esecuzione sono indicati brevemente nella tabella 355.1; di fatto si usa normalmente il livello numero due. Tabella 355.1. Livelli di esecuzione tipici di una distribuzione Debian. Livello di esecuzione 0 1 2 3 4 5 6 Utilizzo Arresto del sistema. Utente singolo. Livello normale per la multiutenza. Livello ausiliario per la multiutenza. Livello disponibile per la multiutenza. Livello disponibile per la multiutenza. Riavvio del sistema. Il file ‘/etc/inittab’ è quello che dirige il funzionamento di Init; analizzandone il contenuto si può intendere il ruolo degli script della procedura di inizializzazione del sistema. # /etc/inittab: init(8) configuration. # $Id: inittab,v 1.8 1998/05/10 10:37:50 miquels Exp $ # The default runlevel. id:2:initdefault: # Boot-time system configuration/initialization script. # This is run first except when booting in emergency (-b) mode. si::sysinit:/etc/init.d/rcS # What to do in single-user mode. ~~:S:wait:/sbin/sulogin # # # # # # # /etc/init.d executes the S and K scripts upon change of runlevel. Runlevel 0 is Runlevel 1 is Runlevels 2-5 Runlevel 6 is halt. single-user. are multi-user. reboot. l0:0:wait:/etc/init.d/rc 0 l1:1:wait:/etc/init.d/rc 1 l2:2:wait:/etc/init.d/rc 2 l3:3:wait:/etc/init.d/rc 3 l4:4:wait:/etc/init.d/rc 4 l5:5:wait:/etc/init.d/rc 5 l6:6:wait:/etc/init.d/rc 6 # Normally not reached, but fallthrough in case of emergency. z6:6:respawn:/sbin/sulogin # What to do when CTRL-ALT-DEL is pressed. ca:12345:ctrlaltdel:/sbin/shutdown -t1 -a -r now 3990 Configurazione di una distribuzione Debian 3991 # Action on special keypress (ALT-UpArrow). kb::kbrequest:/bin/echo "Keyboard Request--edit /etc/inittab to let this work." # What to do when the power fails/returns. pf::powerwait:/etc/init.d/powerfail start pn::powerfailnow:/etc/init.d/powerfail now po::powerokwait:/etc/init.d/powerfail stop # /sbin/getty invocations for the runlevels. # # The "id" field MUST be the same as the last # characters of the device (after "tty"). # # Format: # <id>:<runlevels>:<action>:<process> 1:2345:respawn:/sbin/getty 38400 tty1 2:23:respawn:/sbin/getty 38400 tty2 3:23:respawn:/sbin/getty 38400 tty3 4:23:respawn:/sbin/getty 38400 tty4 5:23:respawn:/sbin/getty 38400 tty5 6:23:respawn:/sbin/getty 38400 tty6 # Example how to put a getty on a serial line (for a terminal) # #T0:23:respawn:/sbin/getty -L ttyS0 9600 vt100 #T1:23:respawn:/sbin/getty -L ttyS1 9600 vt100 # Example how to put a getty on a modem line. # #T3:23:respawn:/sbin/mgetty -x0 -s 57600 ttyS3 355.1.1 Collegamento tra i vari componenti della procedura di inizializzazione del sistema Attraverso il file ‘/etc/inittab’ vengono indicati due script fondamentali, attraverso cui si articola la procedura di inizializzazione del sistema. Si tratta di ‘/etc/init.d/rcS’ e ‘/etc/ init.d/rc’. Il primo viene utilizzato a ogni avvio del sistema e da questo dipendono le operazioni che vanno svolte una volta sola in quella occasione; il secondo serve ogni volta che si cambia il livello di esecuzione. • ‘/etc/init.d/’ È la directory che raccoglie gli script utilizzati nella fase di avvio del sistema e in quella di arresto. • ‘/etc/init.d/rcS’ È lo script di inizializzazione del sistema, organizzato in modo tale da eseguire gli script contenuti nella directory ‘/etc/rcS.d/’, con l’argomento ‘start’. Per la precisione, si tratta di tutti gli script che corrispondono al modello ‘S??*’, ovvero quelli che hanno un nome che inizia con una lettera «S» maiuscola ed è lungo almeno tre caratteri. In realtà, gli script della directory ‘/etc/rcS.d/’ sono solo collegamenti simbolici a script reali contenuti nella directory ‘/etc/init.d/’. In questo modo, tra le altre cose, si configura la tastiera, si attiva la memoria virtuale, si verifica il file system principale e lo si rimonta in lettura e scrittura. Se l’attivazione del file system principale fallisce, viene attivata una shell di emergenza per permettere una correzione del problema. Successivamente si attivano i moduli del kernel, quindi si passa al controllo degli altri file system e al loro montaggio. Infine, si configura e si attiva la rete, assieme ad altre operazioni di importanza minore, come il montaggio di file system di rete. Configurazione di una distribuzione Debian 3992 È importante osservare che lo script ‘/etc/init.d/rcS’ carica inizialmente il file ‘/etc/default/rcS’, che in pratica viene usato per la sua configurazione, definendo alcune variabili di ambiente che dopo vengono analizzate. A queste variabili possono avere accesso anche gli script avviati da ‘/etc/init.d/rcS’. Al termine del suo lavoro normale, lo script ‘/etc/init.d/rcS’ esegue anche gli script che trova nella directory ‘/etc/rc.boot/’, che esiste ancora per motivi di compatibilità con il passato. • ‘/etc/rc.boot/’ È una directory che dovrebbe essere eliminata nel prossimo futuro. Il suo scopo è contenere script da eseguire all’avvio del sistema, prima di entrare in un livello di esecuzione particolare. In generale, questi script dovrebbero essere spostati nella directory ‘/etc/init.d/’, collegandoli poi con dei collegamenti simbolici opportuni nella directory ‘/etc/rcS.d/’. • ‘/etc/init.d/rc’ È lo script principale per il controllo dei livelli di esecuzione. Viene utilizzato da Init, attraverso le indicazioni di ‘/etc/inittab’, con un argomento corrispondente al numero di livello di esecuzione da attivare. /etc/init.d/rc livello_di_esecuzione Semplificando un po’ le cose, ‘/etc/init.d/rc’ si limita ad avviare tutti gli script che trova nella directory ‘/etc/n .d/’ (dove n rappresenta il livello di esecuzione richiesto), cominciando da quelli che iniziano con la lettera ‘K’ e terminando con quelli che iniziano con la lettera ‘S’. In realtà, questi script sono contenuti nella directory ‘/etc/init.d/’, con nomi più espressivi, mentre nelle directory ‘/etc/rcn .d/’ sono contenuti solo dei collegamenti simbolici con nomi scelti appositamente per definire l’ordine in cui le operazioni devono essere svolte. In questo modo, è possibile definire i livelli di esecuzione lasciati a disposizione, semplicemente copiandovi all’interno i collegamenti simbolici necessari e senza toccare alcuno script. • ‘/etc/rcn .d/’ Come accennato, si tratta delle directory riferite a ogni livello di esecuzione (n rappresenta il numero del livello stesso). Al loro interno si trovano solo collegamenti simbolici riferiti agli script che si vuole siano eseguiti. Quando viene selezionato il livello di esecuzione relativo, vengono eseguiti in ordine alfabetico, prima gli script (o meglio i collegamenti) che iniziano con la lettera ‘K’ (Kill) nella forma /etc/rcn .d/script stop allo scopo di disattivare il servizio particolare cui si riferiscono, quindi quelli che iniziano con la lettera ‘S’ (Start), nella forma seguente: /etc/rcn .d/script start • ‘/etc/init.d/’ È il vero contenitore degli script utilizzati dalla procedura di inizializzazione del sistema. Questi vengono utilizzati indirettamente attraverso collegamenti simbolici contenuti nella directory ‘/etc/rcS.d/’ e nelle directory ‘/etc/rcn .d/’. Configurazione di una distribuzione Debian 3993 Molti di questi script possono essere utilizzati dall’amministratore per disattivare o attivare un servizio particolare, senza dover intervenire direttamente nel livello di esecuzione e senza dover ricordare tutte le implicazioni di un servizio particolare. Il formato generale è il seguente: /etc/init.d/servizio {start|stop|restart|force-reload} • ‘/sbin/start-stop-daemon’ Si tratta di un programma specifico della distribuzione Debian (dovrebbe far parte del pacchetto ‘dpkg’) che facilita l’avvio e la conclusione del funzionamento dei demoni avviati attraverso la procedura di inizializzazione del sistema. Per esempio, l’avvio del demone ‘gpm’ viene fatto nel modo seguente (le opzioni di ‘gpm’ sono solo un esempio per semplificare lo script; si tenga presente inoltre che la riga significativa è divisa in due per motivi tipografici, utilizzando il simbolo ci continuazione ‘\’ delle shell Bourne): echo -n "Starting mouse interface server: gpm" start-stop-daemon --start --quiet --exec /usr/sbin/gpm -- \ -m /dev/mouse -t ms echo "." Nello stesso modo, l’arresto del demone viene fatto nel modo seguente: echo -n "Stopping mouse interface server: gpm" start-stop-daemon --stop --quiet --oknodo --pidfile /var/run/gpmpid \ --exec /usr/sbin/gpm echo "." L’uso di ‘start-stop-daemon’ può essere approfondito leggendo la sua pagina di manuale: start-stop-daemon(8). 355.1.2 /etc/init.d/* I file contenuti nella directory ‘/etc/init.d/’, quando si riferiscono a dei servizi, hanno una struttura abbastanza comune, simile a quella seguente. Si fa riferimento all’ipotetico servizio «pippo», a cui corrisponde un demone con lo stesso nome. #!/bin/sh # # Avvia o arresta il servizio «pippo» gestito attraverso il demone «pippo» # # Tizio Tizi <[email protected]> # Prima di cominciare verifica che esista l’eseguibile del demone. test -x /usr/sbin/pippo || exit 0 # Analizza l’argomento e vi si adegua. case "$1" in start) echo -n "Avvio di un servizio inutile: pippo" start-stop-daemon --start --quiet --exec /usr/sbin/pippo echo "." ;; stop) echo -n "Arresto di un servizio inutile: pippo" start-stop-daemon --stop --quiet \ --pidfile /var/run/pippo.pid --exec /usr/sbin/pippo echo "." ;; restart) $0 stop $0 start Configurazione di una distribuzione Debian 3994 ;; reload) echo -n "Rilettura della configurazione di pippo..." start-stop-daemon --stop --signal 1 --quiet \ --pidfile /var/run/named.pid --exec /usr/sbin/named echo "fatto." ;; force-reload) $0 restart ;; *) echo "Utilizzo: /etc/init.d/pippo {start|stop|reload|restart|force-reload}" >&2 exit 1 ;; esac exit 0 Nella prima parte viene verificato che esista effettivamente l’eseguibile del demone ‘pippo’; in caso contrario conclude lo script, ma senza restituire un errore (‘exit 0’). Subito dopo si passa all’analisi del primo (e unico) argomento fornito allo script: • se è ‘start’, si provvede ad avviare il demone attraverso ‘start-stop-daemon’; • se è ‘stop’, si provvede a eliminare il processo relativo, utilizzando l’informazione sul numero PID che dovrebbe essere contenuta nel file ‘/var/run/pippo.pid’, creato dal demone ‘pippo’ stesso; • se è ‘restart’, vengono ripetute le operazioni corrispondenti a ‘stop’ e ‘start’ in sequenza; • se è ‘reload’ (si tratta di un’opzione che non è disponibile in tutti gli script di questo tipo), viene inviato un segnale ‘SIGHUP’ (1) al demone, in modo che questo rilegga la configurazione; • se è ‘force-reload’, viene eseguita in pratica l’operazione corrispondente a ‘restart’, con lo scopo di obbligare alla rilettura della configurazione, anche se ciò costringe ad arrestare e a riavviare il servizio. 355.1.3 /etc/rc?.d/ Le directory ‘/etc/rcn .d/’ e ‘/etc/rcS.d/’, servono a contenere una serie di collegamenti simbolici che puntano a script reali contenuti in ‘/etc/init.d’. I due listati seguenti si riferiscono al contenuto ipotetico di ‘/etc/rc1.d/’ e ‘/etc/rc2.d/’: lrwxrwxrwx lrwxrwxrwx lrwxrwxrwx lrwxrwxrwx lrwxrwxrwx lrwxrwxrwx lrwxrwxrwx lrwxrwxrwx lrwxrwxrwx lrwxrwxrwx lrwxrwxrwx lrwxrwxrwx 1 1 1 1 1 1 1 1 1 1 1 1 root root root root root root root root root root root root root root root root root root root root root root root root 14 17 21 17 14 14 13 17 15 13 13 20 ago ago ago ago ago ago ago ago ago ago ago ago 9 9 9 9 9 9 9 9 9 9 9 9 15:18 15:18 15:18 15:18 15:18 15:18 15:18 15:18 15:18 15:18 15:18 15:18 K11cron -> ../init.d/cron K12kerneld -> ../init.d/kerneld K15netstd_init -> ../init.d/netstd_init K18netbase -> ../init.d/netbase K19bind -> ../init.d/bind K20exim -> ../init.d/exim K20gpm -> ../init.d/gpm K20logoutd -> ../init.d/logoutd K20lprng -> ../init.d/lprng K20ppp -> ../init.d/ppp K20ssh -> ../init.d/ssh K25nfs-server -> ../init.d/nfs-server Configurazione di una distribuzione Debian 3995 lrwxrwxrwx lrwxrwxrwx lrwxrwxrwx lrwxrwxrwx 1 1 1 1 root root root root root root root root 21 16 18 16 ago ago ago ago 9 9 9 9 15:18 15:18 15:18 15:18 K30netstd_misc -> ../init.d/netstd_misc K89pcmcia -> ../init.d/pcmcia K90sysklogd -> ../init.d/sysklogd S20single -> ../init.d/single lrwxrwxrwx lrwxrwxrwx lrwxrwxrwx lrwxrwxrwx lrwxrwxrwx lrwxrwxrwx lrwxrwxrwx lrwxrwxrwx lrwxrwxrwx lrwxrwxrwx lrwxrwxrwx lrwxrwxrwx lrwxrwxrwx lrwxrwxrwx lrwxrwxrwx lrwxrwxrwx 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 root root root root root root root root root root root root root root root root root root root root root root root root root root root root root root root root 18 16 17 21 17 14 14 13 17 15 13 13 20 21 14 19 ago ago ago ago ago ago ago ago ago ago ago ago ago ago ago ago 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 15:18 15:18 15:18 15:18 15:18 15:18 15:18 15:18 15:18 15:18 15:18 15:18 15:18 15:18 15:18 15:18 S10sysklogd -> ../init.d/sysklogd S11pcmcia -> ../init.d/pcmcia S12kerneld -> ../init.d/kerneld S15netstd_init -> ../init.d/netstd_init S18netbase -> ../init.d/netbase S19bind -> ../init.d/bind S20exim -> ../init.d/exim S20gpm -> ../init.d/gpm S20logoutd -> ../init.d/logoutd S20lprng -> ../init.d/lprng S20ppp -> ../init.d/ppp S20ssh -> ../init.d/ssh S25nfs-server -> ../init.d/nfs-server S30netstd_misc -> ../init.d/netstd_misc S89cron -> ../init.d/cron S99rmnologin -> ../init.d/rmnologin I collegamenti che iniziano con la lettera «S» vengono avviati ordinatamente all’attivazione del livello di esecuzione corrispondente, con l’argomento ‘start’, mentre quelli che iniziano con la lettera «K» vengono avviati prima di passare a un nuovo livello di esecuzione, con l’argomento ‘stop’. Il numero che segue la lettera «S» e «K», serve a definire un ordine (alfanumerico), corrispondente a quello in cui i servizi vanno avviati o arrestati. Se si volesse aggiungere uno script, nella directory ‘/etc/init.d/’ per la gestione di un servizio addizionale, si dovrebbero predisporre i collegamenti relativi nella directory in cui servono: per esempio potrebbe essere necessario un collegamento «K» nelle directory ‘/etc/rc0.d/’, ‘/etc/rc1.d/’ e ‘/etc/rc6.d/’, mentre nelle altre directory ‘/etc/rc[2-5].d/’ si potrebbe inserire un collegamento «S», a seconda di come si vogliono gestire i livelli di esecuzione da due a cinque. La distribuzione Debian non dispone di uno script ‘rc.local’. Per ottenere lo stesso risultato, si può predisporre uno script normale, che eventualmente non sia influenzato dagli argomenti; quindi si devono predisporre i collegamenti simbolici di tipo «S» necessari, probabilmente con un numero abbastanza elevato, in modo da farlo intervenire alla fine della procedura di avvio del sistema. 355.1.4 # update-rc.d Il programma ‘update-rc.d’ permette di facilitare la creazione e l’eliminazione dei collegamenti simbolici nelle directory ‘/etc/rcn .d/’. In generale, il suo utilizzo non è indispensabile, ma torna molto utile nella realizzazione di script legati all’installazione e alla disinstallazione dei pacchetti. Qui viene descritto solo il suo uso elementare, dal momento che si può fare tutto quello che si vuole anche senza l’aiuto di questo programma. Eventualmente si può consultare la sua pagina di manuale update-rc.d(8). update-rc.d nome_script remove update-rc.d nome_script defaults Nel primo caso, ‘update-rc.d’ elimina i collegamenti simbolici, riferiti allo script indicato, da tutte le directory ‘/etc/rcn .d/’, compresa la directory ‘/etc/rcS.d/’, purché questo sia effettivamente assente dalla directory ‘/etc/init.d/’. Configurazione di una distribuzione Debian 3996 Nel secondo caso, ‘update-rc.d’ crea i collegamenti simbolici, riferiti allo script indicato, che deve trovarsi nella directory ‘/etc/init.d/’, in base a ciò che di solito è più opportuno: nelle directory corrispondenti ai livelli zero, uno e sei, vengono creati dei collegamenti di tipo K20nome_script Mentre nelle directory dei livelli da due a cinque, vengono creati dei collegamenti di tipo S20nome_script Per esempio, se è stato creato lo script ‘pippo’, ed è stato collocato nella directory ‘/etc/ init.d/’, si possono predisporre i collegamenti simbolici con il comando seguente: # update-rc.d pippo defaults[ Invio ] Adding system startup /etc/rc0.d/K20pippo /etc/rc1.d/K20pippo /etc/rc6.d/K20pippo /etc/rc2.d/S20pippo /etc/rc3.d/S20pippo /etc/rc4.d/S20pippo /etc/rc5.d/S20pippo for /etc/init.d/pippo ... -> ../init.d/pippo -> ../init.d/pippo -> ../init.d/pippo -> ../init.d/pippo -> ../init.d/pippo -> ../init.d/pippo -> ../init.d/pippo Una volta eliminato lo script ‘pippo’ dalla directory ‘/etc/init.d/’, si possono eliminare i collegamenti simbolici relativi con il comando seguente: # update-rc.d pippo remove[ Invio ] Removing any system startup links for /etc/init.d/pippo ... /etc/rc0.d/K20pippo /etc/rc1.d/K20pippo /etc/rc2.d/S20pippo /etc/rc3.d/S20pippo /etc/rc4.d/S20pippo /etc/rc5.d/S20pippo /etc/rc6.d/K20pippo 355.2 Configurazione del sistema La distribuzione Debian non ha un sistema centralizzato per la configurazione, lasciando che ogni pacchetto gestisca la configurazione a suo modo. L’esempio più importante di questa impostazione sta nella configurazione della rete, che avviene attraverso la modifica diretta di uno script di quelli che appartengono alla procedura di inizializzazione del sistema: ‘/etc/init.d/ network’. 355.2.1 Configurazione della tastiera e dei caratteri dello schermo La configurazione della tastiera riguarda il pacchetto ‘kbd’ e avviene per mezzo dello script ‘/etc/init.d/keymaps.sh’, che viene avviato tramite un collegamento simbolico contenuto nella directory ‘/etc/rcS.d/’. Questo script carica automaticamente la mappa della tastiera corrispondente al file ‘/etc/kbd/default.kmap.gz’. Evidentemente, per definire una mappa per la tastiera differente, basta copiare il file opportuno nella directory ‘/etc/kbd/’, dandogli il nome ‘default.kmap.gz’. Sempre riguardo al pacchetto ‘kbd’, la configurazione dei caratteri dello schermo avviene per mezzo dello script ‘/etc/rc.boot/kbd’, che utilizza le definizioni contenute nel file ‘/etc/ kbd/config’. Questo ultimo file è in pratica un pezzo di script, per cui basta assegnare alla variabile ‘CONSOLE_FONT’ il nome del file di definizione dei caratteri che si desidera. Per esempio, Configurazione di una distribuzione Debian 3997 CONSOLE_FONT=lat1u-16.psf.gz fa sì che venga caricato il file ‘/usr/share/consolefonts/lat1u-16.psf.gz’. 355.2.2 Configurazione della rete Come accennato, la rete viene configurata attraverso lo script ‘/etc/init.d/network’. Questo non ha bisogno di accorgimenti particolari, dal momento che è collegato normalmente solo alla directory ‘/etc/rcS.d/’, che contiene i collegamenti simbolici avviati una volta sola all’avvio del sistema. Di solito, lo script in questione viene creato nel momento in cui si installa la distribuzione per la prima volta, in ogni caso, può bastare una cosa come l’esempio seguente, da modificare opportunamente, soprattutto in base al tipo e al numero di interfacce di rete. #! /bin/sh ifconfig lo 127.0.0.1 netmask 255.0.0.0 # route add -net 127.0.0.0 netmask 255.0.0.0 dev lo route add -host 127.0.0.1 dev lo ifconfig eth0 192.168.1.1 netmask 255.255.255.0 broadcast 192.168.1.255 route add -net 192.168.1.0 netmask 255.255.255.0 dev eth0 route add default gw 192.168.1.254 metric 1 Nella directory ‘/etc/rcS.d/’ basta quindi il collegamento simbolico ‘S40network’, che punta a ‘../init.d/network’. 355.2.3 Configurazione dell’orologio hardware In generale, la correzione dell’orologio hardware si ottiene configurando opportunamente il file ‘/etc/adjtime’. Tuttavia, in fase di avvio del sistema operativo, è importante sapere se l’orologio hardware è puntato sul tempo universale coordinato, oppure sull’ora locale. Dal momento che lo script che si occupa di queste cose è controllato da ‘/etc/init.d/rcS’ e dato che questo viene configurato con il file ‘/etc/default/rcS’, all’interno di questo ultimo si può modificare la variabile ‘GMT’. In pratica, per indicare che l’orologio hardware è posizionato sul tempo universale si assegna il valore ‘-u’: # Set GMT="-u" if your system clock is set to GMT, and GMT="" if not. GMT="-u" 355.2.4 Configurazione del mouse per lo schermo a caratteri La gestione del mouse per lo schermo a caratteri avviene attraverso il demone ‘gpm’, come di consueto. L’avvio e l’arresto del servizio è organizzato come al solito attraverso uno script contenuto nella directory ‘/etc/init.d/’. Normalmente, questo script carica la configurazione contenuta nel file ‘/etc/gpm.conf’. Attraverso le variabili dichiarate in questo file di configurazione, lo script di avvio del servizio genera le opzioni opportune per la riga di comando di ‘gpm’. 3998 Configurazione di una distribuzione Debian 355.3 Configurazione di shell Secondo la politica della distribuzione Debian, se un programma ha bisogno che siano definite delle variabili di ambiente, o delle funzioni di shell, per questo deve essere predisposto uno script di avvio apposito, in modo da non dover disturbare l’utente con questa configurazione. In pratica, la configurazione globale delle shell non deve essere modificata dall’installazione di un pacchetto. In generale, se necessario per qualche ragione, l’amministratore del sistema può intervenire nel file ‘/etc/profile’ e negli altri file di configurazione delle altre shell. Come al solito, potrebbe convenire l’inserimento di alcuni alias per evitare la cancellazione involontaria dei file e inoltre si potrebbe impostare il linguaggio predefinito. L’esempio seguente fa riferimento al caso della shell Bash: alias rm=’rm -i’ alias cp=’cp -i’ alias mv=’mv -i’ LANG="it_IT.ISO-8859-1" export LANG Per quanto riguarda la configurazione personale delle shell, non è previsto niente di particolare, a parte il caso dell’utente ‘root’. 355.4 Utenti Con la distribuzione Debian si utilizzano preferibilmente i programmi ‘adduser’ e ‘addgroup’ per registrare nel sistema degli utenti nuovi. Questi due sono un programma Perl unico, che si configura con il file ‘/etc/adduser.conf’. In generale, non dovrebbe essere necessario modificare questa configurazione, soprattutto per non uscire dalla politica predefinita della distribuzione. In generale, si può osservare che vengano gestiti i gruppi privati, per cui, alla registrazione di un utente, viene aggiunto anche un nuovo gruppo con lo stesso nome e lo stesso numero GID. In generale, gli utenti e i gruppi ricevono un numero UID e GID compreso tra 1000 e 29999; l’utente ‘nobody’ ha il numero UID 65534, ed è abbinato al gruppo ‘nogroup’, con lo stesso numero GID. 355.5 Stampa Il sistema di stampa adottato dalla distribuzione Debian è LPRng, che in generale è abbastanza compatibile con quello tradizionale dello Unix BSD. Rispetto ad altre distribuzioni GNU/Linux, Debian lascia all’amministratore la configurazione manuale del file ‘/etc/printcap’ che è comunque piuttosto semplice. Assieme al pacchetto di LPRng viene installato normalmente anche Magicfilter, che come suggerisce il nome è un sistema di filtri per stampanti. Nella directory ‘/etc/magicfilter/’ si trovano una serie di script di Magicfilter, uno per ogni tipo di stampante previsto. È sufficiente modificare la configurazione del file ‘/etc/printcap’, in modo da utilizzare il filtro adatto per la propria stampante. L’esempio seguente definisce la stampante ‘lp’ abbinata alla prima porta parallela a cui si trova connessa una stampante compatibile con il modello HP Laserjet: # /etc/printcap: printer capability database. See printcap(5). # You can use the filter entries df, tf, cf, gf etc. for # your own filters. See the printcap(5) manual page for further Configurazione di una distribuzione Debian 3999 # details. lp|HP Laserjet :lp=/dev/lp0 :sd=/var/spool/lpd/lp :af=/var/log/lp-acct :lf=/var/log/lp-errs :if=/etc/magicfilter/laserjet-filter :pl#66 :pw#80 :pc#150 :mx#0 :sh Come si può osservare, le righe della direttiva ‘lp’ non sono terminate dal simbolo di continuazione ‘\’, che con LPRng non è necessario. Per determinare quale sia il filtro migliore per la propria stampante, occorre fare qualche prova, dal momento che per uno stesso modello ci possono essere diverse alternative. Per quanto riguarda la stampa dei file di testo, volendo sfruttare sistematicamente A2ps per l’impaginazione, si può modificare in coda allo script di Magicfilter l’istruzione seguente: #! /usr/sbin/magicfilter #... # Default entry -- for normal (text) files. MUST BE LAST. #default cat \eE\e&k2G\e(0N \eE default pipe a2ps --portrait --columns=1 --margin=30 --no-header --borders=no --chars-per-line=80 --font-size=10 --interpret=yes --output=2> /dev/null \ \ \ \ 355.5.1 Configurazione del formato della carta La distribuzione Debian organizza la definizione del formato della carta attraverso il file ‘/etc/ papersize’, il quale deve contenere la sigla corrispondente. Di solito i programmi interpretano correttamente i nomi: ‘a3’, ‘a4’, ‘a5’, ‘b5’, ‘letter’, ‘legal’. Eventualmente, per gli altri formati si può utilizzare una delle definizioni comprensibili per Ghostscript. A questo file di configurazione è abbinata una libreria, che in teoria dovrebbe essere utilizzata dai programmi che hanno qualcosa a che fare con la stampa. Per esempio, Ghostscript non ha bisogno dell’opzione ‘-sPAPERSIZE’ perché riesce a ottenere questa informazione dal file di configurazione. 355.6 Configurazione del nome del sistema Il nome del sistema, così come lo intende il programma di servizio ‘hostname’, viene definito nel file ‘/etc/hostname’. Da lì viene letto in fase di avvio del sistema. Un altro file di configurazione simile è ‘/etc/mailname’, che dovrebbe contenere il nome di dominio completo da utilizzare per inviare messaggi di posta elettronica. Configurazione di una distribuzione Debian 4000 355.7 Configurazione dei permessi degli eseguibili Per evitare la proliferazione incontrollata di permessi strani attribuiti agli eseguibili, soprattutto per ciò che riguarda i bit SUID e SGID, la distribuzione GNU/Linux Debian organizza uno script eseguito giornalmente nell’ambito del sistema Cron, con lo scopo di controllare e correggere tali permessi. Questo script si avvale della configurazione contenuta nel file ‘/etc/suid.conf’, che contiene semplicemente un elenco di eseguibili con i permessi che dovrebbero avere. Segue un estratto molto semplice e intuitivo del suo contenuto: # Configuration File for suid programs or special permissions # # The format is: # package file user group permissions lsof-2.0.35 /usr/sbin/lsof root kmem 2755 emacs /usr/lib/emacs/20.3/i386-debian-linux-gnu/movemail root mail 2755 emacs /usr/lib/emacs/19.34/i386-debian-linux/movemail root mail 2755 apache-common /usr/bin/htpasswd root root 755 apache-common /usr/lib/apache/suexec root root 755 xscreensaver /usr/X11R6/bin/xscreensaver root shadow 2755 xcdroast /usr/bin/xcdroast root root 4755 ssh /usr/bin/ssh1 root root 4755 ... In generale, questo file viene aggiornato automaticamente ogni volta che si installa un pacchetto che richiede dei permessi speciali sui suoi eseguibili; tuttavia, quando si vuole cambiare qualcosa in modo manuale, occorre ricordarsi di intervenire anche qui per rendere la modifica permanente. 355.8 Riferimenti • Susan G. Kleinmann, Sven Rudolph, Joost Witteveen, The Debian GNU/Linux FAQ, 1999 <http://ftp.it.debian.org/debian/doc/FAQ/> • Ian Jackson, Christian Schwarz, Debian Policy Manual, 1998 <http://ftp.it.debian.org/debian/doc/package-developer/policy.txt.gz> <http://ftp.it.debian.org/debian/doc/package-developer/policy.html.tar.gz> <http://ftp.it.debian.org/debian/doc/package-developer/policy.pdf.gz> <http://ftp.it.debian.org/debian/doc/package-developer/policy.ps.gz> Appunti di informatica libera 2003.01.01 --- Copyright © 2000-2003 Daniele Giacomini -- daniele @ swlibero.org Capitolo 356 Accorgimenti per una distribuzione Debian In generale, la distribuzione GNU/Linux Debian non ha bisogno di accorgimenti particolari. In questo capitolo vengono annotate poche cose; in particolare sul modo di gestire una raccolta degli archivi Debian e sulla possibilità di realizzare una copia personalizzata della distribuzione. 356.1 Raccogliere gli aggiornamenti La distribuzione Debian, data la sua natura collaborativa, è molto dinamica e nel susseguirsi delle sue edizioni viene prodotta una grande quantità di pacchetti aggiornati. Data la dimensione che ha raggiunto ormai la distribuzione, è improbabile che si riesca ad avere sempre una copia completa della distribuzione; piuttosto è facile trovare nelle riviste dei CD-ROM con questo o quel gruppo di applicativi, più o meno aggiornati. Volendo realizzare una propria copia locale della distribuzione (su disco fisso, o su dischi rimovibili), occorre realizzare qualche script per gestire un po’ meglio la cosa. 356.1.1 Composizione del nome degli archivi Debian Il nome degli archivi Debian è organizzato secondo la struttura seguente: nome_del_pacchetto _versione_e_revisione .deb Per essere precisi, questo è il nome normale, da utilizzare quando si conosce a quale architettura è destinato, in base alla directory in cui si trova a essere conservato. Alle volte, per qualche ragione, il nome degli archivi che si trovano in circolazione non è conforme a questo modello; tuttavia, con l’aiuto delle informazioni contenute negli archivi stessi, è possibile riprodurre il nome standard. I comandi seguenti, che utilizzano ‘dpkg’, permettono di ottenere le informazioni necessarie a ricostruire il nome di un archivio Debian: dpkg --field archivio package restituisce il nome del pacchetto; dpkg --field archivio version Restituisce la stringa che rappresenta la versione e la revisione del pacchetto. Quello che segue è l’esempio di uno script in grado di scandire gli archivi contenuti nella directory corrente, allo scopo di modificarne il nome se questo non corrisponde al modello standard. La scansione viene fatta in due fasi, per verificare alla fine quali archivi non sono stati corretti. #!/bin/bash #======================================================================= # debian-nomi # # Interviene nella directory *corrente* correggendo i nomi degli # archivi che sembrano non essere coerenti. Si utilizza in particolare: # # dpkg --field <archivio> package # dpkg --field <archivio> version # #======================================================================= #----------------------------------------------------------------------# Inizializza le variabili di ambiente che servono per accumulare # i valori per il confronto. #----------------------------------------------------------------------- 4001 4002 Accorgimenti per una distribuzione Debian ARCHIVIO="" PACCHETTO="" VERSIONE="" NOME_CORRETTO="" #---------------------------------------------------------------------# Inizia il ciclo di scansione degli archivi Debian che si trovano # nella directory corrente. # Prima fase silenziosa. #---------------------------------------------------------------------for ARCHIVIO in *.deb do #-----------------------------------------------------------------# Se il nome è «*.deb», non ci sono file del genere. #-----------------------------------------------------------------if [ "$ARCHIVIO" = "*.deb" ] then #-------------------------------------------------------------# Non si fa nulla. #-------------------------------------------------------------exit fi #-----------------------------------------------------------------# Estrae il nome del pacchetto. #-----------------------------------------------------------------PACCHETTO=‘dpkg --field $ARCHIVIO package‘ #-----------------------------------------------------------------# Estrae la versione del pacchetto. #-----------------------------------------------------------------VERSIONE=‘dpkg --field $ARCHIVIO version‘ #-----------------------------------------------------------------# Compone il nome teorico. #-----------------------------------------------------------------NOME_CORRETTO="${PACCHETTO}_${VERSIONE}.deb" #-----------------------------------------------------------------# Confronta con il nome dell’archivio. #-----------------------------------------------------------------if [ "$NOME_CORRETTO" != "$ARCHIVIO" ] then #-------------------------------------------------------------# I nomi sono differenti. # Modifica il nome solo se è possibile. #-------------------------------------------------------------echo "n" | mv -i "$ARCHIVIO" "$NOME_CORRETTO" 2> /dev/null fi done #---------------------------------------------------------------------# Inizia il ciclo di scansione degli archivi Debian che si trovano # nella directory corrente. # Seconda fase di verifica. #---------------------------------------------------------------------for ARCHIVIO in *.deb do #-----------------------------------------------------------------# Estrae il nome del pacchetto. #-----------------------------------------------------------------PACCHETTO=‘dpkg --field $ARCHIVIO package‘ #-----------------------------------------------------------------# Estrae la versione del pacchetto. #-----------------------------------------------------------------VERSIONE=‘dpkg --field $ARCHIVIO version‘ Accorgimenti per una distribuzione Debian 4003 #-----------------------------------------------------------------# Compone il nome teorico. #-----------------------------------------------------------------NOME_CORRETTO="${PACCHETTO}_${VERSIONE}.deb" #-----------------------------------------------------------------# Confronta con il nome dell’archivio. #-----------------------------------------------------------------if [ "$NOME_CORRETTO" != "$ARCHIVIO" ] then #-------------------------------------------------------------# A quanto pare, il nome di questo archivio non è stato # corretto. #-------------------------------------------------------------echo "Non è stato possibile correggere il nome dell’archivio" echo "$ARCHIVIO, che dovrebbe chiamarsi $NOME_CORRETTO." echo fi done #======================================================================= 356.1.2 Eliminazione delle versioni precedenti La versione di un pacchetto è composta da due parti: la versione (vera e propria) e la revisione. Si tratta di due stringhe unite da un trattino: versione -revisione In generale, non è facile confrontare questi valori e per fortuna viene in aiuto ‘dpkg’ che è in grado di affermare quale sia più recente. In pratica, si utilizza uno dei comandi seguenti, per determinare se una versione è maggiore, minore o uguale all’altra: dpkg --compare-versions versione1 gt versione2 dpkg --compare-versions versione1 lt versione2 dpkg --compare-versions versione1 eq versione2 Lo script seguente serve a scandire gli archivi Debian contenuti nella directory corrente, allo scopo di eliminare quelli che contengono uno stesso pacchetto ma di una versione precedente a quanto già disponibile. Durante la scansione, si presume che i nomi degli archivi siano composti correttamente, in modo tale che gli archivi di uno stesso pacchetto si trovino di seguito, nella sequenza alfabetica. #!/bin/bash #======================================================================= # debian-doppi # # Interviene nella directory *corrente* eliminando i file doppi, # più vecchi. Il confronto viene fatto utilizzando: # # dpkg --field <archivio> package # dpkg --field <archivio> version # dpkg --compare-versions <versione1> eq <versione0> # dpkg --compare-versions <versione1> gt <versione0> # dpkg --compare-versions <versione1> lt <versione0> # #======================================================================= #----------------------------------------------------------------------# Inizializza le variabili di ambiente che servono per accumulare # i valori per il confronto. #----------------------------------------------------------------------- Accorgimenti per una distribuzione Debian 4004 ARCHIVIO0="" ARCHIVIO1="" PACCHETTO0="" PACCHETTO1="" VERSIONE0="" VERSIONE1="" #---------------------------------------------------------------------# Inizia il ciclo di scansione degli archivi Debian che si trovano # nella directory corrente. #---------------------------------------------------------------------for ARCHIVIO1 in *.deb do #-----------------------------------------------------------------# Se il nome è «*.deb», non ci sono file del genere. #-----------------------------------------------------------------if [ "$ARCHIVIO1" = "*.deb" ] then #-------------------------------------------------------------# Non si fa nulla. #-------------------------------------------------------------exit fi #-----------------------------------------------------------------# Estrae il nome del pacchetto. #-----------------------------------------------------------------PACCHETTO1=‘dpkg --field $ARCHIVIO1 package‘ #-----------------------------------------------------------------# Estrae la versione del pacchetto. #-----------------------------------------------------------------VERSIONE1=‘dpkg --field $ARCHIVIO1 version‘ #-----------------------------------------------------------------# Confronta con il pacchetto precedente. #-----------------------------------------------------------------if [ "$PACCHETTO1" == "$PACCHETTO0" ] then if dpkg --compare-versions "$VERSIONE1" eq "$VERSIONE0" then #---------------------------------------------------------# Si tratta di un’anomalia in cui si deve intervenire a # mano. #---------------------------------------------------------echo "Gli archivi seguenti hanno la stessa versione:" echo " $ARCHIVIO0" echo " $ARCHIVIO1" echo #---------------------------------------------------------# In questo caso, non occorre spostare i valori nelle # variabili. #---------------------------------------------------------elif dpkg --compare-versions "$VERSIONE1" gt "$VERSIONE0" then #---------------------------------------------------------# Si elimina l’archivio del pacchetto più vecchio. #---------------------------------------------------------rm -f "$ARCHIVIO0" echo "Eliminato $ARCHIVIO0" #---------------------------------------------------------# Sposta i valori nelle variabili. #---------------------------------------------------------- Accorgimenti per una distribuzione Debian 4005 ARCHIVIO0="$ARCHIVIO1" PACCHETTO0="$PACCHETTO1" VERSIONE0="$VERSIONE1" elif dpkg --compare-versions "$VERSIONE1" lt "$VERSIONE0" then #---------------------------------------------------------# Si elimina l’archivio del pacchetto più vecchio. #---------------------------------------------------------rm -f "$ARCHIVIO1" echo "Eliminato $ARCHIVIO1" #---------------------------------------------------------# In questo caso, non occorre spostare i valori nelle # variabili. #---------------------------------------------------------else #---------------------------------------------------------# Questo caso non dovrebbe verificarsi. #---------------------------------------------------------echo "C’è un errore nel confronto degli archivi seguenti:" echo " $ARCHIVIO0" echo " $ARCHIVIO1" echo #---------------------------------------------------------# In questo caso, non occorre spostare i valori nelle # variabili. #---------------------------------------------------------fi else #-------------------------------------------------------------# Dal momento che i pacchetti sono differenti, si devono # salvare le variabili prima di procedere. #-------------------------------------------------------------ARCHIVIO0="$ARCHIVIO1" PACCHETTO0="$PACCHETTO1" VERSIONE0="$VERSIONE1" fi done #======================================================================= 356.2 Realizzazione di una copia personale della distribuzione Per comprendere come realizzare una copia locale della distribuzione GNU/Linux Debian, occorre andare per gradi, partendo dal caso in cui questa è disponibile completamente in un supporto unico, per poi arrivare a comprendere le differenze che si devono introdurre per ottenere una versione distribuita su più supporti. 356.2.1 Distribuzione completa su un supporto unico Si suppone che l’utente ‘tizio’ voglia predisporre una copia locale della distribuzione Debian, a partire dalla directory ‘/home/tizio/DEBIAN/’. La struttura minima che dovrebbe articolarsi a partire da questa directory dovrebbe essere quella che si vede nella figura 356.1. 4006 Accorgimenti per una distribuzione Debian Figura 356.1. Struttura minima di una distribuzione GNU/Linux Debian organizzata in modo da utilizzare un supporto unico. debian/ |-- dists/ | |-- stable --> ../codename | | | ‘-- codename / | | | |-- main/ | | |-- disks-arch / | | | ‘-- current/ | | | | | ‘-- binary-arch / | | |-- Packages | | |-- Packages.gz | | |-- admin/ | | |-- base/ | | |-- comm/ | | : | | | |-- contrib/ | | | | | ‘-- binary-arch / | | |-- Packages | | |-- Packages.gz | | : | | | |-- non-free/ | | : | | | |-- non-US/ | | : | | | ‘-- local/ | : | ‘-- indices/ ‘-- override.gz In breve, viene descritto il senso di questa struttura. • La directory ‘debian/dists/stable/main/disks-i386/current/’ contiene i file delle immagini dei dischetti (per l’architettura i386) da utilizzare per avviare l’installazione e per riprodurre il sistema minimo precedente alla selezione dei pacchetti. • Le directory ‘debian/dists/stable/*/binary-i386/’ contengono la gerarchia finale dei pacchetti di ogni gruppo (‘main/’, ‘contrib/’, ‘non-free/’, ‘non-US/’ e ‘local/’), che potrebbe essere suddivisa o meno in sezioni (‘admin/’, ‘base/’, ecc.). Da quel punto, poi, si inseriscono gli archivi Debian. • I file ‘Packages’ e ‘Packages.gz’, contenuti nelle directory ‘debian/dists/stable/ */binary-i386/’, sono indispensabili per fornire ai programmi come DSelect e APT le informazioni importanti sui pacchetti. • Il file, o i file ‘debian/indices/override*.gz’ servono per ricostruire correttamente i file ‘Packages’. Il problema nella riproduzione di una distribuzione Debian sta nella creazione dei file ‘Packages’ (e di conseguenza anche ‘Packages.gz’). Per arrivare a questo risultato, occorre definire una stringa che serva a individuare la distribuzione, per esempio: Debian GNU/Linux 2.1 slink personalizzata Accorgimenti per una distribuzione Debian 4007 Inoltre occorre un file override (‘debian/indices/override.gz’ o un altro nome simile), contenente l’abbinamento tra i pacchetti, la priorità e la classificazione in sezioni, come nell’estratto seguente: at cron locales ncurses-term acct adjtimex ... adduser ae amiga-fdisk ... lilo silo ... newt0.25 ppp pppconfig syslinux ... important important standard standard optional optional admin admin admin admin admin admin required required required base base base important important base base optional optional optional optional base base base base Infine occorrono i pacchetti, che si trovano lì dove sono. I file override vanno prelevati da una delle varie riproduzioni speculari, tenendo presente che possono essere aggiornati frequentemente. Di solito, questi file sono suddivisi in base ai raggruppamenti principali in cui si articola una versione: ‘main/’, ‘contrib/’,... Per semplificare le operazioni, può convenire la realizzazione di un file unico, come è stato mostrato nella struttura di esempio. A questo file si farà riferimento come ‘override.gz’.1 Disponendo di questo materiale, si può utilizzare ‘dpkg-scanpackages’ per rigenerare i file ‘Packages’. Eventualmente si può vedere la pagina di manuale dpkg-scanpackages(8). tizio$ cd /home/tizio/DEBIAN/debian Ci si posiziona nella directory principale della distribuzione. tizio$ dpkg-scanpackages ←,→-m "Debian GNU/Linux 2.1 slink personalizzata" ←,→dists/stable/main/binary-i386 indices/override.gz ←,→> dists/stable/main/binary-i386/Packages Si genera il file ‘Packages’ per il gruppo di pacchetti della classificazione ‘main/’ (il comando è stato mostrato suddiviso su più righe per motivi tipografici). tizio$ cat dists/stable/main/binary-i386/Packages ←,→| gzip | dists/stable/main/binary-i386/Packages.gz Si genera il file ‘Packages.gz’, comprimendo ‘Packages’ creato precedentemente. In seguito, si fa la stessa cosa per i raggruppamenti ‘contrib/’, ‘non-free/’, ‘non-US/’ e ‘local/’. 1 I file override originali della distribuzione ‘slink’ hanno i nomi: ‘override.slink.gz’, ‘override.slink.contrib.gz’, ‘override.slink.non-free.gz’ e ‘override.slink.non-US.gz’. Per realizzare la propria copia della distribuzione, nulla vieta di fonderli tutti assieme in un file unico, come descritto in questi esempi, dove si fa riferimento a un solo file ‘override.gz’. 4008 Accorgimenti per una distribuzione Debian Anche se alcuni di questi raggruppamenti non vengono utilizzati, nel senso che non si vogliono tenere pacchetti che vi appartengano, è molto importante che siano predisposte le directory vuote e anche i file ‘Packages*’, per facilitare le operazioni con DSelect e APT. ‘dpkg-scanpackages’ può generare delle segnalazioni di errore, in particolare quando trova un pacchetto che non è indicato nel file override. In generale questo non provoca conseguenze gravi, tranne la mancanza di qualche informazione per quel pacchetto. L’esempio seguente è un estratto di uno dei file ‘Packages’, dove si vede la descrizione del pacchetto ‘wget’. Si deve osservare in particolare che le informazioni dei campi ‘Priority’ e ‘Section’ sono state determinate in base al file override, mentre la descrizione del campo ‘X-Medium’ è stata ottenuta dall’opzione ‘-m’ di ‘dpkg-scanpackages’. Package: wget Version: 1.5.3-1.1 Priority: optional Section: web Maintainer: Nicolás Lichtmaier <[email protected]> Depends: libc6 Architecture: i386 Filename: dists/stable/main/binary-i386/wget_1.5.3-1.1.deb Size: 221932 MD5sum: 323962a35dabbf88edfe665ad70eb382 Description: utility to retrieve files from the WWW via HTTP and FTP Wget [formerly known as Geturl] is a freely available network utility to retrieve files from the World Wide Web using HTTP and FTP, the two most widely used Internet protocols. It works non-interactively, thus enabling work in the background, after having logged off. . The recursive retrieval of HTML pages, as well as FTP sites is supported -- you can use Wget to make mirrors of archives and home pages, or traverse the web like a WWW robot (Wget understands /robots.txt). installed-size: 535 X-Medium: Debian GNU/Linux 2.1 slink personalizzata Per accedere facilmente a questa distribuzione locale, basta configurare APT attraverso il file ‘/etc/apt/sources.list’: deb file:/home/tizio/DEBIAN/debian stable main contrib non-free non-US local Se le dimensioni lo consentono, si può trasferire una copia della gerarchia ‘/home/tizio/ DEBIAN/’ in un disco rimovibile, o in un CD-ROM. 356.2.2 Distribuzione suddivisa su diversi supporti Se si vuole masterizzare un CD-R, o comunque si vuole fare una copia della distribuzione suddividendola in più supporti, le cose si complicano. Per prima cosa si deve iniziare da una copia locale organizzata già nelle suddivisioni che si vogliono ottenere. Supponendo di partire dalla directory ‘/home/tizio/DEBIAN/’, conviene aggiungere altre sottodirectory ulteriori, una per ogni suddivisione che si vuole ottenere: ‘1/’, ‘2/’,... La struttura della gerarchia che si articola a partire da queste sottodirectory deve essere la stessa, anche quando alcuni gruppi di pacchetti (‘main/’, ‘contrib/’, ecc.) risultano senza archivi. La figura 356.2 mostra le varianti rispetto al modello già mostrato. Accorgimenti per una distribuzione Debian 4009 Figura 356.2. Struttura minima di una distribuzione GNU/Linux Debian organizzata in modo da utilizzare più supporti. debian/ |-- .disk/ | ‘-- info | |-- local/ | ‘-- local --> ../stable/local | |-- dists/ | |-- stable --> ../codename | | | ‘-- codename / | |-- main/ | | | | | |-- disks-arch / | | | ‘-- current/ | | | | | ‘-- binary-arch / | | |-- Packages | | |-- Packages.gz | | |-- Packages.cd | | |-- Packages.cd.gz | | : | | | |-- contrib/ | | : | | | |-- non-free/ | | : | | | |-- non-US/ | | : | | | ‘-- local/ | : | ‘-- indices/ ‘-- override.gz Rispetto alla situazione precedente, si aggiunge il file ‘debian/.disk/info’, che deve contenere la stringa di descrizione del supporto, una cosa del tipo Debian GNU/Linux 2.1 slink personalizzata disco 1 oppure Debian GNU/Linux 2.1 slink personalizzata disco 2 ecc., mentre nelle directory ‘debian/dists/stable/*/binary-i386/’ appaiono dei file nuovi: ‘Packages.cd’ e ‘Packages.cd.gz’. Infine, il raggruppamento di pacchetti ‘local’, dovrebbe trovarsi nella directory ‘debian/dists/local/local/’. Probabilmente, conviene realizzare un collegamento simbolico per portarlo nella collocazione normale. I supporti distinti, vengono riconosciuti in base alla stringa contenuta nel file ‘debian/.disk/ info’, che va scelta opportunamente e va utilizzata anche per la definizione del campo ‘X-Medium’. Si comincia dalla preparazione dei file ‘Packages’ e ‘Packages.gz’, più o meno come è stato fatto nella situazione precedente: tizio$ cd /home/tizio/DEBIAN/1/debian tizio$ dpkg-scanpackages -m ‘cat .disk/info‘ ←,→dists/stable/main/binary-i386 indices/override.gz ←- 4010 Accorgimenti per una distribuzione Debian ,→> dists/stable/main/binary-i386/Packages tizio$ cat dists/stable/main/binary-i386/Packages ←,→| gzip | dists/stable/main/binary-i386/Packages.gz Come prima, si fa la stessa cosa per gli altri gruppi di pacchetti e poi si ripete il procedimento per la copia contenuta nella directory ‘/home/tizio/DEBIAN/2/’ (si suppone che si tratti di una suddivisione in due soli supporti): tizio$ cd /home/tizio/DEBIAN/2/debian tizio$ dpkg-scanpackages -m ‘cat .disk/info‘ ←,→dists/stable/main/binary-i386 indices/override.gz ←,→> dists/stable/main/binary-i386/Packages tizio$ cat dists/stable/main/binary-i386/Packages ←,→| gzip | dists/stable/main/binary-i386/Packages.gz Alla fine, si devono realizzare i file ‘Packages.cd’, che si compongono della somma dei file ‘Packages’ di ogni gruppo: tizio$ cd /home/tizio/DEBIAN/ tizio$ cat 1/debian/dists/stable/main/binary-i386/Packages ←,→2/debian/dists/stable/main/binary-i386/Packages ←,→> 1/debian/dists/stable/main/binary-i386/Packages.cd tizio$ cat 1/debian/dists/stable/main/binary-i386/Packages ←,→2/debian/dists/stable/main/binary-i386/Packages ←,→> 2/debian/dists/stable/main/binary-i386/Packages.cd tizio$ cat 1/debian/dists/stable/main/binary-i386/Packages.cd ←,→| gzip | 1/debian/dists/stable/main/binary-i386/Packages.cd.gz tizio$ cat 2/debian/dists/stable/main/binary-i386/Packages.cd ←,→| gzip | 2/debian/dists/stable/main/binary-i386/Packages.cd.gz In pratica, i file ‘Packages.cd’ contengono le informazioni su tutti i pacchetti del proprio gruppo; sia quelli presenti effettivamente nel supporto che quelli che si trovano negli altri. I programmi come DSelect distingueranno il supporto in base al nome che gli è stato attribuito, indicato nel file ‘debian/.disk/info’ e riportato nel campo ‘X-Medium’ dei file ‘Packages.cd*’. Per accedere facilmente a questa distribuzione locale, spezzata in due o più parti, basta configurare APT attraverso il file ‘/etc/apt/sources.list’: deb file:/home/tizio/DEBIAN/1/debian stable main contrib non-free non-US local deb file:/home/tizio/DEBIAN/2/debian stable main contrib non-free non-US local # ... Per copiare le due strutture in dischi separati, basta trasferire una copia delle gerarchie ‘/home/ tizio/DEBIAN/*/’. Accorgimenti per una distribuzione Debian 4011 356.3 Riferimenti • Susan G. Kleinmann, Sven Rudolph, Joost Witteveen, The Debian GNU/Linux FAQ, 1999 <http://ftp.it.debian.org/debian/doc/FAQ/> • Ian Jackson, Christian Schwarz, Debian Policy Manual, 1998 <http://ftp.it.debian.org/debian/doc/package-developer/policy.txt.gz> <http://ftp.it.debian.org/debian/doc/package-developer/policy.html.tar.gz> <http://ftp.it.debian.org/debian/doc/package-developer/policy.pdf.gz> <http://ftp.it.debian.org/debian/doc/package-developer/policy.ps.gz> Appunti di informatica libera 2003.01.01 --- Copyright © 2000-2003 Daniele Giacomini -- daniele @ swlibero.org 4012 Accorgimenti per una distribuzione Debian Parte lxxv Annotazioni sulla distribuzione Red Hat 357 Configurazione di una distribuzione Red Hat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4014 357.1 Procedura di inizializzazione del sistema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4014 357.2 Configurazione del sistema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4019 357.3 Configurazione di shell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4023 357.4 Utenti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4025 357.5 Stampa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4026 358 Accorgimenti per una distribuzione Red Hat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4029 358.1 Gestione dei pacchetti non ufficiali . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4029 358.2 Personalizzazione e aggiornamento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4034 358.3 Aggiornamento del kernel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4036 358.4 Aggiornamento manuale di un’installazione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4037 358.5 Semplificazione della configurazione della rete . . . . . . . . . . . . . . . . . . . . . . . . . . 4039 358.6 Riferimenti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4041 4013 Capitolo 357 Configurazione di una distribuzione Red Hat La distribuzione Red Hat utilizza un sistema di configurazione composto da script, che non dovrebbero essere modificati, e da file di configurazione utilizzati dagli script, modificabili attraverso programmi che guidano l’amministratore. Il difetto di questo approccio sta nel fatto che non sempre tutto funziona come previsto e allora occorre mettere le mani sui file, lasciando stare i programmi di configurazione.1 357.1 Procedura di inizializzazione del sistema La procedura di inizializzazione del sistema è attivata dal’eseguibile ‘init’ attraverso le indicazioni di ‘/etc/inittab’. I livelli di esecuzione sono: • 0 arresto del sistema; • 1 singolo utente; • 2 multiutente senza l’utilizzo di eventuali NFS; • 3 multiutente; • 4 non definito (disponibile); • 5 multiutente con procedura di accesso grafica; • 6 riavvio. Il file ‘/etc/inittab’ è quello che dirige il funzionamento di Init e analizzandone il contenuto si può intendere il ruolo degli script della procedura di inizializzazione del sistema. # Default runlevel. The runlevels used by RHS are: # 0 - halt (Do NOT set initdefault to this) # 1 - Single user mode # 2 - Multiuser, without NFS (The same as 3, if you do not have networking) # 3 - Full multiuser mode # 4 - unused # 5 - X11 # 6 - reboot (Do NOT set initdefault to this) # id:3:initdefault: # System initialization. si::sysinit:/etc/rc.d/rc.sysinit l0:0:wait:/etc/rc.d/rc l1:1:wait:/etc/rc.d/rc l2:2:wait:/etc/rc.d/rc l3:3:wait:/etc/rc.d/rc l4:4:wait:/etc/rc.d/rc l5:5:wait:/etc/rc.d/rc l6:6:wait:/etc/rc.d/rc 0 1 2 3 4 5 6 # Things to run in every runlevel. ud::once:/sbin/update # Trap CTRL-ALT-DELETE 1 Le notizie che si raccolgono qui, sono riferite a vecchie edizioni della distribuzione. Queste informazioni, benché obsolete, vengono conservate perché potrebbero ancora essere utili. 4014 Configurazione di una distribuzione Red Hat 4015 ca::ctrlaltdel:/sbin/shutdown -t3 -r now # When our UPS tells us power has failed, assume we have a few minutes # of power left. Schedule a shutdown for 2 minutes from now. # This does, of course, assume you have powerd installed and your # UPS connected and working correctly. pf::powerfail:/sbin/shutdown -f -h +2 "Power Failure; System Shutting Down" # If power was restored before the shutdown kicked in, cancel it. pr:12345:powerokwait:/sbin/shutdown -c "Power Restored; Shutdown Cancelled" # Run gettys in standard runlevels 1:12345:respawn:/sbin/mingetty tty1 2:2345:respawn:/sbin/mingetty tty2 3:2345:respawn:/sbin/mingetty tty3 4:2345:respawn:/sbin/mingetty tty4 5:2345:respawn:/sbin/mingetty tty5 6:2345:respawn:/sbin/mingetty tty6 # Run xdm in runlevel 5 x:5:respawn:/usr/bin/X11/xdm -nodaemon 357.1.1 Collegamento tra i vari componenti della procedura di inizializzazione del sistema Attraverso il file ‘/etc/inittab’ vengono indicati due script fondamentali, attraverso cui si articola la procedura di inizializzazione del sistema. Si tratta di ‘/etc/rc.d/rc.sysinit’ e ‘/etc/rc.d/rc’. Il primo viene utilizzato a ogni avvio del sistema e da questo dipendono le operazioni che vanno svolte una volta sola in quella occasione; il secondo serve ogni volta che si cambia il livello di esecuzione. • ‘/etc/rc.d/’ È la directory che raccoglie gli script utilizzati nella fase di avvio del sistema e in quella di arresto. • ‘/etc/rc.d/rc.sysinit’ È lo script di inizializzazione del sistema. In particolare: – attiva la gestione della memoria virtuale per l’uso delle aree di scambio previste nelle partizioni, in base al contenuto del file ‘/etc/fstab’; – verifica il file system principale e al termine ne esegue il montaggio; – verifica la dipendenza dei moduli; – avvia ‘kerneld’ per automatizzare il caricamento dei moduli del kernel; – verifica gli altri file system indicati per questo nel file ‘/etc/fstab’ e al termine ne esegue il montaggio; – elimina o ripulisce i file utilizzati nella sessione di lavoro precedente, che servivano per segnalare lo stato di funzionamento; – configura l’orologio del sistema; – attiva la gestione della memoria virtuale per l’uso delle aree di scambio previste sui file; – eventualmente esegue ‘/etc/rc.d/rc.serial’ per l’attivazione della porta seriale; – eventualmente esegue ‘/etc/rc.d/rc.modules’ per l’attivazione di moduli del kernel. Configurazione di una distribuzione Red Hat 4016 • ‘/etc/rc.d/rc’ È lo script principale per il controllo dei livelli di esecuzione. Viene utilizzato da Init, attraverso le indicazioni di ‘/etc/inittab’, con un argomento corrispondente al numero di livello di esecuzione da attivare. /etc/rc.d/rc livello_di_esecuzione Semplificando un po’ le cose, ‘/etc/rc.d/rc’ si limita a determinare se è stato richiesto un cambiamento nel livello di esecuzione, quindi avvia tutti gli script che trova nella directory ‘/etc/rc.d/rcn .d/’ (dove n rappresenta il livello di esecuzione richiesto), cominciando da quelli che iniziano con la lettera ‘K’ e terminando con quelli che iniziano con la lettera ‘S’. In realtà, questi script sono contenuti nella directory ‘/etc/rc.d/init.d/’, con nomi più espressivi, mentre nelle directory ‘/etc/rc.d/rcn .d/’ sono contenuti solo dei collegamenti simbolici con nomi scelti appositamente per definire l’ordine in cui le operazioni devono essere svolte. In questo modo, è possibile definire il livello di esecuzione numero quattro, lasciato a disposizione, semplicemente copiandovi all’interno i collegamenti simbolici necessari e senza toccare alcuno script. • ‘/etc/rc.d/rcn .d/’ Come accennato, si tratta delle directory riferite a ogni livello di esecuzione (n rappresenta il numero del livello stesso). Al loro interno si trovano solo collegamenti simbolici riferiti agli script che si vuole siano eseguiti. Quando viene selezionato il livello di esecuzione relativo, vengono eseguiti in ordine alfabetico, prima gli script (o meglio i collegamenti) che iniziano con la lettera ‘K’ (Kill) nella forma /etc/rc.d/rcn .d/script stop allo scopo di disattivare il servizio particolare cui si riferiscono, quindi quelli che iniziano con la lettera ‘S’ (Start), nella forma seguente: /etc/rc.d/rcn .d/script start • ‘/etc/rc.d/init.d/’ È il vero contenitore degli script utilizzati dalla procedura di inizializzazione del sistema. Questi vengono utilizzati indirettamente attraverso collegamenti simbolici contenuti nelle directory ‘/etc/rc.d/rcn .d/’. Molti di questi script caricano le informazioni contenute in file di configurazione collocati altrove e ciò rappresenta la base del sistema di configurazione della distribuzione Red Hat, perché permette di limitarsi alla modifica di questi file, invece che intervenire direttamente sugli script stessi. Molti di questi script possono essere utilizzati dall’amministratore per disattivare o attivare un servizio particolare, senza dover utilizzare un livello di esecuzione diverso e senza dover ricordare tutte le implicazioni di un particolare servizio. Il formato generale è il seguente: /etc/rc.d/init.d/servizio {start|stop|status} • ‘/etc/rc.d/init.d/functions’ Si tratta di uno script utilizzato da quasi tutti gli altri, per definire alcune funzioni standard. In particolare, queste funzioni, servono per evitare l’avvio di demoni già in funzione e comunque per uniformare il sistema di avvio e conclusione del loro funzionamento. – ‘pidofproc’ Restituisce attraverso lo standard output i numeri PID abbinati a processi con il nome fornito. Per questo, tenta inizialmente di utilizzare il programma ‘pidof’; se fallisce, Configurazione di una distribuzione Red Hat 4017 analizza quanto contenuto nella directory ‘/var/run/’; come ultima risorsa tenta di analizzare il risultato dell’esecuzione di ‘ps’. – ‘daemon’ Avvia un programma dopo aver verificato che non sia già in funzione, restituendo attraverso lo standard output il nome di questo, senza l’eventuale percorso di avvio. – ‘killproc’ Elimina un processo utilizzando la funzione ‘pidofproc’ per determinare il suo numero PID. Restituisce attraverso lo standard output il nome del processo eliminato. – ‘status’ Restituisce, attraverso lo standard output, lo stato del servizio corrispondente. • ‘/var/lock/subsys/’ Questa directory viene utilizzata dagli script contenuti in ‘/etc/rc.d/init.d/’ per annotare la presenza in funzione di un servizio determinato: se esiste un file (vuoto) con quel nome, il servizio è considerato attivo. 357.1.2 /etc/rc.d/init.d/* I file contenuti nella directory ‘/etc/rc.d/init.d/’, quando si riferiscono a dei servizi, hanno una struttura abbastanza comune, simile a quella seguente. Si fa riferimento all’ipotetico servizio «pippo», a cui corrisponde un demone con lo stesso nome. #!/bin/sh # # chkconfig: 345 85 15 # description: Servizio Pippo. Si tratta di un servizio che non serve \ # a nulla e non interessa a nessuno. # # Caricamento delle funzioni standard. . /etc/rc.d/init.d/functions # Analisi dell’argomento usato nella chiamata. case "$1" in start) echo -n "Avvio del servizio Pippo: " daemon pippo echo touch /var/lock/subsys/pippo ;; stop) echo -n "Spegnimento del servizio Pippo: " killproc pippo rm -f /var/lock/subsys/pippo echo ;; status) status pippo ;; restart) killall -HUP pippo ;; *) echo "Usage: pippo {start|stop|restart|status}" exit 1 esac exit 0 Configurazione di una distribuzione Red Hat 4018 Nella prima parte viene letto il contenuto del file contenente la definizione delle funzioni standard, quindi si analizza il primo argomento fornito allo script. Se era ‘start’ si provvede ad avviare uno o più programmi attraverso la funzione ‘daemon’, quindi si segnala il fatto creando un file vuoto (con ‘touch’) nella directory ‘/var/lock/subsys/’. Se l’argomento era ‘stop’ si provvede a eliminare i processi relativi, normalmente attraverso la funzione ‘killproc’, quindi si elimina il file corrispondente nella directory ‘/var/lock/subsys’. Se l’argomento era ‘status’ si visualizza lo stato del servizio attraverso la funzione ‘status’. Infine, se l’argomento era ‘restart’, di solito si invia un segnale ‘SIGHUP’ al processo corrispondente al servizio. In questi script, alcuni commenti introduttivi hanno un ruolo preciso: servono a definire i livelli di esecuzione con cui questi vengono presi in considerazione, il momento in cui devono essere avviati i servizi relativi e il momento in cui devono essere chiusi gli stessi servizi. Nell’esempio mostrato si tratta del pezzo seguente: # chkconfig: 345 85 15 # description: Servizio Pippo. Si tratta di un servizio che non serve \ # a nulla e non interessa a nessuno. La riga ‘# chkconfig 345 85 15’, serve a stabilire che il servizio corrispondente può essere avviato solo quando il livello di esecuzione va da tre a cinque. Il numero 85 successivo, indica l’ordine nell’avvio del servizio e, dato il numero, si intuisce che si vuole fare in modo che questo avvenga dopo molti altri. Il numero 15 finale, indica l’ordine di disattivazione del servizio in fase di arresto del sistema; si intende dal numero che si vuole fare in modo di chiuderlo abbastanza presto rispetto agli altri. Verrà chiarito meglio nella prossima sezione il senso di questi numeri. La riga ‘# description:’ serve ad annotare una descrizione del servizio, come promemoria per facilitare l’utilizzo del programma ‘ntsysv’, che sarà mostrato successivamente. Per il momento, si osservi che la descrizione può continuare su più righe di commento, purché si utilizzi il simbolo ‘\’ subito prima della conclusione della riga. 357.1.3 /etc/rc.d/rc?.d/ Le directory ‘/etc/rc.d/rcn .d/’ servono a contenere una serie di collegamenti simbolici che puntano a script della directory ‘/etc/rc.d/init.d’. Il listato seguente dovrebbe chiarire il meccanismo. lrwxrwxrwx lrwxrwxrwx lrwxrwxrwx lrwxrwxrwx lrwxrwxrwx lrwxrwxrwx lrwxrwxrwx lrwxrwxrwx lrwxrwxrwx lrwxrwxrwx lrwxrwxrwx lrwxrwxrwx 1 1 1 1 1 1 1 1 1 1 1 1 root root root root root root root root root root root root root root root root root root root root root root root root 13 13 15 16 17 17 15 16 16 14 18 11 13:39 13:39 13:39 13:39 13:39 13:39 13:39 13:39 13:39 13:39 13:39 12:44 K15gpm -> ../init.d/gpm K60atd -> ../init.d/atd K60crond -> ../init.d/crond K96pcmcia -> ../init.d/pcmcia S01kerneld -> ../init.d/kerneld S10network -> ../init.d/network S15nfsfs -> ../init.d/nfsfs S20random -> ../init.d/random S30syslog -> ../init.d/syslog S50inet -> ../init.d/inet S75keytable -> ../init.d/keytable S99local -> ../rc.local I collegamenti che iniziano con la lettera «S» vengono avviati ordinatamente all’attivazione del livello di esecuzione corrispondente, con l’argomento ‘start’, mentre quelli che iniziano con la lettera «K» vengono avviati prima di passare a un nuovo livello di esecuzione, con l’argomento ‘stop’. Il numero che segue la lettera «S» e «K», serve a definire un ordine (alfabetico) corrispondente a quello in cui i servizi vanno avviati o interrotti. Se si volesse predisporre uno script, nella directory ‘/etc/rc.d/init.d/’ per la gestione di un servizio addizionale, si dovrebbero predisporre Configurazione di una distribuzione Red Hat 4019 due collegamenti nella directory del livello di esecuzione prescelto, simili a quelli già visti, con un numero adatto a collocarli nella posizione giusta nell’ordine delle azioni da compiere. Si osservi la presenza del collegamento ‘S99local’ che punta allo script ‘/etc/rc.d/ rc.local’. Si tratta di un’anomalia nella logica generale, dal momento che si fa riferimento a qualcosa di esterno alla directory ‘/etc/rc.d/init.d/’. Il numero, 99, è obbligatorio, dal momento che l’esecuzione di questo deve avvenire alla fine dell’avvio di tutti gli altri servizi. 357.1.4 # ntsysv e chkconfig Attraverso il programma ‘ntsysv’, è possibile aggiungere o togliere servizi da attivare nel livello di esecuzione standard. ‘ntsysv’ provvede da solo a creare i collegamenti simbolici mostrati nella sezione precedente, utilizzando la convenzione mostrata. Per stabilire il numero da usare per i collegamenti di avvio (quelli che iniziano con la lettera ‘S’) e per quelli di conclusione (‘K’), si avvale del commento iniziale contenuto negli script originali. Per riprendere l’esempio già mostrato in precedenza, se nella directory ‘/etc/rc.d/init.d/’ si trova lo script ‘pippo’, che contiene il commento iniziale seguente, #! /bin/sh # # chkconfig: 345 85 15 # description: Servizio Pippo. Si tratta di un servizio che non serve \ # a nulla e non interessa a nessuno. ‘ntsysv’ sarà in grado di creare i collegamenti ‘S85pippo’ e ‘K15pippo’. La descrizione, inol- tre, è utile per ricordare a cosa serve questo servizio (o comunque a cosa serve questo script), quando è il momento di scegliere se attivarlo o meno. Il programma ‘chkconfig’ serve fondamentalmente alle stesse funzioni di ‘ntsysv’, con la differenza che si tratta di un programma a riga di comando, mentre il secondo è interattivo e utilizza una maschera visiva piuttosto amichevole. 357.2 Configurazione del sistema La maggior parte dei file di configurazione della distribuzione Red Hat si trova nella directory ‘/etc/sysconfig’. I nomi dei file permettono di capire, intuitivamente, il genere di cose che con essi si intendono configurare. In particolare, la directory ‘/etc/sysconfig/ network-scripts/’ contiene una serie di script e di altri file necessari a facilitare la gestione delle interfacce di rete e degli instradamenti. 357.2.1 Configurazione della rete L’organizzazione dei file di configurazione e degli script per la connessione in rete è un po’ complicata, per permetterne il controllo attraverso il pannello di controllo, o più precisamente, attraverso ‘netcfg’. Configurazione di una distribuzione Red Hat 4020 357.2.1.1 /etc/sysconfig/network Si tratta del file contenente le informazioni fondamentali sulla connessione alla rete: • attivazione o meno della rete; • nome completo dell’elaboratore; • nome del dominio; • router (gateway) predefinito; • interfaccia di rete che porta verso il router predefinito. Come al solito si utilizza la semplificazione per cui l’elaboratore ha un solo nome e un solo dominio di appartenenza, anche se ha più interfacce di rete (e quindi più nomi e più domini). Evidentemente, se ci sono più interfacce, si deve scegliere un nome e un dominio. Alcune direttive { | } NETWORKING= yes no Permette di attivare (‘yes’) o di disattivare (‘no’) la configurazione delle rete. HOSTNAME=nome_fqdn Il nome completo (FQDN) dell’elaboratore, possibilmente quello corrispondente a un’interfaccia di rete realmente esistente. Il file ‘/etc/HOSTNAME’, generato normalmente in modo automatico, dovrebbe contenere questo nome. DOMAINNAME=dominio Permette di definire il nome del dominio dell’elaboratore. In pratica, si può riferire solo a un dominio di un’interfaccia di rete particolare. { | } FORWARD_IPV4= yes no Permette di attivare (‘yes’) o di disattivare (‘no’) l’inoltro IP. Perché l’elaboratore possa funzionare come router, è indispensabile che questa funzione sia attivata, mentre, per motivi di sicurezza, il valore predefinito è ‘no’. GATEWAY=indirizzo_ip_del_router_predefinito Permette di definire l’indirizzo IP di un router per l’instradamento predefinito, cioè quel router da utilizzare quando si vuole inoltrare un pacchetto verso un indirizzo per il quale non esista già un instradamento specifico. GATEWAYDEV=interfaccia_di_rete Permette di indicare esplicitamente il nome dell’interfaccia di rete da utilizzare per l’instradamento predefinito. NISDOMAIN=dominio_nis Permette di definire il dominio NIS a cui appartiene l’elaboratore. Esempi L’esempio seguente si riferisce alla configurazione dell’elaboratore portatile.plip.dg. In particolare, si utilizza un router che ha indirizzo IP 192.168.254.254, raggiungibile attraverso l’interfaccia ‘plip1’. NETWORKING=yes FORWARD_IPV4=false HOSTNAME=portatile.plip.dg DOMAINNAME=plip.dg GATEWAY=192.168.254.254 GATEWAYDEV=plip1 Configurazione di una distribuzione Red Hat 4021 357.2.1.2 /etc/sysconfig/static-routes Si tratta della definizione degli instradamenti statici, cioè quelli che non cambiano. Riguarda sia gli instradamenti alle reti accessibili direttamente che a quelle raggiungibili solo attraverso un router. L’esempio seguente dovrebbe essere abbastanza chiaro: la prima riga definisce un instradamento alla rete locale, le altre due definiscono gli instradamenti verso altre reti accessibili attraverso il router 192.168.1.254. eth0 net 192.168.1.0 netmask 255.255.255.0 eth0 net 192.168.2.0 netmask 255.255.255.0 gw 192.168.1.254 eth0 net 192.168.3.0 netmask 255.255.255.0 gw 192.168.1.254 357.2.1.3 /etc/sysconfig/network-scripts/ifcfg-* Per ogni interfaccia di rete gestita, appare un file di configurazione con il nome ‘ifcfg-interfaccia ’ nella directory ‘/etc/sysconfig/network-scripts/’. Questi file contengono informazioni differenti in funzione del tipo di interfaccia. Alcune direttive DEVICE=interfaccia Definisce il nome dell’interfaccia di rete corrispondente. IPADDR=indirizzo_ip L’indirizzo IP dell’interfaccia. NETMASK=maschera_di_rete La maschera di rete. NETWORK=indirizzo_di_rete Indirizzo della rete. BROADCAST=indirizzo_broadcast Indirizzo broadcast. { | } ONBOOT= yes no Permette di attivare (‘yes’), o di disattivare (‘no’), l’interfaccia all’avvio del sistema. Esempi L’esempio seguente si riferisce alla configurazione dell’interfaccia ‘lo’, praticamente obbligatoria, corrispondente al file ‘/etc/sysconfig/network-scripts/ifcfg-lo’. DEVICE=lo IPADDR=127.0.0.1 NETMASK=255.0.0.0 NETWORK=127.0.0.0 BROADCAST=127.255.255.255 ONBOOT=yes L’esempio seguente si riferisce alla configurazione dell’interfaccia ‘eth0’ con un indirizzo IP 192.168.1.1 e la maschera di rete 255.255.255.0. DEVICE=eth0 IPADDR=192.168.1.1 NETMASK=255.255.255.0 NETWORK=192.168.1.0 BROADCAST=192.168.1.255 ONBOOT=yes BOOTPROTO=none Configurazione di una distribuzione Red Hat 4022 357.2.2 Configurazione di altri elementi Il resto della configurazione è gestito attraverso file contenuti esclusivamente nella directory ‘/etc/sysconfig/’. 357.2.2.1 /etc/sysconfig/clock Il file ‘/etc/sysconfig/clock’ permette di configurare l’orologio del sistema. Per farlo, si deve conoscere come è impostato l’orologio hardware. In pratica, è importante sapere se questo è allineato al tempo universale oppure all’ora locale. Eventualmente può essere usato il programma ‘timeconfig’ per definire correttamente questo file. Alcune direttive { | } UTC= true false Se viene utilizzato il valore ‘true’, si intende che l’orologio hardware sia allineato al tempo universale; diversamente, si intende che sia allineato all’ora locale. 357.2.2.2 /etc/sysconfig/keyboard Il file ‘/etc/sysconfig/keyboard’ permette di configurare la tastiera. Eventualmente può essere usato il programma ‘kbdconfig’ per definire correttamente questo file. Alcune direttive KEYTABLE=mappa_tastiera Definisce la mappa della tastiera, indicando il file corrispondente. Esempi KEYTABLE="/usr/share/keymaps/i386/qwerty/it.kmap" Definisce la configurazione della tastiera italiana. KEYTABLE=it.kmap Esattamente come nell’esempio precedente, senza il problema di dover indicare tutto il percorso per raggiungere il file, dal momento che si tratta di quello predefinito. 357.2.2.3 /etc/sysconfig/mouse Il file ‘/etc/sysconfig/mouse’ permette di configurare il mouse per l’utilizzo attraverso il programma ‘gpm’. Eventualmente può essere usato il programma ‘mouseconfig’ per definire correttamente questo file. Alcune direttive MOUSETYPE=tipo Permette di definire il tipo di mouse utilizzato. Sono validi i nomi seguenti. • ‘microsoft’ • ‘mouseman’ • ‘mousesystems’ Configurazione di una distribuzione Red Hat • • • • • • • 4023 ‘ps/2’ ‘msbm’ ‘logibm’ ‘atibm’ ‘logitech’ ‘mmseries’ ‘mmhittab’ { | } XEMU3= yes no Abilita o disabilita l’emulazione del tasto centrale. Esempi L’esempio seguente rappresenta la configurazione per un mouse compatibile con il tipo Microsoft a due tasti, per cui il terzo deve essere emulato. MOUSETYPE="Microsoft" XEMU3=yes 357.3 Configurazione di shell Anche la configurazione della shell è molto importante per il sistema, risultando relativamente complessa. 357.3.1 Bash • ‘/etc/profile’ Si tratta del file di configurazione generale, secondo lo standard, ma è organizzato in modo da permettere l’inserimento di altri segmenti, senza toccarlo. Infatti, alla fine vengono caricati tutti i file che si trovano nella directory ‘/etc/profile.d/’ e terminano con l’estensione ‘.sh’. # /etc/profile # System wide environment and startup programs # Functions and aliases go in /etc/bashrc PATH="$PATH:/usr/X11R6/bin" PS1="[\u@\h \W]\\$ " ulimit -c 1000000 if [ ‘id -gn‘ = ‘id -un‘ -a ‘id -u‘ -gt 14 ]; then umask 002 else umask 022 fi USER=‘id -un‘ LOGNAME=$USER MAIL="/var/mail/$USER" HOSTNAME=‘/bin/hostname‘ HISTSIZE=1000 HISTFILESIZE=1000 export PATH PS1 HOSTNAME HISTSIZE HISTFILESIZE USER LOGNAME MAIL for i in /etc/profile.d/*.sh ; do if [ -x $i ]; then . $i fi done Configurazione di una distribuzione Red Hat 4024 • ‘/etc/bashrc’ Secondo le intenzioni di chi ha organizzato la distribuzione, questo file dovrebbe contenere solo la definizione di funzioni e di alias di interesse generale, ma il suo utilizzo dipende dalla configurazione personalizzata di ogni utente, che potrebbe anche escludere l’inclusione di questo file. # /etc/bashrc # System wide functions and aliases # Environment stuff goes in /etc/profile # For some unknown reason bash refuses to inherit # PS1 in some circumstances that I can’t figure out. # Putting PS1 here ensures that it gets loaded every time. PS1="[\u@\h \W]\\$ " alias which="type -path" Per qualche motivo, il file ‘/etc/bashrc’ fornito con la distribuzione contiene la definizione della variabile ‘PS1’, che va a sovrapporsi a quanto già indicato nel file di configurazione generale, ‘/etc/profile’. Se si intende modificare l’aspetto predefinito dell’invito della shell, conviene agire in questo file. Potrebbe essere conveniente definire, per tutti gli utenti, una serie di alias ai comandi più «pericolosi», trasformando quindi il file nel modo seguente (la dichiarazione della variabile ‘PS1’ viene commentata). # /etc/bashrc # System wide functions and aliases # Environment stuff goes in /etc/profile ## For some unknown reason bash refuses to inherit ## PS1 in some circumstances that I can’t figure out. ## Putting PS1 here ensures that it gets loaded every time. #PS1="[\u@\h \W]\\$ " alias which="type -path" alias rm=’rm -i’ alias cp=’cp -i’ alias mv=’mv -i’ • ‘~/.bashrc’ È il file di configurazione di una shell Bash interattiva. Secondo le intenzioni di chi ha organizzato la distribuzione, questo file dovrebbe contenere solo la definizione di funzioni e di alias personalizzati, dove alla fine dovrebbe richiamare il file ‘/etc/bashrc’ che contiene le stesse cose, ma a livello generale. # .bashrc # User specific aliases and functions # Source global definitions if [ -f /etc/bashrc ]; then . /etc/bashrc fi • ‘~/.bash_profile’ È il file di configurazione utilizzato dalla shell Bash quando questa viene avviata a seguito di un accesso. Per mantenere uniformità con l’insieme, esegue a sua volta il contenuto di ‘~/.bashrc’ # .bash_profile # Get the aliases and functions Configurazione di una distribuzione Red Hat 4025 if [ -f ~/.bashrc ]; then . ~/.bashrc fi # User specific environment and startup programs PATH=$PATH:$HOME/bin ENV=$HOME/.bashrc USERNAME="" export USERNAME ENV PATH 357.3.1.1 Configurazione aggiuntiva La directory ‘/etc/profile.d/’ viene usata per contenere una serie di file da inserire ordinatamente alla fine di ‘/etc/profile’. Attraverso questo meccanismo, l’installazione di un programma può definire un’aggiunta nella configurazione della shell senza dover modificare il file ‘/etc/profile’. I file in questione, devono terminare con l’estensione ‘.sh’, non serve che siano eseguibili e nemmeno che inizino con la definizione della shell che deve interpretarli. L’esempio seguente mostra uno di questi file con la definizione di alcune variabili utili. # /etc/profile.d/config.sh LANG="it_IT.ISO-8859-1" export LANG PATH="$PATH:$HOME/bin:." export PATH PS1=’\u@\h:\w\$ ’ export PS1 alias rm=’rm -i’ alias cp=’cp -i’ alias mv=’mv -i’ Nell’esempio viene definita la variabile ‘LANG’, nel modo corretto per l’Italia, quindi vengono aggiunte al percorso di ricerca degli eseguibili, la directory ‘~/bin’ e la directory corrente. Successivamente, viene definita la variabile ‘PS1’ (l’invito della shell) e una serie di alias. In precedenza si è visto che la distribuzione Red Hat indica il file ‘/etc/bashrc’ come il contenitore adatto per la definizione dell’invito e degli alias. Dipende dal gusto dell’amministratore del sistema la scelta di come intervenire. 357.4 Utenti Utenti e gruppi vengono gestiti in modo differente dal solito: si tende ad abbinare a ogni utente un gruppo con lo stesso nome e lo stesso numero. Questa tecnica permette di lasciare al gruppo gli stessi permessi dell’utente, facilitando la creazione e lo scioglimento di gruppi di lavoro con la semplice creazione di gruppi nuovi a cui si abbinano gli utenti che ne fanno parte. Questo viene descritto un po’ meglio nella sezione 54.5.4. In queste condizioni, la maschera dei permessi utilizzata normalmente è 0028. È il caso di osservare che l’utente e il gruppo ‘nobody’ hanno il numero 99. Lo script ‘useradd’ inserisce gli utenti a partire dal numero 500 in poi, aggregando a ognuno un gruppo privato. 4026 Configurazione di una distribuzione Red Hat 357.4.1 /etc/passwd root::0:0:root:/root:/bin/bash bin:*:1:1:bin:/bin: daemon:*:2:2:daemon:/sbin: adm:*:3:4:adm:/var/adm: lp:*:4:7:lp:/var/spool/lpd: sync:*:5:0:sync:/sbin:/bin/sync shutdown:*:6:0:shutdown:/sbin:/sbin/shutdown halt:*:7:0:halt:/sbin:/sbin/halt mail:*:8:12:mail:/var/mail: news:*:9:13:news:/var/spool/news: uucp:*:10:14:uucp:/var/spool/uucp: operator:*:11:0:operator:/root: games:*:12:100:games:/usr/games: gopher:*:13:30:gopher:/usr/lib/gopher-data: ftp:*:14:50:FTP User:/home/ftp: nobody:*:99:99:Nobody:/: 357.4.2 /etc/group root::0:root bin::1:root,bin,daemon daemon::2:root,bin,daemon sys::3:root,bin,adm adm::4:root,adm,daemon tty::5: disk::6:root lp::7:daemon,lp mem::8: kmem::9: wheel::10:root mail::12:mail news::13:news uucp::14:uucp,root man::15: games::20: gopher::30: dip::40: ftp::50: nobody::99: users::100: 357.5 Stampa Il sistema di stampa adottato da Red Hat è quello tradizionale, cioè BSD. Le directory delle code, riferite ad altrettante voci del file ‘/etc/printcap’, si diramano a partire da ‘/var/spool/ lpd/’. Ognuna di queste contiene il filtro di stampa (se viene utilizzato) e i file di configurazione utilizzati dal filtro. Tutto questo, compreso il file ‘/etc/printcap’, è gestito direttamente dal programma di configurazione della stampa, ‘printtool’. Configurazione di una distribuzione Red Hat 4027 357.5.1 /var/spool/lpd/*/ All’interno della directory per la coda di stampa si trovano, oltre ai file utilizzati da ‘lpr’ e ‘lpd’, anche lo script usato come filtro e i relativi file di configurazione. • ‘filter’ È il filtro che fa riferimento a diversi componenti collocati sotto ‘/usr/lib/rhs/ rhs-printfilters/’. • ‘general.cfg’ È un file di configurazione generale della stampa. Segue un esempio nel quale si fa riferimento a: – stampante locale; – sistema di stampa PostScript (attraverso un filtro); – foglio A4; – file ASCII trasformati in PostScript. # # General config options for printing on this queue # Generated by PRINTTOOL, do not modify. # export DESIRED_TO=ps export PAPERSIZE=a4 export PRINTER_TYPE=LOCAL export ASCII_TO_PS=YES • ‘postscript.cfg’ È un file di configurazione per l’emulazione della stampa PostScript. Segue un esempio nel quale si fa riferimento a: – stampante compatibile con HP Laserjet; – risoluzione 300×300 dpi; – foglio A4. # # configuration related to postscript printing # generated automatically by PRINTTOOL # manual changes to this file may be lost # GSDEVICE=laserjet RESOLUTION=300x300 COLOR= PAPERSIZE=a4 EXTRA_GS_OPTIONS="" REVERSE_ORDER= PS_SEND_EOF=NO # # following is related to printing multiple pages per output page # NUP=1 RTLFTMAR=18 TOPBOTMAR=18 • ‘textonly.cfg’ È un file di configurazione della stampa di solo testo. Segue un esempio, nel quale si fa riferimento, in particolare, alla trasformazione dei codici di interruzione di riga in modo che corrispondano sempre a <CR><LF>. Configurazione di una distribuzione Red Hat 4028 # # text-only printing options for printing on this queue # Generated by PRINTTOOL, do not modify. # TEXTONLYOPTIONS= CRLFTRANS=1 TEXT_SEND_EOF=YES 357.5.2 /etc/printcap Dal momento che la stampa è organizzata attraverso questo sistema di filtri, controllato da ‘printtool’, sarebbe meglio non modificare il file standard ‘/etc/printcap’, o almeno limitarsi a utilizzare le sole caratteristiche che ‘printtool’ può gestire. # # # # # # # /etc/printcap Please don’t edit this file directly unless you know what you are doing! Be warned that the control-panel printtool requires a very strict format! Look at the printcap(5) man page for more info. This file can be edited with the printtool in the control-panel. ##PRINTTOOL3## LOCAL laserjet 300x300 a4 {} LaserJet Default {} lp:\ :sd=/var/spool/lpd/lp:\ :mx#0:\ :sh:\ :lp=/dev/lp1:\ :if=/var/spool/lpd/lp/filter: Appunti di informatica libera 2003.01.01 --- Copyright © 2000-2003 Daniele Giacomini -- daniele @ swlibero.org Capitolo 358 Accorgimenti per una distribuzione Red Hat Ogni distribuzione ha i suoi limiti; a volte dei piccoli accorgimenti possono risolvere problemi importanti. 358.1 Gestione dei pacchetti non ufficiali La Red Hat, come azienda, produce una distribuzione GNU/Linux limitata a un certo numero di applicativi, quelli che è in grado di seguire e garantire in base al personale a sua disposizione. Naturalmente, questo è il limite comune delle distribuzioni GNU/Linux commerciali. Tutti i pacchetti assemblati all’esterno dell’azienda, vengono «relegati» all’area dei contributi (‘contrib/’) e, spesso, di questi programmi non si può fare a meno. Se non si ha un accesso permanente a Internet, diventa utile conservare da qualche parte questi programmi che non fanno parte della distribuzione standard. Si pone però un problema nel momento in cui si viene in possesso di un CD-ROM contenente parte di questi contributi, che si ritiene essere aggiornato. Infatti, tale CD-ROM non li può contenere tutti e se si eliminano quelli conservati precedentemente si rischia di perdere qualcosa che serve; inoltre, se si mescolano i file, si rischia di avere due o tre versioni diverse di uno stesso pacchetto. Con un po’ di abilità si può realizzare un programmino che analizza una directory, cerca di identificare i pacchetti uguali ed elimina quelli delle versioni più vecchie. 358.1.1 Composizione del nome dei pacchetti RPM Il nome dei pacchetti RPM è organizzato secondo la struttura seguente: nome_dell’applicativo -versione -rilascio.architettura .rpm In pratica, dopo il nome dell’applicativo segue un trattino e quindi l’indicazione della versione, quindi ancora un trattino e poi il rilascio, ovvero la versione riferita alla trasformazione in un pacchetto RPM. Nella parte finale, dopo un punto di separazione, appare la sigla dell’architettura e l’estensione ‘.rpm’. L’esempio seguente mostra il nome del pacchetto contenente il kernel generico e i moduli precompilati per la versione 2.0.34, rilascio 1, per l’architettura i386. kernel-2.0.34-1.i386.rpm Volendo confrontare i nomi di file di versioni differenti, occorre estrapolare la versione e il rilascio. Nella migliore delle ipotesi, la versione è composta da una serie di numeri separati da un punto, dove il primo numero è quello più significativo; nello stesso modo può essere organizzato il rilascio. Il problema si pone quando la versione o il rilascio contengono dei dati alfabetici: diventa impossibile determinare a priori il modo corretto di confronto. 358.1.2 Programma per l’eliminazione dei file RPM doppi Quello che segue è uno script scritto in Perl per la scansione di una directory, quella corrente nel momento in cui si avvia, allo scopo di eliminare i file corrispondenti a pacchetti già esistenti in versioni più recenti. #!/usr/bin/perl #====================================================================== # rpmdoppi # # Interviene nella directory *corrente* eliminando i file doppi, 4029 Accorgimenti per una distribuzione Red Hat 4030 # più vecchi. #====================================================================== $file0 = ""; $file1 = ""; $nome0 = ""; $nome1 = ""; $versione0 = ""; $versione1 = ""; $rilascio0 = ""; $rilascio1 = ""; $architettura0 = ""; $architettura1 = ""; $verA0 $verA1 $verB0 $verB1 $verC0 $verC1 $verD0 $verD1 $verE0 $verE1 $verF0 $verF1 = = = = = = = = = = = = ""; ""; ""; ""; ""; ""; ""; ""; ""; ""; ""; ""; #---------------------------------------------------------------------# Carica l’elenco dei file dalla directory corrente. #---------------------------------------------------------------------open ( ELENCO, "ls *.rpm | sort |" ); #---------------------------------------------------------------------# Scandisce nome per nome. #---------------------------------------------------------------------while ( $file1 = <ELENCO> ) { #-----------------------------------------------------------------# Estrae gli elementi che compongono il nome del file. #-----------------------------------------------------------------$file1 =~ m{^(.*)-([^-]*)-([^-]*)\.([^._-]*)\.rpm}; #-----------------------------------------------------------------# Distribuisce i vari pezzi a variabili più comprensibili. #-----------------------------------------------------------------$nome1 = $1; $versione1 = $2; $rilascio1 = $3; $architettura1 = $4; #-----------------------------------------------------------------# Verifica se i nomi sono comparabili. #-----------------------------------------------------------------if ( $nome1 eq $nome0 ) { if ( $architettura1 eq $architettura0 ) { #---------------------------------------------------------# Ok, i nomi sono comparabili. #---------------------------------------------------------; } else { Accorgimenti per una distribuzione Red Hat #---------------------------------------------------------# Le architetture sono differenti. #---------------------------------------------------------print "Attenzione all’architettura:\n"; print " $file0"; print " $file1"; #---------------------------------------------------------# Riprende il ciclo. #---------------------------------------------------------next; } } else { #-------------------------------------------------------------# I nomi sono differenti, quindi ripete il ciclo. #-------------------------------------------------------------next; } #-----------------------------------------------------------------# Qui i nomi e le architetture sono uguali. #-----------------------------------------------------------------#-----------------------------------------------------------------# Se l’ultima cifra della versione è alfabetica e la penultima # è numerica, quella alfabetica viene # trasformata in numerica per facilitare il confronto. #-----------------------------------------------------------------if ( $versione1 =~ m{^.*\d[A-Za-z]$} && $versione0 =~ m{^.*\d[A-Za-z]$} ) { #-------------------------------------------------------------# L’ultimo elemento della versione è una lettera alfabetica. # Converte la lettera in numero. #-------------------------------------------------------------$versione1 =~ m{^(.*)([A-Za-z])$}; $versione1 = $1 . ord $2; $versione0 =~ m{^(.*)([A-Za-z])$}; $versione0 = $1 . ord $2; } #-----------------------------------------------------------------# Si verifica che la versione sia numerica. #-----------------------------------------------------------------if ( $versione1 =~ m{^[0-9.]*$} && $versione0 =~ m{^[0-9.]*$} ) { #-------------------------------------------------------------# La versione è correttamente numerica. # Si procede a estrarre i vari valori separati da punti # (al massimo sei). #-------------------------------------------------------------$versione1 =~ m{^(\d*)\.*(\d*)\.*(\d*)\.*(\d*)\.*(\d*)\.*(\d*)$}; $verA1 = $1; $verB1 = $2; $verC1 = $3; $verD1 = $4; $verE1 = $5; $verF1 = $6; $versione0 =~ m{^(\d*)\.*(\d*)\.*(\d*)\.*(\d*)\.*(\d*)\.*(\d*)$}; $verA0 = $1; $verB0 = $2; $verC0 = $3; $verD0 = $4; $verE0 = $5; 4031 Accorgimenti per una distribuzione Red Hat 4032 $verF0 = $6; #-------------------------------------------------------------# Si procede al confronto tra le versioni. #-------------------------------------------------------------if ( $verA1 > $verA0 ) { print "Eliminazione del file $file0"; system( "rm $file0" ); next; } elsif ( $verA1 < $verA0 ) { print "Eliminazione del file $file1"; system( "rm $file1" ); next; } elsif ( $verB1 > $verB0 ) { print "Eliminazione del file $file0"; system( "rm $file0" ); next; } elsif ( $verB1 < $verB0 ) { print "Eliminazione del file $file1"; system( "rm $file1" ); next; } elsif ( $verC1 > $verC0 ) { print "Eliminazione del file $file0"; system( "rm $file0" ); next; } elsif ( $verC1 < $verC0 ) { print "Eliminazione del file $file1"; system( "rm $file1" ); next; } elsif ( $verD1 > $verD0 ) { print "Eliminazione del file $file0"; system( "rm $file0" ); next; } elsif ( $verD1 < $verD0 ) { print "Eliminazione del file $file1"; system( "rm $file1" ); next; } elsif ( $verE1 > $verE0 ) { print "Eliminazione del file $file0"; system( "rm $file0" ); next; } elsif ( $verE1 < $verE0 ) { print "Eliminazione del file $file1"; system( "rm $file1" ); next; } elsif ( $verF1 > $verF0 ) { print "Eliminazione del file $file0"; system( "rm $file0" ); next; } elsif ( $verF1 < $verF0 ) { print "Eliminazione del file $file1"; system( "rm $file1" ); next; } } elsif ( $versione1 =~ m{^$versione0$} ) { #-------------------------------------------------------------# Le versioni sono uguali; più avanti si verifica il numero # di rilascio. #-------------------------------------------------------------; } else { #-------------------------------------------------------------- Accorgimenti per una distribuzione Red Hat # La versione contiene simboli non numerici. #-------------------------------------------------------------print "Attenzione ai file seguenti (versione non numerica)\n"; print " $file0"; print " $file1"; next; } #-----------------------------------------------------------------# Qui le versioni sono uguali. #-----------------------------------------------------------------#-----------------------------------------------------------------# Si verifica che il rilascio sia numerico. #-----------------------------------------------------------------if ( $rilascio1 =~ m{^[0-9.]*$} && $rilascio0 =~ m{^[0-9.]*$} ) { #-------------------------------------------------------------# Il rilascio è correttamente numerico. # Si procede a estrarre i vari valori separati da punti # (al massimo 3). #-------------------------------------------------------------$rilascio1 =~ m{^(\d*)\.*(\d*)\.*(\d*)$}; $verA1 = $1; $verB1 = $2; $verC1 = $3; $rilascio0 =~ m{^(\d*)\.*(\d*)\.*(\d*)$}; $verA0 = $1; $verB0 = $2; $verC0 = $3; #-------------------------------------------------------------# Si procede al confronto tra i rilasci. #-------------------------------------------------------------if ( $verA1 > $verA0 ) { print "Eliminazione del file $file0"; system( "rm $file0" ); next; } elsif ( $verA1 < $verA0 ) { print "Eliminazione del file $file1"; system( "rm $file1" ); next; } elsif ( $verB1 > $verB0 ) { print "Eliminazione del file $file0"; system( "rm $file0" ); next; } elsif ( $verB1 < $verB0 ) { print "Eliminazione del file $file1"; system( "rm $file1" ); next; } elsif ( $verC1 > $verC0 ) { print "Eliminazione del file $file0"; system( "rm $file0" ); next; } elsif ( $verC1 < $verC0 ) { print "Eliminazione del file $file1"; system( "rm $file1" ); next; } else { print "I file seguenti sembrano identici\n"; print " $file0"; print " $file1"; next; } } else { 4033 Accorgimenti per una distribuzione Red Hat 4034 #-------------------------------------------------------------# Il rilascio contiene simboli non numerici. #-------------------------------------------------------------print "Attenzione ai file seguenti (rilascio non numerico)\n"; print " $file0"; print " $file1"; next; } } continue { #-----------------------------------------------------------------# Accantona i dati per il confronto. #-----------------------------------------------------------------$file0 = $file1; $nome0 = $nome1; $versione0 = $versione1; $rilascio0 = $rilascio1; $architettura0 = $architettura1; } #---------------------------------------------------------------------# Il lavoro è terminato; viene chiuso l’elenco dei file. #---------------------------------------------------------------------close ( ELENCO ); #====================================================================== Come si può intuire, questo programma potrebbe anche fallire nel suo intento. In ogni caso, bisogna analizzare i messaggi per intervenire manualmente sui file che non possono essere trattati automaticamente. 358.2 Personalizzazione e aggiornamento Una delle cose più fastidiose della distribuzione GNU/Linux Red Hat sono gli aggiornamenti numerosi, già dopo pochi giorni che una nuova versione viene pubblicata. Se si scarica la distribuzione dalla rete, o se la si acquista attraverso uno dei tanti distributori non ufficiali, si ottiene sempre solo una versione «originale», con tutti i difetti che potrebbe avere, dove gli aggiornamenti vanno fatti dopo l’installazione, in modo manuale. Quando si installa GNU/Linux in modo sistematico su un gran numero di elaboratori, questo problema diventa delicato, perché il lavoro di aggiornamento deve essere moltiplicato su tutte le macchine, mentre sarebbe utile la possibilità di ottenere una distribuzione personalizzata e aggiornata come si vuole. 358.2.1 Installazione normale o attraverso il dischetto supplementare Allo stato attuale, i dischetti per l’installazione sono due. Il primo è sufficiente per le forme di installazione più comuni, come quella da un CD-ROM o da un file system di rete NFS, mentre il secondo deve essere usato in tutti gli altri casi. Il programma di installazione aggiuntivo, collocato nel dischetto supplementare è più tollerante e in molti casi è in grado di installare una distribuzione contenente file di versioni differenti rispetto a quelle previste nell’edizione standard. Al contrario, il programma del primo dischetto richiede un’esatta corrispondenza tra i nomi dei file. Accorgimenti per una distribuzione Red Hat 4035 Per la precisione, le forme di installazione che fanno uso del solo dischetto di avvio, richiedono la presenza del file ‘RedHat/base/hdlist’, che contiene l’elenco e le descrizioni dei pacchetti RPM disponibili. 358.2.2 Personalizzazione Come accennato, per poter personalizzare una distribuzione Red Hat con i pacchetti aggiornati, occorre rigenerare il file ‘RedHat/base/hdlist’. Questo si ottiene con il programma ‘misc/ src/install/genhdlist’. Supponendo di disporre di una distribuzione Red Hat per la piattaforma i386, a partire dalla directory ‘/MIO_RHL/’, strutturata come si vede dallo schema seguente (che è comunque semplificato rispetto alla realtà), si devono compiere i passi elencati successivamente. /MIO_RHL |-- RedHat/ | |-- RPMS/ | |-- base/ | |-- instimage/ | | ‘... | | | ‘-- i386 | |-- doc/ | |-- dosutils/ | ‘... | |-- images/ | |-- misc/ | |-- boot/ | ‘-- src/ | |-- install/ | ‘... | |-- updates/ | |-- COPYING |-- README ‘-- RPM-PGP-KEY 1. Si copiano i file dei pacchetti aggiornati nella directory ‘RPMS/’ della versione personalizzata che si sta predisponendo. # cp /MIO_RHL/updates/* /MIO_RHL/RedHat/RPMS/ 2. In qualche modo si eliminano i pacchetti doppi più vecchi. 3. Si rigenera il file ‘RedHat/base/hdlist’, ma prima si sistemano i permessi, nel caso serva. # chmod u+x /MIO_RHL/misc/src/install/genhdlist # chmod 644 /MIO_RHL/RedHat/base/hdlist # /MIO_RHL/misc/src/install/genhdlist /MIO_RHL/ Come si può intuire, l’eliminazione dei pacchetti doppi più vecchi può essere fatta con l’aiuto dello script ‘rpmdoppi’ già descritto in questo capitolo. 4036 Accorgimenti per una distribuzione Red Hat 358.3 Aggiornamento del kernel Comunque si decida di aggiornare una distribuzione Red Hat, il kernel è un punto che crea solitamente dei problemi. Segue la descrizione del modo più conveniente per aggiornarlo. 358.3.1 Installazione fisica Per prima cosa, con il sistema già funzionante, si procede all’aggiornamento simultaneo di tutti i pacchetti del kernel, saltando solo quelli che non vengono già utilizzati nel sistema. L’aggiornamento simultaneo è necessario per evitare problemi di conflitti. Il modo più semplice è quello di collocare i file dei pacchetti desiderati in una directory temporanea e da lì installarli contemporaneamente. # rpm -Uv /tmp/updates/*.rpm 358.3.2 initrd Se il sistema utilizza unità SCSI, dal momento che i kernel modulari predisposti dalle distribuzioni Red Hat non includono nel blocco principale la gestione di questi dispositivi, occorre aggiornare anche l’immagine ‘initrd’. Questa infatti deve contenere i moduli necessari per il riconoscimento delle unità SCSI esistenti e, avendo aggiornato il kernel, occorre ricostruire anche questo file. Se la gestione dei moduli è configurata correttamente, dovrebbe bastare il comando seguente, dove la versione e il rilascio vanno sostituiti con quelli del kernel aggiornato. mkinitrd /boot/initrd-versione -rilascio versione -rilascio In pratica, immaginando che si tratti della versione 2.0.34 rilascio 1, si dovrebbe procedere nel modo seguente: # mkinitrd /boot/initrd-2.0.34-1 2.0.34-1 358.3.3 LILO Una volta che i file del kernel e l’immagine ‘initrd’ sono al loro posto, ci si deve prendere cura del sistema di avvio, di solito con LILO. Evidentemente, occorre ritoccare il file ‘/etc/lilo.conf’ in modo che venga avviato il file corretto del kernel e venga utilizzato eventualmente la nuova immagine ‘initrd’. L’esempio seguente riguarda il caso di un kernel 2.0.34-1. boot=/dev/hda map=/boot/map install=/boot/boot.b prompt timeout=50 image=/boot/vmlinuz-2.0.34-1 label=linux root=/dev/hda3 initrd=/boot/initrd-2.0.34-1 read-only Naturalmente, alla fine, occorre avviare ‘lilo’ per sistemare il settore di avvio. # lilo Accorgimenti per una distribuzione Red Hat 4037 358.3.4 Dischetto di avvio di emergenza Anche il dischetto di avvio di emergenza può essere ricostruito facilmente. Basta utilizzare il comando seguente, che si rifà al solito caso del kernel 2.0.34-1. # mkbootdisk --device /dev/fd0 2.0.34-1 358.4 Aggiornamento manuale di un’installazione Un altro difetto importante della distribuzione Red Hat è che l’aggiornamento di un’edizione precedente funziona di rado: quasi sempre il programma di installazione si interrompe a metà. Esistendo questo rischio, è decisamente sconsigliabile di tentare un’operazione del genere: o si reinstalla da zero, o è meglio aggiornare pacchetto per pacchetto, al momento della necessità. Chi ha già una buona pratica con il programma ‘rpm’ ed è in grado di superare i problemi comuni dovuti alle dipendenze, potrebbe cimentarsi in una sorta di aggiornamento semiautomatico che viene descritto qui. Si tratta comunque di un’operazione delicata da fare con prudenza e che potrebbe anche fallire. Prima di proseguire è bene chiarire che il pacchetto ‘basesystem’ non deve essere aggiornato e che i pacchetti RPM del kernel vanno aggiornati a parte secondo le modalità già descritte a questo proposito. 358.4.1 Estrazione dell’elenco dei pacchetti installati Il programma ‘rpm’ non prevede un modo per aggiornare solo i pacchetti già esistenti nel sistema. Per arrivare a questo occorre un po’ di lavoro e per prima cosa è necessario ottenere un elenco dei nomi dei pacchetti (vecchi) già installati che si presume di volere aggiornare. # rpm --queryformat ’%{NAME}\n’ -qa | sort Il comando mostrato genera un elenco ordinato dei nomi dei pacchetti installati. La cosa importante è che i nomi sono senza l’indicazione della versione. L’idea è che con l’elenco che si ottiene, dopo aver tolto ‘basesystem’ e i pacchetti del kernel, si potrebbe alimentare un comando di aggiornamento (‘rpm -U’). Si può modificare il comando che genera l’elenco nel modo seguente, per i motivi che si chiariranno in seguito. # rpm --queryformat ’%{NAME}-\[0-9\]\*.rpm\n’ -qa | sort Si ottiene qualcosa di molto simile all’elenco seguente: AfterStep-APPS-[0-9]*.rpm AfterStep-[0-9]*.rpm AnotherLevel-[0-9]*.rpm ElectricFence-[0-9]*.rpm ImageMagick-[0-9]*.rpm MAKEDEV-[0-9]*.rpm SysVinit-[0-9]*.rpm ... Come si può intuire, l’intenzione è quella di ottenere un elenco di modelli (glob) che corrispondano ai rispettivi file dei pacchetti aggiornati, di cui non si conosce a priori il numero della versione. Da come sono stati scritti, si presume che dopo il nome di un pacchetto ci sia un trattino (‘-’), seguito da una cifra numerica, da una stringa indefinita e infine dall’estensione ‘.rpm’. Ciò non può essere sempre vero, però funziona nella maggior parte dei casi. Accorgimenti per una distribuzione Red Hat 4038 358.4.2 Aggiornare i pacchetti in base all’elenco L’elenco descritto nella sezione precedente, quello contenente i modelli di shell (o modelli glob), va controllato e da lì si devono eliminare i pacchetti che non si possono o non si vogliono aggiornare. È già stato ripetuto che non si deve aggiornare ‘basesystem’ e che i pacchetti del kernel vanno aggiornati a parte. Una volta che l’elenco è corretto, ci si può posizionare nella directory che contiene i file RPM aggiornati e si può lanciare il comando di aggiornamento. # cd /mnt/cdrom/RedHat/RPMS # rpm -Uv ‘cat /tmp/elenco‘ 2>&1 | tee /tmp/risultato Dall’esempio si intende che i pacchetti si trovano nella directory ‘/mnt/cdrom/RedHat/ RPMS/’, che l’elenco dei modelli da aggiornare si trova nel file ‘/tmp/elenco’ e che si vuole conservare una copia dei messaggi nel file ‘/tmp/risultato’. Purtroppo, di solito non funziona... 358.4.3 Problemi Questo tipo di procedimento lascia aperti una serie di problemi che si manifestano in modo non del tutto prevedibile. • Alcune dipendenze potrebbero risultare non soddisfatte. Se per qualunque motivo non dovessero essere soddisfatte tutte le dipendenze, si può tentare di isolare i pacchetti che creano questo problema, togliendo le voci relative dal file di elenco, installandoli successivamente a mano, cercando di risolvere le dipendenze. Di solito può trattarsi di librerie nuove o di parti che sono state scorporate in pacchetti separati. Eventualmente si può tentare di installare tali pacchetti prima di iniziare con l’aggiornamento generale. • Alcuni pacchetti potrebbero avere cambiato nome. Se un pacchetto nella versione nuova della distribuzione ha cambiato nome, non si ottiene il suo aggiornamento, perché il modello che si utilizza per indicarlo non coincide. Se si tratta di un pacchetto indispensabile ad altri, si otterrà la segnalazione di errori dovuti alle dipendenze. • Alcuni pacchetti potrebbero essere stati scissi in diversi pacchetti più specifici. Se un pacchetto è stato scisso, può darsi che il nome vecchio sia stato mantenuto per la parte principale di questo, così non si ottiene l’installazione della parte aggiuntiva. Questo fatto può portare comunque a problemi di dipendenza. • A un certo punto dell’aggiornamento si potrebbe arrivare a uno scarico della memoria (core dump). Se l’aggiornamento si interrompe, è possibile modificare il file contenente l’elenco dei modelli in modo da eliminare le voci corrispondenti ai pacchetti già esaminati e aggiornati; quindi si può ripetere il comando di aggiornamento. Accorgimenti per una distribuzione Red Hat 4039 358.5 Semplificazione della configurazione della rete La configurazione della rete secondo l’impostazione della Red Hat, in presenza di situazioni particolari potrebbe tradursi in un labirinto troppo complicato. Anche l’uso degli strumenti previsti, come ‘netcfg’ o ‘linuxconf’, potrebbe essere insufficiente per le proprie esigenze. 358.5.1 Raggiro parziale del problema Si potrebbe decidere di saltare questo sistema, inserendo i comandi necessari all’attivazione delle interfacce di rete e alla definizione degli instradamenti nel file ‘/etc/rc.d/rc.local’. In tal caso conviene: • predisporre il file ‘/etc/sysconfig/network’, avendo cura di attivare la rete (‘NETWORKING=yes’); • predisporre il file ‘/etc/sysconfig/network-scripts/ifcfg-lo’ in modo che sia definita l’interfaccia di loopback; • eliminare ogni altro file ‘/etc/sysconfig/network-scripts/ifcfg-*’ in modo che non venga definita alcuna interfaccia reale; • aggiungere in coda al file ‘/etc/rc.d/rc.local’ i comandi necessari ad attivare le interfacce di rete e a definire gli instradamenti. In alternativa si potrebbe eliminare completamente la directory ‘/etc/sysconfig/ network-scripts/’. In tal caso, nel file ‘/etc/rc.d/rc.local’ andrebbero aggiunti anche i comandi necessari a configurare e instradare l’interfaccia di loopback. Questo tipo di approccio ha anche altre conseguenze, per esempio l’impossibilità di attivare un’interfaccia PPP attraverso gli strumenti della distribuzione. Anche per queste cose occorre creare degli script appositi. L’effetto peggiore di questo metodo sta nel fatto che lo script ‘/etc/rc.d/rc.local’ viene avviato per ultimo, nella sequenza della procedura di inizializzazione del sistema, per cui alcuni servizi che fanno affidamento sull’attivazione precedente della rete potrebbero non essere in grado di avviarsi correttamente. 358.5.2 Sostituzione del file /etc/rc.d/init.d/network Nella procedura di inizializzazione del sistema utilizzato da Red Hat, lo script ‘/etc/rc.d/ init.d/network’ è quello che si utilizza per attivare le interfacce di rete nel momento giusto. La sostituzione del contenuto di questo script con un altro che sia indipendente dai meccanismi che compongono la directory ‘/etc/sysconfig/network-scripts/’, potrebbe essere una soluzione migliore, anche se non perfetta. Quello che segue è un esempio più o meno complesso di quello che potrebbe contenere questo script: si utilizza una porta parallela PLIP con il mascheramento IP e una scheda Ethernet connessa a Internet. #!/bin/sh # # network Attiva/disattiva la rete # # chkconfig: 345 10 97 # description: Attiva/Disattiva la rete. Accorgimenti per una distribuzione Red Hat 4040 # Riutilizza le variabili definite nel file di configurazione # della rete della Red Hat. . /etc/sysconfig/network RiattivazioneRete() { # Si prende cura della configurazione dell’inoltro IPv4 secondo # Red Hat. if [ "$FORWARD_IPV4" = "no" -o "$FORWARD_IPV4" = "false" ] then echo "0" > /proc/sys/net/ipv4/ip_forward echo "L’inoltro IPv4 è disabilitato." else echo "1" > /proc/sys/net/ipv4/ip_forward echo "L’inoltro IPv4 è abilitato." fi # loopback /sbin/ifconfig lo down /sbin/ifconfig lo 127.0.0.1 netmask 255.0.0.0 /sbin/route del 127.0.0.1 /sbin/route del 127.0.0.0 # /sbin/route add -net 127.0.0.0 netmask 255.0.0.0 dev lo /sbin/route add -host 127.0.0.1 dev lo # plip /sbin/modprobe plip /sbin/ifconfig plip0 down /sbin/ifconfig plip0 192.168.254.254 pointopoint 0.0.0.0 /sbin/route del 192.168.254.0 /sbin/route add -net 192.168.254.0 dev plip0 # firewall /sbin/ipchains /sbin/ipchains /sbin/ipchains /sbin/ipchains -F -P -A -L forward forward ACCEPT forward -s 192.168.0.0/16 -d 0/0 -j MASQ forward -n # eth0 /sbin/modprobe eth0 /sbin/ifconfig eth0 down /sbin/ifconfig eth0 196.195.194.7 netmask 255.255.255.0 /sbin/route del 196.195.194.0 /sbin/route add -net 196.195.194.0 netmask 255.255.255.0 dev eth0 Instradamento predefinito /sbin/route del 0.0.0.0 /sbin/route add -net default gw 196.195.194.1 dev eth0 } # Verifica del modo in cui è stato chiamato lo script. case "$1" in start | restart | reload) RiattivazioneRete touch /var/lock/subsys/network ;; stop) /sbin/ifconfig /sbin/ifconfig /sbin/ifconfig /sbin/ifconfig /sbin/ifconfig /sbin/ifconfig eth0 down eth1 down eth2 down plip0 down plip1 down plip2 down Accorgimenti per una distribuzione Red Hat 4041 echo "0" > /proc/sys/net/ipv4/ip_forward echo "L’inoltro IPv4 è disabilitato." rm -f /var/lock/subsys/network ;; status) /sbin/ifconfig /sbin/route -n /sbin/ipchains -L -n ;; probe) exit 0 ;; *) echo "Utilizzo: network {start|stop|restart|reload|status}" exit 1 esac exit 0 Questa alternativa consente l’eliminazione di tutta la directory ‘/etc/sysconfig/ network-scripts/’ e del file ‘/etc/sysconfig/static-routes’; inoltre risolve il problema legato al momento in cui si attiva o disattiva la rete. È evidente che anche in questo caso non è più possibile configurare la rete attraverso gli strumenti consueti e l’attivazione di una possibile connessione PPP deve essere fatta in modo personalizzato, eventualmente attraverso degli script. 358.6 Riferimenti • Morten Kjeldgaard, Peter von der Ahé, Burning a Red Hat CD mini-HOWTO <http://www.linux.org/docs/ldp/howto/HOWTO-INDEX/howtos.html> Appunti di informatica libera 2003.01.01 --- Copyright © 2000-2003 Daniele Giacomini -- daniele @ swlibero.org 4042 Accorgimenti per una distribuzione Red Hat Parte lxxvi i86 359 Minix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4044 359.1 Procurarsi il software . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4044 359.2 Preparazione all’installazione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4045 359.3 Avvio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4046 359.4 Installazione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4048 359.5 Ricompilazione del kernel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4052 359.6 Parametri di avvio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4054 359.7 Configurazione della rete . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4055 359.8 Personalizzazione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4057 359.9 Tastiera . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4057 359.10 Altri programmi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4060 359.11 Copie di sicurezza . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4062 359.12 Convivenza tra Minix e GNU/Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4063 359.13 Riferimenti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4063 360 ELKS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4064 360.1 Sperimentare ELKS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4064 360.2 Immagini di dischetti già pronti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4065 360.3 Avvio di ELKS all’interno di DOSEMU . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4065 360.4 Riferimenti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4065 4043 Capitolo 359 Minix Minix 1 è nato originariamente come sistema didattico. Minix non ha avuto il successo e la diffusione che avrebbe potuto avere a causa delle limitazioni della sua licenza iniziale. In seguito le cose sono cambiate, fortunatamente, perché Minix resta probabilmente l’unica possibilità reale per chi vuole utilizzare elaboratori con architettura i286 o inferiore. Lo scopo di questo capitolo è introdurre all’uso di Minix per poter riutilizzare i vecchi elaboratori i286, in particolare, collegandoli a una rete locale TCP/IP. Le informazioni seguenti si riferiscono alla versione 2.0.0. 359.1 Procurarsi il software Minix è ottenibile dalla rete, precisamente a partire dalla sua pagina di presentazione ufficiale, quella del suo primo autore (La sigla «AST» rappresenta le iniziali di Andrew S. Tanenbaum), oltre che dai vari siti speculari del relativo FTP. <http://www.cs.vu.nl/~ast/minix.html> Minix è nato assieme a un libro che tuttora dovrebbe essere accompagnato da un CD-ROM contenente il sistema operativo: • Andrew S. Tanenbaum, Alber S. Woodhull, Operating Systems: Design and Implementation, 2/e, Prentice-Hall 359.1.1 Pacchetti essenziali Minix è un sistema molto piccolo e composto da pochi pacchetti. Questi hanno alcune particolarità: i nomi sono composti con lettere maiuscole e gli archivi compressi utilizzano la combinazione ‘tar’+‘compress’ e sono evidenziati dall’uso dell’estensione ‘.TAZ’. Per prima cosa è necessario riprodurre la coppia di dischetti ‘ROOT’ e ‘USR’, a partire dai file omonimi. Il primo è in grado di avviarsi e contiene un file system minimo che si installa in un disco RAM, il secondo contiene una piccola serie di programmi da montare nella directory ‘/usr/’, che servono per poter installare Minix nel disco fisso. Se si ha poca memoria a disposizione (i classici 640 Kibyte sono il minimo in assoluto per poter fare funzionare Minix), si può evitare l’utilizzo del disco RAM fondendo i due file in un solo dischetto. Data l’intenzione di questo capitolo verrà descritta l’ultima di queste modalità di installazione. Il mini sistema che si ottiene attraverso i due file appena citati, permette di installare, più o meno automaticamente, un insieme minimo di programmi contenuto nell’archivio ‘USR.TAZ’ Esistono due versioni di questi tre file: una per architettura i386 o superiore e l’altra per i microprocessori inferiori. Date le intenzioni, si dovranno utilizzare i file della versione denominata ‘i86’. 1 Minix licenza simile a BSD 4044 Minix 4045 359.2 Preparazione all’installazione Il procedimento che viene descritto è valido sia per dischetti da 1440 Kibyte che da 1200 Kibyte. I due file ‘ROOT’ e ‘USR’ vanno copiati uno di seguito all’altro. Utilizzando GNU/Linux, si può fare nel modo seguente: # cat ROOT USR > /dev/fd0 Dal punto di vista di Minix, il dischetto inserito nella prima unità a dischetti corrisponde al dispositivo ‘/dev/fd0’, come per GNU/Linux, ma risulta diviso in partizioni: l’immagine ‘ROOT’ risulta essere ‘/dev/fd0a’ e l’immagine ‘USR’ è ‘/dev/fd0c’ (la partizione ‘b’ è vuota).2 L’archivio ‘USR.TAZ’ deve essere suddiviso in diversi dischetti, con un procedimento un po’ insolito: viene semplicemente tagliato a fettine della dimensione massima contenibile dal tipo di dischetti che si utilizza. Nel caso si tratti di dischetti da 1440 Kibyte, si può utilizzare GNU/Linux, o un altro sistema Unix, nel modo seguente: # dd if=/USR.TAZ of=/dev/fd0 bs=1440k count=1 skip=0 # dd if=/USR.TAZ of=/dev/fd0 bs=1440k count=1 skip=1 # dd if=/USR.TAZ of=/dev/fd0 bs=1440k count=1 skip=2 Se si trattasse di dischetti da 1200 Kibyte, occorrerebbe modificare la dimensione del blocco, come nell’esempio seguente: # dd if=/USR.TAZ of=/dev/fd0 bs=1200k count=1 skip=0 # dd if=/USR.TAZ of=/dev/fd0 bs=1200k count=1 skip=1 # dd if=/USR.TAZ of=/dev/fd0 bs=1200k count=1 skip=2 359.2.1 Nomi di dispositivo riferiti alle partizioni Minix viene installato normalmente all’interno di una partizione primaria suddivisa in almeno due partizioni secondarie. La prima partizione serve a contenere il file system principale ed è di piccole dimensioni: 1440 Kibyte. La seconda serve per tutto il resto e viene montata in corrispondenza della directory ‘/usr/’. Inizialmente le partizioni erano tre e ‘/usr/’ era la terza. Attualmente, ‘/usr/’ continua a essere la terza partizione e si finge che esista una seconda partizione senza alcuno spazio a disposizione. Il primo disco fisso viene identificato dal dispositivo ‘/dev/hd0’, il secondo da ‘/dev/hd5’. Le partizioni primarie del primo disco fisso vanno da ‘/dev/hd1’ a ‘/dev/hd4’; quelle del secondo disco fisso da ‘/dev/hd6’ a ‘/dev/hd9’. Le partizioni secondarie corrispondono al nome del dispositivo della partizione primaria con l’aggiunta di una lettera alfabetica che ne indica l’ordine. • /dev/hd0 – /dev/hd1 * /dev/hd1a * /dev/hd1b 2 Se si trattasse della seconda unità a dischetti, si parlerebbe di ‘/dev/fd1’, ‘/dev/fd1a’ e ‘/dev/fd1c’. Minix 4046 * /dev/hd1c * ... – /dev/hd2 – /dev/hd3 – /dev/hd4 • /dev/hd5 – /dev/hd6 – /dev/hd7 – /dev/hd8 – /dev/hd9 Minix può essere installato in una partizione primaria qualunque, purché ci siano almeno 40 Mibyte a disposizione. Utilizzando la versione di Minix ‘i86’, non conviene tentare di superare i 128 Mibyte. 359.3 Avvio Minix utilizza un sistema di avvio piuttosto sofisticato; per fare un paragone con GNU/Linux, si tratta di qualcosa che compie le stesse funzioni di LILO, o di un cosiddetto bootloader. Il sistema che svolge questa funzione in Minix si chiama boot monitor ed è importante capire subito come si utilizza se non si ha molta memoria RAM a disposizione, quanta ne richiederebbe un disco RAM per l’immagine ‘ROOT’. Per cominciare, dopo aver preparato il dischetto ‘ROOT’+‘USR’, lo si inserisce senza la protezione contro la scrittura e si avvia l’elaboratore. Questo è ciò che appare. Minix boot monitor 2.5 Press ESC to enter the monitor Hit a key as follows: = Start Minix Premendo il tasto [ Esc ] si attiva il boot monitor, mentre premendo [ = ] (si fa riferimento alla tastiera americana e questo simbolo si trova in corrispondenza della nostra lettera «ì») si avvia Minix con le impostazioni predefinite. Dal momento che si immagina di avere a disposizione poca memoria (solo 1 Mibyte), non si può avviare Minix così, perché il contenuto dell’immagine ‘ROOT’ verrebbe caricato come disco RAM. È necessario utilizzare subito il boot monitor. [ Esc ] [ESC] fd0> Si ottiene un invito (prompt), attraverso il quale possono essere utilizzati alcuni comandi importanti per predisporre l’avvio del sistema Minix. Per ottenere aiuto si può utilizzare il comando ‘help’. fd0> help[ Invio ] Minix 4047 Si ottiene un riepilogo dei comandi e del modo con cui possono essere utilizzati. Le cose più importanti che si possono fare con il boot monitor sono: l’avvio a partire da una partizione differente da quella prestabilita; l’assegnamento o il ripristino al valore predefinito di una variabile. Queste variabili sono solo entità riferite al sistema di avvio e la loro modifica permette di cambiare il modo con cui si avvia il kernel. Il comando ‘set’ permette di elencare il contenuto di queste variabili. fd0> set[ Invio ] rootdev = (ram) ramimagedev = (bootdev) ramsize = (0) processor = (286) bus = (at) memsize = (640) emssize = (330) video = (vga) chrome = (mono) image = (minix) main() = (menu) I valori appaiono tutti tra parentesi tonde perché rappresentano le impostazioni predefinite. Quando si cambia qualche valore, questo appare senza le parentesi. La prima cosa da cambiare è il dispositivo di avvio, ‘rootdev’. Si deve assegnare il nome di dispositivo riferito all’immagine ‘ROOT’ su dischetto. Si tratta di ‘/dev/fd0a’, come dire, la prima partizione secondaria del dischetto. In questo caso, il nome del dispositivo può anche essere indicato senza la parte iniziale, limitandolo al solo ‘fd0a’. fd0> rootdev=fd0a[ Invio ] fd0> set[ Invio ] rootdev = fd0a ramimagedev = (bootdev) ramsize = (0) processor = (286) bus = (at) memsize = (640) emssize = (330) video = (vga) chrome = (mono) image = (minix) main() = (menu) Per avviare il sistema, basta utilizzare il comando ‘boot’ senza argomenti. fd0> boot[ Invio ] In questo modo si lascia il boot monitor e si avvia il kernel. Una volta avviato il sistema, viene richiesto immediatamente il montaggio della seconda immagine, ‘USR’, contenente gli strumenti necessari all’installazione. Avendo avviato senza disco RAM, il dischetto contenente l’immagine ‘ROOT’ non può essere tolto e questo è il motivo per il quale deve essere contenuta nello stesso dischetto insieme a ‘USR’.3 Minix 2.0.0 Copyright 1997 Prentice-Hall, Inc. Executing in 16-bit protected mode 3 Una cosa da sapere subito è che Minix non utilizza la sequenza [ Ctrl+C ] per interrompere un programma. Per questo si usa il tasto [ Canc ] da solo. Minix 4048 Memory size = 970K MINIX = 206K RAM disk = 0K Available = 765K Mon Nov 3 15:24:15 MET 1997 Finish the name of device to mount as /usr: /dev/ Date le premesse, occorre specificare il nome del dispositivo corrispondente all’immagine ‘USR’: si tratta di ‘fd0c’. fd0c[ Invio ] /dev/fd0c is read-write mounted on /usr Starting standard daemons: update. Login as root and run ’setup’ to install Minix. Minix Release 2.0 Version 0 noname login: root[ Invio ] # 359.4 Installazione L’installazione di Minix avviene in tre fasi: 1. preparazione della partizione di destinazione; 2. trasferimento del contenuto del dischetto, ovvero delle immagini ‘ROOT’ e ‘USR’; 3. dopo il riavvio, trasferimento dell’archivio ‘USR.TAZ’ e possibilmente, se si dispone di una partizione di almeno 40 Mibyte, anche di ‘SYS.TAZ’ e ‘CMD.TAZ’. 359.4.1 Setup Per iniziare l’installazione, dopo aver avviato il sistema Minix dal dischetto, si utilizza lo script ‘setup’. # setup[ Invio ] This is the Minix installation script. Note 1: If the screen blanks suddenly then hit F3 to select "software scrolling". Note 2: If things go wrong then hit DEL and start over. Note 3: The installation procedure is described in the manual page usage(8). It will be hard without it. Note 4: Some questions have default answers, like this: [y] Simply hit RETURN (or ENTER) if you want to choose that answer. Note 5: If you see a colon (:) then you should hit RETURN to continue. : [ Invio ] Dopo la breve spiegazione, avendo premuto il tasto [ Invio ] si passa all’indicazione del tipo di tastiera. La scelta è ovvia, ‘italian’, anche se non corrisponde esattamente: la barra verticale Minix 4049 (quella per le pipeline) si trova al posto della lettera «ì» (i accentata). Durante questa fase di installazione conviene utilizzare la tastiera nazionale (‘italian’) per evitare spiacevoli incidenti quando si utilizza il programma di gestione delle partizioni. What type of keyboard do you have? french german italian japanese latin-am olivetti You can choose one of: scandinavn spanish uk us-std us-swap Keyboard type? [us-std] italian[ Invio ] Minix needs one primary partition of at least 30 Mb (it fits in 20 Mb, but it needs 30 Mb if fully recompiled. Add more space to taste.) If there is no free space on your disk then you have to back up one of the other partitions, shrink, and reinstall. See the appropriate manuals of the the operating systems currently installed. Restart your Minix installation after you have made space. To make this partition you will be put in the editor "part". Follow the advice under the ’!’ key to make a new partition of type MINIX. Do not touch an existing partition unless you know precisely what you are doing! Please note the name of the partition (hd1, hd2, ..., hd9, sd1, sd2, ... sd9) you make. (See the devices section in usage(8) on Minix device names.) : Il programma di Minix che permette di accedere alla tabella delle partizioni è ‘part’ ed è ciò che sta per essere avviato. Come sempre, l’uso di un programma di questo genere è molto delicato: un piccolo errore mette fuori uso tutti i dati eventualmente contenuti in altre partizioni. [ Invio ] Select device Device /dev/hd0 Num Sort ? ? ? ? ? ? ? ? Type ? ? ? ? ? ? ? ? ----first---Cyl Head Sec ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? --geom/last-Cyl Head Sec ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ------sectors----Base Size Kb ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Type ’+’ or ’-’ to change, ’r’ to read, ’?’ for more help, ’!’ for advice Prima di utilizzare questo programma conviene leggere la sua guida interna, ottenibile con la pressione del tasto [ ? ]. Il cursore si presenta inizialmente sull’indicazione del disco, ‘/dev/ hd0’, e può essere cambiato semplicemente premendo i tasti [ + ] o [ - ]. Una volta raggiunto il disco desiderato (in questo caso il primo disco va bene), si deve leggere la sua tabella delle partizioni, in modo da rimpiazzare tutti i punti interrogativi che riempiono lo schermo. [r] Select device Device /dev/hd0 Num 1* 2 3 4 Sort hd1 hd2 hd3 hd4 Type 86 DOS-BIG 00 None 00 None 00 None ----first---Cyl Head Sec 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 --geom/last-Cyl Head Sec 615 8 17 614 7 16 613 0 0 0 7 0 0 0 16 -1 -1 -1 ------sectors----Base Size Kb 0 83640 41820 17 0 0 0 83487 0 0 0 41743 0 0 0 Minix 4050 Con questo esempio si suppone di avere solo un vecchio disco fisso MFM di circa 40 Mibyte, nel quale la prima partizione primaria era stata dedicata in precedenza al Dos. Così, basta cambiare il numero che identifica il tipo di partizione. Per farlo, vi si posiziona sopra il cursore, spostandolo con i tasti freccia, quindi si usano i tasti [ + ] o [ - ] fino a fare apparire il numero 8116. Al primo intervento per cambiare un valore qualsiasi, viene richiesto esplicitamente se si intende modificare effettivamente i dati della tabella delle partizioni. Do you wish to modify existing partitions (y/n) [ y ] Una volta modificato il tipo, la prima partizione dovrebbe apparire così come segue: Num Sort Type 1* hd1 81 MINIX 0 1 0 613 7 16 17 83487 41743 Quindi si conclude. [q] Save partition table? (y/n) [ y ] Lo script di configurazione e installazione riprende richiedendo quale sia la partizione su cui installare Minix. In questo caso si tratta della prima, cioè ‘/dev/hd1’. Please finish the name of the primary partition you have created: (Just type RETURN if you want to rerun "part") /dev/ hd1[ Invio ] You have created a partition named: /dev/hd1 The following subpartitions are about to be created on /dev/hd3: Root subpartition: /usr subpartition: /dev/hd1a /dev/hd1c 1440 kb rest of hd1 Hit return if everything looks fine, or hit DEL to bail out if you want to think it over. The next step will destroy /dev/hd1. : Come accennato in precedenza, Minix viene installato in due partizioni secondarie: la prima serve a contenere il file system principale, la seconda per il resto. In seguito si possono montare anche partizioni successive. Migrating from floppy to disk... Scanning /dev/hd1c for bad blocks. (Hit DEL to stop the scan if are absolutely sure that there can not be any bad blocks. Otherwise just wait.) La scansione del disco fisso è necessaria se si utilizza un vecchio disco MFM, come si suppone di avere in questo esempio, mentre può essere inutile con un disco ATA. È importante fare attenzione: se ci sono settori inutilizzabili, vengono creati alcuni file ‘/usr/.Bad_*’ che non vanno cancellati! Alla fine, lo script procede a copiare il contenuto del dischetto nel disco fisso. What is the memory size of this system in kilobytes? [4096 or more] La dimensione di memoria RAM disponibile effettivamente è solo di 970 Kibyte, quindi si inserisce questo valore; se la memoria a disposizione fosse maggiore o uguale a 4 Mibyte, non occorrerebbe indicare alcunché, basterebbe solo confermare. 970[ Invio ] Second level file system block cache set to 0 kb. Minix 4051 A questo punto, termina l’installazione del dischetto nel disco fisso e si può passare a riavviare il sistema da lì. Please insert the installation ROOT floppy and type ’halt’ to exit Minix. You can type ’boot hd1’ to try the newly installed Minix system. See "TESTING" in the usage manual. # halt[ Invio ] System Halted 359.4.2 Avvio del sistema copiato nel disco fisso Una volta conclusa l’esecuzione dello script di configurazione e installazione, si ritorna sotto il controllo del boot monitor, attraverso il quale è possibile avviare il sistema dalla prima partizione del disco fisso. fd0> boot /dev/hd1[ Invio ] Minix 2.0.0 Copyright 1997 Prentice-Hall, Inc. Executing in 16-bit protected mode at-hd0: 615x8x17 Memory size = 970K MINIX = 206K RAM disk = 0K Available = 765K Mon Nov 3 16:01:27 MET 1997 Starting standard daemons: update. Login as root and run ’setup /usr’ to install floppy sets. Minix Release 2.0 Version 0 noname login: # root[ Invio ] Il suggerimento dato all’avvio ricorda che è possibile installare altre serie di dischetti, a cominciare da ‘USR.TAZ’, utilizzando il comando ‘setup /usr’. 359.4.3 Installazione delle serie di dischetti Tra i pacchetti di Minix, ‘USR.TAZ’ è essenziale e cambia a seconda del tipo di architettura (i86 o i386). Però, dal momento che c’è spazio sufficiente nel disco fisso, conviene installare anche ‘SYS.TAZ’ per poter ricompilare il kernel e ‘CMD.TAZ’ che contiene i sorgenti dei vari programmi di servizio. Tutti questi pacchetti devono essere suddivisi in dischetti nel modo visto in precedenza per il caso di ‘USR.TAZ’. # setup /usr[ Invio ] Lo script ‘setup’ chiede una serie di conferme. What is the size of the images on the diskettes? [all] Premendo semplicemente [ Invio ] si intende che i dischetti vadano letti nella loro interezza. [ Invio ] 4052 Minix What floppy drive to use? [0] Premendo semplicemente [ Invio ] si fa riferimento alla prima unità, ‘/dev/fd0’. [ Invio ] Please insert input volume 1 and hit return Si inserisce il primo dischetto e si conferma [ Invio ] Inizia la fase di estrazione di quanto contenuto nel primo dischetto, a partire dalla directory ‘/usr/’. Quando termina l’estrazione del primo dischetto, viene richiesto il successivo, fino alla conclusione. Conviene ripetere la procedura fino a quando sono stati installati anche gli archivi ‘SYS.TAZ’ e ‘CMD.TAZ’. 359.4.4 Dischetto di avvio Minix è molto semplice e non è necessario un dischetto di avvio realizzato appositamente. È sufficiente il dischetto utilizzato per iniziare l’installazione. Se si hanno difficoltà con l’avviamento di Minix dal disco fisso, si può avviare il boot monitor dal dischetto e con quello utilizzare il comando ‘boot /dev/hd1’. 359.4.5 Conclusione Per chiudere l’attività di Minix, si può fare nel solito modo comune a quasi tutti i sistemi Unix. # shutdown -h now[ Invio ] 359.5 Ricompilazione del kernel Anche Minix, nella sua semplicità, richiede una ricompilazione del kernel per la sua ottimizzazione. In particolare, per poter attivare la gestione del TCP/IP occorre passare per la configurazione e ricompilazione. Il file del kernel, secondo la tradizione di Minix, dovrebbe trovarsi nella directory radice e avere il nome ‘minix’. Se però, invece di trattarsi di un file, si tratta di una directory, nella fase di avvio viene eseguito il file più recente contenuto in tale directory. Il kernel normale, cioè quello che si trova dopo l’installazione, dovrebbe essere ‘/minix/2.0.0’. Per poter ricompilare il kernel occorre avere installato il pacchetto ‘SYS.TAZ’. Si procede come segue: 1. si modifica il file ‘/usr/include/minix/config.h’; 2. ci si posiziona nella directory ‘/usr/src/tools/’; 3. si avvia la compilazione con il comando ‘make’. Al termine si ottiene il file del kernel (o immagine) corrispondente a ‘/usr/src/tools/image’ che si può copiare e rinominare come si ritiene più opportuno. Minix 4053 359.5.1 /usr/include/minix/config.h La configurazione che viene proposta deriva dagli esempi precedenti, in cui si ha una particolare penuria di memoria. Seguono solo alcuni pezzi. /* If ROBUST is set to 1, writes of i-node, directory, and indirect blocks * from the cache happen as soon as the blocks are modified. This gives a more * robust, but slower, file system. If it is set to 0, these blocks are not * given any special treatment, which may cause problems if the system crashes. */ #define ROBUST 1 /* 0 for speed, 1 for robustness */ La macro ‘ROBUST’ permette di sincronizzare le operazioni di accesso al disco. Nell’esempio mostrato si attiva questa opzione, in modo da poter utilizzare il sistema con tranquillità (e ovviamente con maggiore lentezza). /* Number of slots in the process table for user processes. */ #define NR_PROCS 32 Il numero massimo dei processi eseguibili può essere una seria limitazione all’uso simultaneo dell’elaboratore da parte di più utenti, ma la scarsa memoria a disposizione consiglia di mantenere basso questo valore. /* Enable or disable the second level file system cache on the RAM disk. */ #define ENABLE_CACHE2 0 Sempre a causa della carenza di memoria, è opportuno disabilitare la memoria cache. /* Include or exclude device drivers. Set to 1 to include, 0 to exclude. */ #define ENABLE_NETWORKING 1 /* enable TCP/IP code */ #define ENABLE_AT_WINI 1 /* enable AT winchester driver */ #define ENABLE_BIOS_WINI 1 /* enable BIOS winchester driver */ #define ENABLE_ESDI_WINI 1 /* enable ESDI winchester driver */ #define ENABLE_XT_WINI 0 /* enable XT winchester driver */ #define ENABLE_ADAPTEC_SCSI 0 /* enable ADAPTEC SCSI driver */ #define ENABLE_MITSUMI_CDROM 0 /* enable Mitsumi CD-ROM driver */ #define ENABLE_SB_AUDIO 0 /* enable Soundblaster audio driver */ In questa sezione è importante abilitare ciò che serve ed eliminare il resto. In particolare, è qui che si attiva la connettività TCP/IP, che non risulta attivata in modo predefinito. /* NR_CONS, NR_RS_LINES, and NR_PTYS determine the number of terminals the * system can handle. */ #define NR_CONS 2 /* # system consoles (1 to 8) */ #define NR_RS_LINES 1 /* # rs232 terminals (0, 1, or 2) */ #define NR_PTYS 2 /* # pseudo terminals (0 to 64) */ Il numero predefinito di console virtuali è due, ma può essere espanso, sempre che ciò possa avere senso date le limitazioni del sistema. Invece è importante attivare gli pseudoterminali, cioè il numero massimo di connessioni remote. Volendo gestire la rete, è il caso di indicare almeno uno pseduoterminale. Per modificare il file ‘/usr/include/minix/config.h’ si può utilizzare ‘vi’, che è un collegamento a ‘elvis’, oppure ‘elle’.4 Si procede con la compilazione. # cd /usr/src/tools[ Invio ] # make[ Invio ] 4 Il programma ‘elvis’ in particolare, è molto tradizionale: i tasti freccia generano proprio le lettere corrispondenti e quindi non possono essere usati durante la fase di inserimento. ‘elle’ è un Emacs ridotto al minimo. 4054 Minix Al termine della compilazione, se non sono occorsi incidenti, si ottiene il file ‘image’. # cp image /minix/rete.0.1[ Invio ] Questo dovrebbe bastare, trattandosi del file più recente nella directory ‘/minix/’, è anche quello che verrà avviato la prossima volta. # shutdown -h[ Invio ] 359.5.2 File di dispositivo Quando si ricompila il kernel è probabile che si renda necessaria la creazione di file di dispositivo che prima non erano necessari. Nel caso della gestione della rete, sono necessari i file seguenti: • ‘/dev/eth’; • ‘/dev/ip’; • ‘/dev/tcp’; • ‘/dev/udp’; • ‘/dev/ttyp0’ e successivi; • ‘/dev/ptyp0’ e successivi. Questo ragionamento vale anche per le console virtuali: se si vogliono molte console, forse è necessario aggiungere i file relativi. Probabilmente c’è già tutto ciò di cui si può avere bisogno, ma se manca si può creare con lo script ‘MAKEDEV’. MAKEDEV dispositivo Per esempio, trovandosi già nella directory ‘/dev/’, si può creare il dispositivo ‘/dev/tcp’ nel modo seguente: # MAKEDEV tcp 359.6 Parametri di avvio Anche Minix richiede alcuni parametri di avvio in presenza di hardware particolare. La gestione di questi avviene in modo molto semplice attraverso il boot monitor: basta definire una nuova variabile, assegnandole il valore corretto. 359.6.1 Scheda di rete Per gestire una rete occorre una scheda di rete Ethernet. Nell’esempio seguente si immagina di disporre di una scheda compatibile con il modello NE2000 configurata con indirizzo di I/O 30016 e IRQ 11. Il parametro di avvio per ottenere il riconoscimento della scheda Ethernet è ‘DPETHn ’, dove n è il numero della scheda, a partire da zero. DPETHn =indirizzo_i/o :irq:indirizzo_di_memoria Minix 4055 La scheda NE2000 non utilizza alcun indirizzo di memoria, quindi, per il nostro esempio occorre il parametro seguente: DPETH0=300:11 Come si vede, l’indirizzo di I/O è espresso implicitamente in esadecimale e l’IRQ in decimale, mentre l’indirizzo di memoria viene omesso trattandosi di una NE2000. Per inserire tale parametro si utilizza il boot monitor nel modo seguente: hd0> DPETH0=300:11[ Invio ] hd0> save[ Invio ] L’ultima istruzione, ‘save’, salva questo parametro che altrimenti dovrebbe essere indicato ogni volta che si avvia il sistema. Se la scheda di rete viene riconosciuta, all’avvio appare il messaggio seguente: Minix 2.0.0 Copyright 1997 Prentice-Hall, Inc. Executing in 16-bit protected mode ne2000: NE2000 at 300:11 359.7 Configurazione della rete La configurazione della rete va fatta con cura, in modo da non avere bisogno di alcuni demoni che permettono una sorta di autoconfigurazione. Negli esempi seguenti si configura il nuovo sistema Minix tenendo conto di questa situazione: • dinkel.brot.dg, IP 192.168.1.1, servizio DNS e router predefinito; • minix.brot.dg, IP 192.168.1.25, elaboratore Minix. Per quanto possibile, si fa in modo di non avere bisogno del DNS. 359.7.1 /etc/hosts Volendo attivare localmente la risoluzione dei nomi e degli indirizzi è necessario il file ‘/etc/ hosts’, che va configurato come al solito, esattamente come si fa con GNU/Linux. 127.0.0.1 192.168.1.1 192.168.1.25 localhost dinkel.brot.dg minix.brot.dg 359.7.2 /etc/hostname.file Il file ‘/etc/hostname.file’ serve solo a definire il nome dell’elaboratore locale, in senso generale. Non ha niente a che vedere con le interfacce di rete. # echo "minix.brot.dg" > /etc/hostname.file[ Invio ] Minix 4056 359.7.3 /etc/resolv.conf Il file ‘/etc/resolv.conf’ permette di indicare gli indirizzi dei nodi che forniscono un servizio DNS. Nell’esempio proposto si vuole fare in modo che il sistema di risoluzione dei nomi avvenga localmente, per mezzo di quanto contenuto nel file ‘/etc/hosts’. Per questo viene indicato come servente DNS anche l’indirizzo locale (loopback). nameserver 127.0.0.1 nameserver 192.168.1.1 359.7.4 /etc/rc.net Lo script ‘/etc/rc.net’ viene utilizzato da ‘/etc/rc’ per attivare la rete. Lo si può utilizzare per attivare l’interfaccia di rete e per definire l’instradamento verso il router (l’instradamento verso la rete connessa all’interfaccia è predefinito). # Attiva l’interfaccia e l’instradamento verso la sua rete. ifconfig -h 192.168.1.25 # Definisce l’instradamento predefinito verso il router add_route -g 192.168.1.1 359.7.5 /etc/rc Probabilmente, è utile ritoccare il file ‘/etc/rc’, per eliminare l’avvio automatico di alcuni demoni inutili dal momento che la rete è configurata. Quello che segue è il pezzo che attiva la gestione della rete. # Network initialization. (</dev/eth </dev/tcp) 2>/dev/null && net=true # Is there a TCP/IP server? if [ "$net" -a -f /etc/rc.net ] then # There is a customized TCP/IP initialization script; run it. . /etc/rc.net elif [ "$net" ] && [ "‘hostaddr -e‘" = 0:0:0:0:0:0 ] then # No network hardware, configure a fixed address to run TCP/IP alone. ifconfig -h 192.9.200.1 fi if [ "$net" ] then echo -n "Starting network daemons: " for daemon in rarpd nonamed irdpd talkd do if [ -f /usr/bin/$daemon ] then echo -n " $daemon" $daemon & fi done echo . # Get the nodename from the DNS and set it. hostaddr -a >/etc/hostname.file || echo noname >/etc/hostname.file echo -n "Starting network services:" for pair in ’shell in.rshd’ ’login in.rld’ \ ’telnet in.telnetd’ ’ftp in.ftpd’ do Minix 4057 set $pair if [ -f /usr/bin/$2 ] then echo -n " $1" tcpd $1 /usr/bin/$2 & fi done echo . fi Vale la pena di modificare quanto segue: if [ "$net" ] then echo -n "Starting network daemons: " for daemon in nonamed talkd ### rarpd nonamed irdpd talkd do ... Nel pezzo precedente non vengono avviati i demoni ‘rarpd’ e ‘irdpd’, che sono necessari rispettivamente per ottenere l’indirizzo IP in base all’indirizzo hardware della scheda Ethernet e a definire gli instradamenti verso i router. Eventualmente, si potrebbe anche evitare di avviare ‘talkd’ se non si intende utilizzare ‘talk’. Il demone ‘nonamed’ è necessario se non si vuole essere obbligati ad avere un servizio DNS esterno; in pratica è necessario perché venga interpretato il contenuto del file ‘/etc/hosts’. 359.8 Personalizzazione Il sistema risulta configurato in maniera piuttosto disordinata, a cominciare dal fatto che la directory personale dell’utente ‘root’ corrisponde alla directory radice; così, al suo interno si trovano i file di configurazione dell’amministratore. Probabilmente, la prima cosa da fare è quella di creare una directory ‘/root/’, porvi al suo interno i file di configurazione (dovrebbe trattarsi di ‘.ellepro.b1’, ‘.exrc’ e ‘.profile’), modificando anche il file ‘/etc/passwd’ in modo da assegnare all’utente ‘root’ questa nuova directory. 359.8.1 /etc/passwd, /etc/group e /etc/shadow Minix, nonostante la sua semplicità, utilizza le password shadow. Pertanto, se si tenta di inserire un utente manualmente, occorre intervenire anche su questo file, ‘/etc/shadow’, altrimenti l’utente non riuscirà ad accedere. Il file ‘/etc/group’, se non va bene com’è, deve essere modificato manualmente, mentre per gli utenti conviene affidarsi allo script ‘adduser’. adduser utente gruppo directory_home Dopo aver creato un utente, come al solito è opportuno utilizzare il programma ‘passwd’ per assegnare la parola d’ordine.5 359.9 Tastiera La mappa della tastiera viene definita attraverso il programma ‘loadkeys’ e il file contenente la mappa desiderata. Per cui, 5 Lo script ‘adduser’ si avvale della directory personale dell’utente ‘ast’ per inserire i file di configurazione iniziali. Questa directory, corrispondente a ‘/usr/ast/’, svolge il ruolo di scheletro delle directory personali da creare. Volendo si può realizzare un proprio script per rendere la cosa più elegante. Minix 4058 # loadkeys ./tastiera.map permette di caricare la mappa del file ‘tastiera.map’ contenuto nella directory corrente. La mappa della tastiera, secondo la scelta fatta durante l’installazione di Minix, avviene per mezzo del file ‘/etc/keymap’: se lo script ‘/etc/rc’ lo trova durante la fase di avvio, lo carica attraverso ‘loadkeys’. 359.9.1 Modifica della mappa La configurazione della tastiera italiana, per quanto riguarda la versione 2.0 di Minix, non è perfetta. Per modificare la mappa occorre intervenire sul file ‘/usr/src/kernel/keymaps/ italian.src’. Dopo la modifica si deve compilare il sorgente in modo da ottenere il file ‘/usr/src/kernel/keymaps/italian.map’. Al termine, questo file va copiato e rinominato in modo da sostituire ‘/etc/keymap’. Il sorgente corretto potrebbe apparire come nell’esempio seguente, in particolare, per ottenere la tilde (‘~’) si deve usare la combinazione [ AltGr+ì ], mentre per ottenere l’apostrofo inverso (‘‘’) si deve usare la combinazione [ AltGr+’ ]. I caratteri che si trovano oltre il settimo bit, vengono rappresentati in ottale. /* Modified by Daniele Giacomini daniele @ swlibero.org 1998.12.22 /* Keymap for Italian standard keyboard, similar to Linux layout. */ */ u16_t keymap[NR_SCAN_CODES * MAP_COLS] = { /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* scan-code !Shift Shift Alt AltGr Alt+Sh Ctrl */ ==================================================================== */ 00 - none */ 0, 0, 0, 0, 0, 0, 01 - ESC */ C(’[’), C(’[’), CA(’[’),C(’[’), C(’[’), C(’[’), 02 - ’1’ */ ’1’, ’!’, A(’1’), ’1’, ’!’, C(’A’), 03 - ’2’ */ ’2’, ’"’, A(’2’), ’2’, ’@’, C(’@’), 04 - ’3’ */ ’3’, 0234, A(’3’), ’3’, 0234, C(’C’), 05 - ’4’ */ ’4’, ’$’, A(’4’), ’4’, ’$’, C(’D’), 06 - ’5’ */ ’5’, ’%’, A(’5’), ’5’, ’%’, C(’E’), 07 - ’6’ */ ’6’, ’&’, A(’6’), ’6’, ’&’, C(’F’), 08 - ’7’ */ ’7’, ’/’, A(’7’), ’{’, ’/’, C(’G’), 09 - ’8’ */ ’8’, ’(’, A(’8’), ’[’, ’(’, C(’H’), 10 - ’9’ */ ’9’, ’)’, A(’9’), ’]’, ’)’, C(’I’), 11 - ’0’ */ ’0’, ’=’, A(’0’), ’}’, ’=’, C(’@’), 12 - ’-’ */ ’\’’, ’?’, A(’\’’),’\‘’, ’?’, C(’@’), 13 - ’=’ */ 0215, ’^’, 0215, ’~’, ’^’, C(’^’), 14 - BS */ C(’H’), C(’H’), CA(’H’),C(’H’), C(’H’), 0177, 15 - TAB */ C(’I’), C(’I’), CA(’I’),C(’I’), C(’I’), C(’I’), 16 - ’q’ */ L(’q’), ’Q’, A(’q’), ’q’, ’Q’, C(’Q’), 17 - ’w’ */ L(’w’), ’W’, A(’w’), ’w’, ’W’, C(’W’), 18 - ’e’ */ L(’e’), ’E’, A(’e’), ’e’, ’E’, C(’E’), 19 - ’r’ */ L(’r’), ’R’, A(’r’), ’r’, ’R’, C(’R’), 20 - ’t’ */ L(’t’), ’T’, A(’t’), ’t’, ’T’, C(’T’), 21 - ’y’ */ L(’y’), ’Y’, A(’y’), ’y’, ’Y’, C(’Y’), 22 - ’u’ */ L(’u’), ’U’, A(’u’), ’u’, ’U’, C(’U’), 23 - ’i’ */ L(’i’), ’I’, A(’i’), ’i’, ’I’, C(’I’), 24 - ’o’ */ L(’o’), ’O’, A(’o’), ’o’, ’O’, C(’O’), 25 - ’p’ */ L(’p’), ’P’, A(’p’), ’p’, ’P’, C(’P’), 26 - ’[’ */ 0212, 0202, 0212, ’[’, ’{’, C(’[’), 27 - ’]’ */ ’+’, ’*’, A(’+’), ’]’, ’}’, C(’]’), 28 - CR/LF */ C(’M’), C(’M’), CA(’M’),C(’M’), C(’M’), C(’J’), 29 - Ctrl */ CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, 30 - ’a’ */ L(’a’), ’A’, A(’a’), ’a’, ’A’, C(’A’), 31 - ’s’ */ L(’s’), ’S’, A(’s’), ’s’, ’S’, C(’S’), 32 - ’d’ */ L(’d’), ’D’, A(’d’), ’d’, ’D’, C(’D’), 33 - ’f’ */ L(’f’), ’F’, A(’f’), ’f’, ’F’, C(’F’), 34 - ’g’ */ L(’g’), ’G’, A(’g’), ’g’, ’G’, C(’G’), Minix /* 35 /* 36 /* 37 /* 38 /* 39 /* 40 /* 41 /* 42 /* 43 /* 44 /* 45 /* 46 /* 47 /* 48 /* 49 /* 50 /* 51 /* 52 /* 53 /* 54 /* 55 /* 56 /* 57 /* 58 /* 59 /* 60 /* 61 /* 62 /* 63 /* 64 /* 65 /* 66 /* 67 /* 68 /* 69 /* 70 /* 71 /* 72 /* 73 /* 74 /* 75 /* 76 /* 77 /* 78 /* 79 /* 80 /* 81 /* 82 /* 83 /* 84 /* 85 /* 86 /* 87 /* 88 /* 89 /* 90 /* 91 /* 92 /* 93 /* 94 /* 95 /* 96 /* 97 /* 98 /* 99 /*100 4059 - ’h’ */ ’j’ */ ’k’ */ ’l’ */ ’;’ */ ’\’’ */ ’‘’ */ l. SHIFT*/ ’\\’ */ ’z’ */ ’x’ */ ’c’ */ ’v’ */ ’b’ */ ’n’ */ ’m’ */ ’,’ */ ’.’ */ ’/’ */ r. SHIFT*/ ’*’ */ ALT */ ’ ’ */ CapsLck */ F1 */ F2 */ F3 */ F4 */ F5 */ F6 */ F7 */ F8 */ F9 */ F10 */ NumLock */ ScrLock */ Home */ CurUp */ PgUp */ ’-’ */ Left */ MID */ Right */ ’+’ */ End */ Down */ PgDown */ Insert */ Delete */ Enter */ ??? */ ??? */ F11 */ F12 */ ??? */ ??? */ ??? */ ??? */ ??? */ ??? */ ??? */ EXT_KEY */ ??? */ ??? */ ??? */ ??? */ L(’h’), L(’j’), L(’k’), L(’l’), 0225, 0205, ’\\’, SHIFT, 0227, L(’z’), L(’x’), L(’c’), L(’v’), L(’b’), L(’n’), L(’m’), ’,’, ’.’, ’-’, SHIFT, ’*’, ALT, ’ ’, CALOCK, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, NLOCK, SLOCK, HOME, UP, PGUP, NMIN, LEFT, MID, RIGHT, PLUS, END, DOWN, PGDN, INSRT, 0177, C(’M’), 0, ’<’, F11, F12, 0, 0, 0, 0, 0, 0, 0, EXTKEY, 0, 0, 0, 0, ’H’, ’J’, ’K’, ’L’, 0207, 0370, ’|’, SHIFT, ’|’, ’Z’, ’X’, ’C’, ’V’, ’B’, ’N’, ’M’, ’;’, ’:’, ’_’, SHIFT, ’*’, ALT, ’ ’, CALOCK, SF1, SF2, SF3, SF4, SF5, SF6, SF7, SF8, SF9, SF10, NLOCK, SLOCK, ’7’, ’8’, ’9’, ’-’, ’4’, ’5’, ’6’, ’+’, ’1’, ’2’, ’3’, ’0’, ’.’, C(’M’), 0, ’>’, SF11, SF12, 0, 0, 0, 0, 0, 0, 0, EXTKEY, 0, 0, 0, 0, A(’h’), ’h’, A(’j’), ’j’, A(’k’), ’k’, A(’l’), ’l’, 0225, ’@’, 0205, ’#’, ’\\’, ’\\’, SHIFT, SHIFT, 0227, 0227, A(’z’), ’z’, A(’x’), ’x’, A(’c’), ’c’, A(’v’), ’v’, A(’b’), ’b’, A(’n’), ’n’, A(’m’), ’m’, A(’,’), ’,’, A(’.’), ’.’, A(’-’), ’-’, SHIFT, SHIFT, A(’*’), ’*’, ALT, ALT, A(’ ’), ’ ’, CALOCK, CALOCK, AF1, AF1, AF2, AF2, AF3, AF3, AF4, AF4, AF5, AF5, AF6, AF6, AF7, AF7, AF8, AF8, AF9, AF9, AF10, AF10, NLOCK, NLOCK, SLOCK, SLOCK, AHOME, AHOME, AUP, AUP, APGUP, APGUP, ANMIN, ANMIN, ALEFT, ALEFT, AMID, AMID, ARIGHT, ARIGHT, APLUS, APLUS, AEND, AEND, ADOWN, ADOWN, APGDN, APGDN, AINSRT, AINSRT, A(0177),0177, CA(’M’),C(’M’), 0, 0, A(’<’), ’|’, AF11, AF11, AF12, AF12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, EXTKEY, EXTKEY, 0, 0, 0, 0, 0, 0, 0, 0, ’H’, ’J’, ’K’, ’L’, ’@’, ’#’, ’|’, SHIFT, ’|’, ’Z’, ’X’, ’C’, ’V’, ’B’, ’N’, ’M’, ’;’, ’:’, ’_’, SHIFT, ’*’, ALT, ’ ’, CALOCK, ASF1, ASF2, ASF3, ASF4, ASF5, ASF6, ASF7, ASF8, ASF9, ASF10, NLOCK, SLOCK, ’7’, ’8’, ’9’, ’-’, ’4’, ’5’, ’6’, ’+’, ’1’, ’2’, ’3’, ’0’, ’.’, C(’M’), 0, ’>’, ASF11, ASF12, 0, 0, 0, 0, 0, 0, 0, EXTKEY, 0, 0, 0, 0, C(’H’), C(’J’), C(’K’), C(’L’), C(’@’), C(’@’), C(’\\’), SHIFT, C(’@’), C(’Z’), C(’X’), C(’C’), C(’V’), C(’B’), C(’N’), C(’M’), C(’@’), C(’@’), C(’_’), SHIFT, C(’M’), ALT, C(’@’), CALOCK, CF1, CF2, CF3, CF4, CF5, CF6, CF7, CF8, CF9, CF10, NLOCK, SLOCK, CHOME, CUP, CPGUP, CNMIN, CLEFT, CMID, CRIGHT, CPLUS, CEND, CDOWN, CPGDN, CINSRT, 0177, C(’J’), 0, C(’@’), CF11, CF12, 0, 0, 0, 0, 0, 0, 0, EXTKEY, 0, 0, 0, 0, Minix 4060 /*101 /*102 /*103 /*104 /*105 /*106 /*107 /*108 /*109 /*110 /*111 /*112 /*113 /*114 /*115 /*116 /*117 /*118 /*119 /*120 /*121 /*122 /*123 /*124 /*125 /*126 /*127 }; - ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 Dopo la modifica, si avvia la compilazione. # cd /usr/src/kernel/keymaps/[ Invio ] # make[ Invio ] Vengono generati tutti i file di configurazione che non siano già presenti (se si vuole ripetere la compilazione occorre prima rimuovere il file ‘italian.map’). # cp italian.map /etc/keymap[ Invio ] Al prossimo riavvio sarà utilizzata la nuova mappa. 359.10 Altri programmi Il software a disposizione per Minix non è molto e può essere trovato negli stessi FTP da cui si accede ai file del sistema operativo citati qui. In particolare, tra i programmi riferiti alla rete, vale la pena di ricordare i pacchetti ‘HTTPD.TAZ’ e ‘FROG.TAZ’. Il primo è un servente HTTP molto semplice e il secondo è un programma per il tracciamento dell’instradamento (simile a Traceroute). Sono entrambi molto utili e compilabili facilmente. 359.10.1 HTTPD.TAZ Minix dispone di un servente HTTP elementare e lo si trova distribuito nel pacchetto ‘HTTPD.TAZ’. I pacchetti supplementari, come questo, vanno installati a partire dalla directory ‘/usr/local/’ e i sorgenti vanno collocati in ‘/usr/local/src/’. # cd /usr/local/src[ Invio ] Supponendo che il file ‘HTTPD.TAZ’ si trovi nel dischetto, montato nella directory ‘/mnt/’, si può agire come segue: Minix 4061 # cat /mnt/HTTPD.TAZ | compress -d | tar xvf -[ Invio ] Si ottiene la creazione della directory ‘httpd/’ a partire dalla posizione corrente, cioè ‘/usr/ local/src/’. # cd httpd[ Invio ] Si procede con la compilazione. # make[ Invio ] Quindi si installa il programma. # make install[ Invio ] L’installazione non si occupa di copiare i file di configurazione: bisogna farlo manualmente. # cp httpd.conf /etc[ Invio ] # cp httpd.mtype /etc[ Invio ] Il demone ‘httpd’, installato in ‘/usr/local/bin/’, ha bisogno di un utente ‘www’, in base alla configurazione predefinita del file ‘/etc/httpd.conf’ che non serve modificare. # adduser www operator /usr/home/www[ Invio ] Naturalmente la scelta della directory personale di questo utente fittizio è solo un fatto di gusto personale. Sempre in base alla configurazione predefinita, occorre aggiungere alla directory personale la directory ‘exec/’ e all’interno di questa si devono collocare un paio di file. # mkdir /usr/home/www/exec[ Invio ] # cp dir2html /usr/home/www/exec[ Invio ] # cp dir2html.sh /usr/home/www/exec[ Invio ] Per ultimo, occorre avviare il demone. Per questo conviene ritoccare il file ‘/etc/rc.net’ in modo da aggiungere la riga seguente: tcpd http /usr/local/bin/httpd & 359.10.2 FROG.TAZ ‘frog’ è un programma per il tracciamento dell’instradamento. È semplice, ma anche molto importante. L’estrazione dell’archivio avviene nel modo solito, così come la compilazione e l’installazione. # cd /usr/local/src[ Invio ] # cat /mnt/FROG.TAZ | compress -d | tar xvf -[ Invio ] # cd frog[ Invio ] # make[ Invio ] # make install[ Invio ] Tutto qui. 4062 Minix 359.11 Copie di sicurezza Le copie di sicurezza possono essere fatte soltanto utilizzano ‘tar’ e ‘compress’. Dal momento che il sistema è organizzato in modo piuttosto rigido, con una partizione principale molto piccola e una partizione ‘/usr/’, normalmente conviene preoccuparsi solo di questa seconda partizione. Per la prima converrebbe realizzare un dischetto di avvio e installazione con gli stessi file di configurazione, compresi ‘/etc/passwd’, ‘/etc/group’ e ‘/etc/shadow’. 359.11.1 Archiviazione Se si dispone di abbastanza spazio libero nella partizione ‘/usr/’, se ne può fare la copia di sicurezza in un file collocato all’interno della stessa partizione. Successivamente si può scaricare su dischetti. Si può procedere nel modo seguente: # cd /usr[ Invio ] # tar cf - . | compress -c > .BKP.TAZ[ Invio ] Si ottiene il file ‘/usr/.BKP.TAZ’ contenente la copia di quanto contenuto nella directory corrente ‘/usr/’. Successivamente si può copiare il file ottenuto, a pezzi, su una serie di dischetti formattati in precedenza. Si comincia con la formattazione e si suppone di disporre di dischetti da 1440 Kibyte. # format /dev/fd0 1440[ Invio ] ... Una volta preparati i dischetti formattati, si può scaricare il file nei dischetti. # dd if=/usr/.BKP.TAZ of=/dev/fd0 bs=1440k count=1 skip=0[ Invio ] # dd if=/usr/.BKP.TAZ of=/dev/fd0 bs=1440k count=1 skip=2[ Invio ] ... 359.11.2 Recupero Per recuperare un sistema archiviato nel modo mostrato nella sezione precedente, si deve cominciare dall’installazione con il dischetto iniziale. La cosa migliore sarebbe l’utilizzo di un dischetto modificato opportunamente in modo che i file di configurazione corrispondano a quanto utilizzato nel proprio sistema. Dopo l’installazione iniziale che consiste nel trasferimento di quanto contenuto nel dischetto iniziale nel disco fisso, si procede con l’installazione della copia (preparata in precedenza) della partizione collocata a partire da ‘/usr/’. # setup /usr[ Invio ] Uno dopo l’altro verranno richiesti tutti i dischetti. Minix 4063 359.12 Convivenza tra Minix e GNU/Linux Se lo si desidera, si può fare convivere Minix assieme a GNU/Linux, nello stesso disco fisso, su partizioni distinte. Ma installare Minix in una partizione libera di un disco in cui GNU/Linux è già stato installato richiede prudenza e attenzione. • L’installazione di Minix provoca l’alterazione dell’MBR del disco fisso, di conseguenza, al termine si avvia solo Minix. Quindi, prima di installare Minix occorre preparare uno o più dischi di avvio di GNU/Linux, in modo da poter in seguito ripristinare il sistema di avvio attraverso LILO. • Quando si conclude il lavoro con Minix e si esegue un riavvio con un semplice [ Ctrl+Alt+Canc ], si ottiene un avvio a caldo (warm boot), ma se dopo si vuole avviare un kernel Linux, questo non potrà essere caricato. Pertanto, è necessario un riavvio a freddo, al limite attraverso lo spegnimento e la riaccensione dell’elaboratore. 359.12.1 LILO Una volta che si è riusciti a fare riavviare il sistema GNU/Linux, con i dischetti di avvio a cui si faceva riferimento in precedenza, conviene modificare il file ‘/etc/lilo.conf’ in modo che si possa scegliere tra l’avvio di GNU/Linux, Minix ed eventualmente altro. Supponendo di avere installato Minix nella seconda partizione del primo disco fisso, le righe necessarie nel file ‘/etc/lilo.conf’ sono quelle seguenti: other=/dev/hda2 label=minix table=/dev/hda Volendo supporre che Minix sia stato installato nel secondo disco fisso, sempre nella seconda partizione, le righe sarebbero quelle seguenti: other=/dev/hdb2 label=minix table=/dev/hdb loader=/boot/chain.b In pratica, è esattamente ciò che si fa quando si vuole controllare l’avvio del Dos. 359.13 Riferimenti Minix è un sistema operativo molto limitato rispetto a GNU/Linux. Resta comunque l’unica opportunità, almeno per ora, di fronte a vecchi elaboratori i286 o inferiori. Molti particolari importanti non sono stati descritti, ma le informazioni relative sono comunque accessibili dai siti FTP di distribuzione di Minix e dalla documentazione interna costituita delle pagine di manuale (‘man’). • Andrew S. Tanenbaum, Alber S. Woodhull, Operating Systems: Design and Implementation, 2/e, Prentice-Hall • <http://www.cs.vu.nl/~ast/minix.html> Appunti di informatica libera 2003.01.01 --- Copyright © 2000-2003 Daniele Giacomini -- daniele @ swlibero.org Capitolo 360 ELKS Il progetto ELKS (Embeddable linux kernel subset) vuole creare un sistema operativo per i microprocessori ix86 di fascia bassa, a partire da un sottoinsieme di funzionalità di GNU/Linux. È possibile avviare un mini sistema composto da un dischetto di avvio (boot) e un dischetto contenente un sistema minimo; con un po’ di pazienza è anche possibile installarlo in una partizione del disco fisso. È disponibile un compilatore da utilizzare con GNU/Linux, per produrre binari ELKS, cioè tutto il necessario per sviluppare questo nuovo sistema; è possibile eseguire i binari ELKS su GNU/Linux, attraverso una libreria di emulazione; è possibile avviare ELKS anche all’interno di DOSEMU. 360.1 Sperimentare ELKS ELKS non è un sistema completo, quindi necessita di un pacchetto di sviluppo, composto essenzialmente da un compilatore, da utilizzare in una piattaforma GNU/Linux normale. Questo pacchetto è Dev86, distribuito normalmente in forma sorgente. Una volta scaricato il pacchetto di sviluppo, questo può essere espanso a partire dalla directory ‘/usr/src/’, nell’elaboratore GNU/Linux, come mostrato dall’esempio seguente: # cd /usr/src[ Invio ] # tar xzvf Dev86src-0.13.4.tar.gz[ Invio ] Si otterrà la directory ‘/usr/src/linux-86’ che si articola ulteriormente. Terminata l’installazione occorre compilare questi sorgenti e installarli. # cd /usr/src/linux-86[ Invio ] # make install[ Invio ] A questo punto si può pensare ai sorgenti del kernel di ELKS e dei vari programmi di sistema e di servizio. Anche questi vanno installati a partire da ‘/usr/src/’. # cd /usr/src[ Invio ] # tar xzvf elks-0.0.67.tar.gz[ Invio ] # tar xzvf elkscmd.tar.gz[ Invio ] Si ottengono le directory ‘/usr/src/elks/’ e ‘/usr/src/elkscmd/’. La prima contiene il kernel, la seconda i programmi di contorno. Per compilare il kernel basta eseguire i passi seguenti. # cd /usr/src/elks[ Invio ] # make config[ Invio ] # make dep ; make clean[ Invio ] # make[ Invio ] Si ottiene il file ‘/usr/src/elks/Image’ che può essere trasferito nel modo solito in un dischetto, attraverso ‘cp’ o ‘dd’. 4064 ELKS 4065 Per compilare gli altri programmi occorre passare le varie directory in cui si articola ‘/usr/src/ elkscmd/’ e usare il comando ‘make’. 360.2 Immagini di dischetti già pronti La realizzazione di un sistema ELKS è un po’ difficoltosa in questa fase iniziale del suo progetto di realizzazione. La cosa migliore è partire dalle immagini già pronte, contenute normalmente in un pacchetto unico. Per trasferirle nei dischetti ci si comporta nel modo solito, esattamente come si fa per le immagini di dischetti di GNU/Linux. Volendo, l’immagine ‘boot’ (quella di avvio) può essere sostituita semplicemente con un kernel compilato personalmente, mentre l’immagine ‘root’ può essere rielaborata aggiungendo o sostituendo altri programmi. L’immagine ‘root’ contiene un file system Minix. Per mettere in funzione il sistema ELKS è sufficiente avviare l’elaboratore con il dischetto ottenuto dall’immagine ‘boot’, sostituendolo con quello dell’immagine ‘root’, quando il kernel lo richiede. 360.3 Avvio di ELKS all’interno di DOSEMU ELKS può essere avviato all’interno di DOSEMU, sia in una console che in una finestra di X. Per farlo basta avviare l’emulatore in modo che esegua il caricamento dal dischetto. Questo si ottiene di solito utilizzando l’opzione ‘-A’. # dos -A[ Invio ] La figura 360.1 mostra la fase finale dell’avvio di ELKS. Si nota in particolare l’invito della shell (il prompt), che è molto scarno: per ora non si possono usare i caratteri jolly e tutte le altre funzioni cui si è abituati con le shell normali. Figura 360.1. L’avvio di ELKS. 360.4 Riferimenti • ELKS: the embeddable Linux kernel system <http://elks.sourceforge.net/> • Source Forge: ELKS <http://sourceforge.net/projects/elks/> 4066 Appunti di informatica libera 2003.01.01 --- Copyright © 2000-2003 Daniele Giacomini -- daniele @ swlibero.org ELKS Parte lxxvii Dos 361 Dos: introduzione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4070 361.1 Avvio del sistema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4070 361.2 Dispositivi secondo il Dos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4071 361.3 Directory, file ed eseguibili . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4071 361.4 Comandi e ambiente di avvio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4073 361.5 Caratteri jolly . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4074 361.6 Invito dell’interprete dei comandi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4075 361.7 Comandi interni principali . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4075 361.8 Flussi standard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4081 361.9 Accesso diretto ai dispositivi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4083 361.10 Riferimenti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4083 362 Dos: dischi, file system, directory e file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4084 362.1 Suddivisione in partizioni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4084 362.2 Inizializzazione di un’unità di memorizzazione . . . . . . . . . . . . . . . . . . . . . . . . . . 4084 362.3 Etichetta di un’unità di memorizzazione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4085 362.4 Analisi e correzione del file system . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4086 362.5 Copia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4086 362.6 Trasferimento del sistema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4087 362.7 Modifica delle unità . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4087 362.8 Altre particolarità . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4088 363 Dos: configurazione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4092 363.1 CONFIG.SYS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4092 363.2 AUTOEXEC.BAT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4096 363.3 Comandi ridondanti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4096 363.4 Localizzazione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4097 363.5 Orologio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4098 364 Dos: script dell’interprete dei comandi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4099 364.1 Parametri, variabili ed espansione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4099 364.2 Chiamate di altri script . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4099 364.3 Strutture di controllo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4099 364.4 Comandi utili negli script . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4102 365 Dos: gestione della memoria centrale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4105 365.1 Gestione particolare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4105 4067 365.2 Comandi appositi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4105 365.3 Verifica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4106 366 FreeDOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4107 366.1 Installazione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4107 366.2 Impostazione e configurazione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4108 366.3 RxDOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4109 366.4 Riferimenti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4109 367 Progetto GNUish . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4110 367.1 Programmi di servizio vari . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4110 367.2 Gnuplot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4110 367.3 Spreadsheet Calculator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4111 367.4 Ispell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4111 367.5 Perl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4111 367.6 Riferimenti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4111 368 The valuable DOS Freeware page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4112 368.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4112 368.2 OS and GUI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4113 368.3 Utility . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4113 368.4 Network . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4114 368.5 Compilers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4115 368.6 Typesetting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4116 368.7 More Dos software sources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4116 368.8 Search engines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4117 369 Clean the Clipper 5.2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4118 369.1 Step 1: try to compile with the /P parameter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4120 369.2 Step 2: understand well the use of code blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . 4120 369.3 Step 3: understand the object programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4121 369.4 Step 4: understand the get object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4122 369.5 Step 5: trying to stop using commands . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4124 369.6 Step 6: free yourself from STD.CH - /U . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4136 369.7 Step 7: take control over all include files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4136 370 nanoBase 1997 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4143 370.1 What is it . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4143 370.2 The dot command line . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4143 370.3 The menu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4144 370.4 The macro recording, compiling and execution . . . . . . . . . . . . . . . . . . . . . . . . . . 4144 370.5 The report system . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4145 370.6 The integrated text editor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4146 4068 370.7 The internal documentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4146 370.8 Download it . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4147 370.9 Bugs and known problems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4147 371 nanoBase 1997 user manual . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4149 371.1 Dos xBase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4150 371.2 Composition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4154 371.3 How to use nB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4156 371.4 Status line . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4157 371.5 The dot line . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4158 371.6 The menu system . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4158 371.7 The text editor DOC() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4169 371.8 The help text file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4170 371.9 Macro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4170 371.10 Data types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4174 371.11 Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4182 371.12 Delimiters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4183 371.13 Code blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4184 371.14 Standard functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4184 371.15 nB functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4232 371.16 Normal command substitution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4262 371.17 nB command substitution functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4269 371.18 RPT: the nB print function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4273 371.19 How can I... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4277 371.20 The source files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4277 4069 Capitolo 361 Dos: introduzione DOS è acronimo di Disk Operating System e sta a indicare il nome di un sistema operativo per micro elaboratori basati su microprocessori i86, successore del vecchio CP/M. Probabilmente, data la sua estrema limitatezza, è un po’ azzardato voler parlare di «sistema operativo», tanto che qualcuno lo appella: «gestore di interruzioni» (interrupt). Questo sistema operativo nasce come software proprietario; tuttavia, attualmente il progetto più attivo attorno a questo tipo di sistema è FreeDOS, il cui scopo è quello di realizzarne un’edizione libera e completa. 361.1 Avvio del sistema Un sistema Dos è composto essenzialmente da un kernel, un interprete dei comandi e da una serie di programmi di servizio. Questo concetto è analogo ai sistemi Unix, con la differenza che il kernel offre funzionalità molto scarse e solo per mezzo di interruzioni software (IRQ). Nelle versioni proprietarie del Dos, il kernel era suddiviso in due file, che raccoglievano funzionalità distinte in base all’importanza relativa. I nomi usati sono stati differenti e nel caso di FreeDOS il kernel è contenuto tutto in un solo file (tabella 361.1). Tabella 361.1. Comparazione tra i nomi dei file che compongono il kernel di un sistema Dos. Microsoft IO.SYS MSDOS.SYS IBM IBMBIO.COM IBMDOS.COM Novell, Caldera IBMBIO.COM IBMDOS.COM RxDOS RXDOSBIO.SYS RXDOS.SYS FreeDOS KERNEL.SYS -- I file del kernel devono trovarsi nella directory radice della partizione o del dischetto per poter essere avviati. Per la precisione, l’avvio del kernel viene gestito direttamente dal codice inserito nel settore di avvio della partizione o del dischetto (512 Kibyte), che a sua volta viene avviato dal firmware (il BIOS, secondo la terminologia specifica dell’architettura i86 e successiva). Il kernel, dopo essere stato avviato, non attiva una procedura di avvio, ma si limita a interpretare uno script speciale, ‘CONFIG.SYS’, e subito dopo avvia l’interprete dei comandi, ovvero la shell. Tradizionalmente, il programma in questione è ‘COMMAND.COM’. Secondo la tradizione, l’interprete dei comandi che viene avviato dal kernel si occupa subito di eseguire lo script ‘AUTOEXEC.BAT’. Gli script ‘CONFIG.SYS’ e ‘AUTOEXEC.BAT’ devono trovarsi nella directory radice del disco o della partizione da cui si avvia il sistema, ovvero quella in cui si trova già il kernel che viene avviato. L’interprete dei comandi, ‘COMMAND.COM’, è in grado di eseguire direttamente alcune funzionalità, attraverso comandi interni che non si traducono in programmi di servizio veri e propri. Tradizionalmente ‘COMMAND.COM’ si colloca nella directory radice del disco o della partizione in cui si trova il kernel stesso. Ciò non è propriamente indispensabile, ma conviene attenersi a questa linea per evitare fastidi inutili. 4070 Dos: introduzione 4071 361.2 Dispositivi secondo il Dos I dispositivi secondo il Dos hanno un nome, composto da lettere e cifre numeriche, terminato da due punti opzionali: [] nome_dispositivo : Il nome in questione può essere indicato utilizzando lettere maiuscole o minuscole, senza che la cosa faccia differenza. I nomi più comuni sono elencati nella tabella 361.2. È il caso di osservare che i due punti che concludono il nome, vanno usati necessariamente quando questo viene abbinato ad altre informazioni da cui non potrebbe essere distinto (per esempio un percorso). Tabella 361.2. Nomi dei dispositivi più comuni in Dos. Dispositivo ‘A:’ ‘B:’ ‘C:’ ‘D:’, ‘E:’,... ‘Z:’ ‘CON:’ ‘PRN:’ ‘LPT1:’, ‘LPT2:’,... ‘COM1:’, ‘COM2:’,... Descrizione Disco nella prima unità a dischetti. Disco nella seconda unità a dischetti. Prima partizione Dos nel primo disco fisso. Partizione Dos o altro tipo di disco. Console: tastiera e schermo. Porta stampante principale. Porte parallele. Porte seriali. Il Dos mantiene distinti i dischi e le partizioni, nel senso che questi non devono creare una struttura unica come avviene nei sistemi Unix. Pertanto, quando si fa riferimento a un percorso di un file o di una directory, si deve tenere in considerazione anche il disco o la partizione in cui si trova. Il modo utilizzato dal Dos per identificare i dischi e le partizioni, di fatto impedisce di accedere a questi dispositivi in modo indipendente dal file system sottostante. Per intenderci, l’«unità» ‘X:’ può essere una partizione Dos di un disco non meglio identificato; mentre non esiste un modo univoco per poter raggiungere il dispositivo fisico in cui si trova questo disco. 361.3 Directory, file ed eseguibili Il Dos è nato dopo Unix e da questo sistema ha ereditato alcuni concetti elementari (forse troppo pochi). I percorsi di file e directory si separano con una barra obliqua, che però è inversa rispetto allo Unix. Anche con il Dos c’è una directory radice; tuttavia si aggiunge l’indicazione dell’unità di memorizzazione (il disco o la partizione). Si può osservare a questo proposito la figura 361.1. Figura 361.1. Struttura di un percorso in un file system Dos. C:\PRIMO\SECONDO\TERZO\QUARTO ^ ^ ^ ^ ^ | | | | | | | | | file o directory finale | | | | | | | directory | | | | | separazione tra una directory e la successiva | | | directory radice | unità di memorizzazione I nomi di file e directory possono essere indicati utilizzando lettere maiuscole o minuscole, senza che la cosa possa fare differenza. Questi nomi possono essere composti utilizzando anche cifre numeriche e altri simboli (che comunque è bene usare con parsimonia). 4072 Dos: introduzione Per la precisione, sono esclusi i simboli: ‘/’, ‘\’, ‘[’, ‘]’, ‘<’, ‘>’, ‘+’, ‘=’, ‘;’, ‘:’, ‘,’, ‘?’, ‘*’, ‘{’, ‘}’ e il punto che va usato esattamente come descritto nel seguito. Tradizionalmente, il Dos utilizza un tipo di file system elementare, denominato FAT (Dos-FAT), in cui i nomi dei file e delle directory possono essere composti utilizzando al massimo 11 caratteri, di cui otto compongono un prefisso e tre un suffisso. Il prefisso e il suffisso di questi nomi appaiono uniti attraverso un punto. Per esempio: ‘CIAO.COM’, ‘LETTERA.TXT’, ‘PIPPO.NB’,... Questa conformazione dei nomi è una caratteristica fondamentale del Dos, da cui derivano una serie di consuetudini e di limitazioni molto importanti. È importante osservare che non è opportuno che i nomi dei file coincidano con quelli dei dispositivi (senza i due punti finali). In pratica, non conviene creare file del tipo ‘CON:’, ‘PRN:’, ecc. Tutto dipende dal contesto, ma in generale è bene fare attenzione a questo particolare. Come nei sistemi Unix il Dos annovera il concetto di directory corrente, a cui si aggiunge il concetto di unità di memorizzazione corrente. Infatti, la directory va collocata in un disco o in una partizione. In base a questo principio, si possono indicare dei percorsi relativi, che fanno riferimento alla posizione corrente (nell’unità di memorizzazione corrente). Tuttavia, in più, ogni unità di memorizzazione ha una sua directory corrente. Per esempio, fare riferimento a un file in una certa unità di memorizzazione ‘x :’, senza specificare il percorso, significa indicare implicitamente la directory corrente di quella unità. Per esempio, supponendo che la directory corrente dell’unità ‘X:’ sia ‘X:\PRIMO\SECONDO\’, facendo riferimento al file ‘X:CIAO’, si intende indicare implicitamente il file ‘X:\PRIMO\ SECONDO\CIAO’. In un percorso si possono usare anche i simboli ‘.’ e ‘..’, con lo stesso significato che hanno in un sistema Unix: la directory stessa e la directory genitrice. Il file system tradizionale del Dos consente di annotare solo poche informazioni per i file e le directory: la data di modifica e quattro indicatori booleani, rappresentati da altrettante lettere: • H file o directory nascosti; • S file o directory di sistema; • R file o directory in sola lettura e non cancellabile; • A file o directory da archiviare (i dati sono stati modificati). Si tratta di attributi completamente differenti da quelli di Unix. Si può osservare in particolare la mancanza di un attributo che specifichi la possibilità di eseguire un programma o di attraversare una directory. Secondo la tradizione Dos, gli attributi vanno considerati nel modo seguente: • A viene attivato ogni volta che il file viene scritto o modificato e serve per automatizzare i sistemi di copia periodica; • R se attivo, il Dos non consente la scrittura o la rimozione; • S se attivo si tratta di un file di «sistema», ma in pratica si comporta come l’attributo H; • H se attivo si tratta di un file «nascosto», che così non dovrebbe apparire nelle liste di file e directory. Dos: introduzione 4073 In generale, file e directory nascosti o di sistema non dovrebbero essere spostati fisicamente, nemmeno nell’ambito della stessa unità di memorizzazione. Questa esigenza nasce in particolare per i file del kernel, che non possono essere spostati se si vuole poter riavviare il sistema operativo. Dal momento che il file system non permette di determinare se un file è un eseguibile, l’unico modo per permettere al sistema di conoscere questa caratteristica sta nell’uso di suffissi convenzionali nei nomi: i file che terminano con l’estensione ‘.COM’ e ‘.EXE’ sono programmi binari (la differenza tra i due tipi di estensione riguarda il formato del binario); quelli che terminano per ‘.BAT’ sono script dell’interprete dei comandi (‘COMMAND.COM’). La prima stranezza che deriva da questa caratteristica del Dos sta nel fatto che per avviare un eseguibile di questi, è sufficiente indicare il nome del file senza l’estensione, che diventa così un componente opzionale agli occhi dell’utilizzatore. 361.4 Comandi e ambiente di avvio L’interprete dei comandi tradizionale dei sistemi Dos è il programma ‘COMMAND.COM’, che viene avviato direttamente dal kernel. ‘COMMAND.COM’ può essere avviato più volte successive, anche se di solito ciò è di scarsa utilità, dal momento che il Dos non è un sistema operativo in multiprogrammazione. In ogni caso, quando viene avviato dal kernel, si occupa di interpretare ed eseguire lo script ‘AUTOEXEC.BAT’ che si trova nella directory radice dell’unità di avvio. ‘COMMAND.COM’ mostra un invito simile idealmente a quello delle shell Unix, dopo il quale possono essere inseriti i comandi. A loro volta, questi possono essere riferiti a comandi interni corrispondenti a funzionalità offerte direttamente dall’interprete, oppure possono rappresentare la richiesta di avvio di un programma esterno. Figura 361.2. Riga di comando. C:\>DIR A: /W ^ ^ ^ ^ | | | | | | | opzione | | | | | argomento | | | comando | invito Il Dos ha ereditato da Unix anche il concetto di variabile di ambiente. Il meccanismo è lo stesso ed è fondamentale la variabile di ambiente ‘PATH’, con la quale si possono indicare i percorsi di ricerca degli eseguibili. Tuttavia, il Dos ha delle caratteristiche speciali, per cui, è il caso di fare alcuni esempi di comandi: • C:\>C:\PRIMO\SECONDO.EXE questo comando avvia l’esecuzione del file ‘C:\PRIMO\SECONDO.EXE’; • C:\>C:\PRIMO\SECONDO questo comando potrebbe avviare l’esecuzione del primo dei file seguenti che riesce a trovare; – ‘C:\PRIMO\SECONDO.COM’ Dos: introduzione 4074 – ‘C:\PRIMO\SECONDO.EXE’ – ‘C:\PRIMO\SECONDO.BAT’ • C:\>SECONDO questo comando potrebbe avviare l’esecuzione del primo dei file seguenti che dovesse riuscire a trovare, ma in mancanza può continuare la ricerca nei percorsi indicati nella variabile di ambiente ‘PATH’. – ‘C:.\SECONDO.COM’ – ‘C:.\SECONDO.EXE’ – ‘C:.\SECONDO.BAT’ I percorsi indicati nella variabile di ambiente ‘PATH’ sono separati da un punto e virgola; per esempio: C:\;C:\DOS;C:\FDOS\BIN Di solito, il Dos dà per scontato che si cerchino gli eseguibili a cominciare dalla directory corrente. Per questo, occorre considerare che è sempre come se la variabile di ambiente ‘PATH’ contenesse questa indicazione prima delle altre: ‘.;C:\;C:\DOS;C:\FDOS\BIN’. È da osservare che FreeDOS si comporta in maniera differente, in quanto richiede espressamente questa indicazione della directory corrente. 361.5 Caratteri jolly Il Dos imita l’utilizzo dei caratteri jolly come avviene nei sistemi Unix per opera delle shell. Tuttavia, nel Dos non si tratta di un’espansione che avviene per opera della shell, ma vi deve provvedere ogni programma per conto proprio. Questo rappresenta una gravissima deficienza del Dos, che però è irrimediabile. Su questa base, i comandi tendono a richiedere l’indicazione di un argomento che rappresenta il nome di uno o più file prima delle opzioni eventuali. Ma c’è un altro problema. Il punto che divide in due i nomi dei file e delle directory è un muro insuperabile per i caratteri jolly. I simboli che si possono utilizzare sono solo l’asterisco e il punto interrogativo. L’asterisco vale per una sequenza qualunque di caratteri, escluso il punto; il punto interrogativo vale per un carattere qualunque.1 Esempi *.* Corrisponde a un nome qualunque. *.COM Un nome che termina con l’estensione ‘.COM’. CIAO.X?X Tutti i nomi che iniziano per ‘CIAO’ e hanno un’estensione composta da un lettera «X» iniziale e finale, senza specificare cosa ci sia al secondo posto. * Tutti i nomi che non hanno estensione (che non contengono il punto). 1 Ci sono programmi di origine Unix, portati in Dos, che non hanno questa limitazione riferita al punto che separa l’estensione. Dos: introduzione 4075 361.6 Invito dell’interprete dei comandi Esiste un’altra variabile di ambiente fondamentale per il Dos. Si tratta di ‘PROMPT’, che consente di modificare l’aspetto dell’invito dell’interprete dei comandi. La cosa funziona un po’ come nelle shell Unix, per cui si assegna una stringa che può contenere dei simboli speciali, praticamente delle sequenze di escape che vengono espanse prima della visualizzazione. La tabella 361.3 riepiloga questi simboli particolari. In origine, il Dos mostrava in modo predefinito un invito simile all’esempio seguente, C> in cui appare solo l’unità di memorizzazione corrente. Questo tipo di impostazione corrisponderebbe alla stringa ‘$N$G’. In seguito, si è passati a un invito simile al prossimo esempio, C:\BIN\> in cui si aggiunge anche l’informazione della directory corrente. Questo corrisponde alla stringa ‘$P$G’. Tabella 361.3. Sequenze di escape per definire dei componenti speciali all’interno di una stringa di invito. Simbolo $Q $$ $T $D $V $N $G $L $B $H $E $_ Corrispondenza = $ Ora corrente. Data corrente. Numero della versione. Lettera dell’unità corrente. > < | <BS> (cancella il carattere precedente) <ESC> (1B16) Codice di interruzione di riga. Cancellando il contenuto della variabile di ambiente ‘PROMPT’ si ripristina la stringa di invito predefinita. 361.7 Comandi interni principali I comandi interni sono quelli che non corrispondono a programmi di servizio veri e propri, ma sono funzionalità svolte direttamente dall’interprete dei comandi. Nelle sezioni seguenti ne vengono descritti brevemente alcuni. 361.7.1 CH, CHDIR [percorso] CHDIR [percorso] CH ‘CH’, o ‘CHDIR’, è un comando interno dell’interprete dei comandi, che consente di visualizzare o di cambiare la directory corrente. È indifferente l’uso di ‘CD’ o di ‘CHDIR’; se il comando non è seguito dal percorso, si ottiene solo la visualizzazione della directory corrente. Si osservi che Dos: introduzione 4076 se si indica un percorso assoluto di unità di memorizzazione, se questa non corrisponde a quella attuale, si cambia la directory corrente di quella unità. Esempi C:\>CD Visualizza la directory corrente. C:\>CD \TMP\LAVORO Sposta la directory corrente in ‘\TMP\LAVORO\’. C:\TMP\LAVORO>CD DATI\LETTERE Sposta la directory corrente in ‘DATI\LETTERE\’ che a sua volta discende dalla posizione iniziale precedente. C:\TMP\LAVORO\DATI\LETTERE>CD .. Sposta la directory corrente nella posizione della directory genitrice di quella iniziale. C:\TMP\LAVORO\DATI>CD F:\TMP Cambia la directory corrente dell’unità ‘F:’, senza intervenire nell’unità corrente. 361.7.2 X: {A|B|...|Z}: Il Dos gestisce le unità di memorizzazione in modo speciale. Per cambiare l’unità di memorizzazione corrente, non esiste un comando analogo a ‘CD’: si deve indicare il nome dell’unità a cui si vuole accedere. Esempi C:\>A: Cambia l’unità di memorizzazione attuale, facendola diventare ‘A:’. A:\>F: Cambia l’unità di memorizzazione attuale, facendola diventare ‘F:’. 361.7.3 MD, MKDIR MD directory MKDIR directory ‘MD’, o ‘MKDIR’, è un comando interno dell’interprete dei comandi, che consente di creare una directory vuota. Esempi C:\>MD LAVORO Crea la directory ‘LAVORO\’ a partire da quella corrente. C:\>MD \TMP\DATA Dos: introduzione Crea la directory ‘\TMP\DATA\’ nell’unità corrente. C:\>MD F:\TMP\DATA Crea la directory ‘\TMP\DATA\’ nell’unità ‘F:’. 4077 Dos: introduzione 4078 361.7.4 RD, RMDIR RM directory RMDIR directory ‘RD’, o ‘RMDIR’, è un comando interno dell’interprete dei comandi, che consente di cancellare una directory vuota. Esempi C:\>RD LAVORO Cancella la directory ‘LAVORO\’ a partire da quella corrente. C:\>RD \TMP\DATA Cancella la directory ‘\TMP\DATA\’ nell’unità corrente. C:\>RD F:\TMP\DATA Cancella la directory ‘\TMP\DATA\’ nell’unità ‘F:’. 361.7.5 DIR DIR [directory|file] [/P] [/W] ‘DIR’ è un comando interno dell’interprete dei comandi, che consente di visualizzare l’elenco del contenuto di una directory o l’elenco di un gruppo di file. L’argomento del comando può essere composto utilizzando caratteri jolly, secondo lo standard del Dos, ovvero i simboli ‘*’ e ‘?’. Alcune opzioni /P Blocca lo scorrimento dell’elenco in attesa della pressione di un tasto quando questo è più lungo del numero di righe che possono apparire sullo schermo. /W Visualizza solo i nomi dei file e delle directory, senza altre informazioni, permettendo così di vedere più nomi assieme in un’unica schermata. Esempi C:\>DIR *.* Visualizza l’elenco di tutti i file contenuti nella directory corrente. C:\>DIR ESEMPIO.* Visualizza l’elenco di tutti i file il cui nome inizia per ‘ESEMPIO’ e continua con un’estensione qualunque. C:\>DIR *.DOC Visualizza l’elenco di tutti i file il cui nome termina con l’estensione ‘.DOC’. C:\>DIR F:\DOC\*.* Visualizza l’elenco di tutti i file contenuti nella directory ‘\DOC\’ dell’unità ‘F:’. C:\>DIR F: Visualizza l’elenco di tutti i file contenuti nella directory corrente dell’unità ‘F:’. Dos: introduzione 4079 361.7.6 COPY [file_destinazione ] [opzioni] file_2 [+ ...] [file_destinazione ] [opzioni] COPY file_origine COPY file_1 + ‘COPY’ è un comando interno dell’interprete dei comandi, che consente di copiare uno o più file (sono escluse le directory). Anche qui è consentito l’uso di caratteri jolly, ma al contrario dei sistemi Unix, i caratteri jolly possono essere usati anche nella destinazione. Il ‘COPY’ del Dos consente anche di unire assieme più file. Alcune opzioni /V Fa in modo che venga verificato il risultato della copia. /B Fa sì che la copia avvenga in modo «binario». Questa opzione può servire quando si copia un file su un dispositivo e si vuole evitare che alcuni codici vengano interpretati in modo speciale. /Y Non chiede conferma prima di sovrascrivere i file, se questi esistono già nella destinazione. Esempi C:\>COPY ESEMPIO PROVA Copia il file ‘ESEMPIO’ nella directory corrente ottenendo il file ‘PROVA’, sempre nella directory corrente. C:\>COPY C:\DOS\*.* C:\TMP Copia tutto il contenuto della directory ‘\DOS\’ dell’unità ‘C:’ nella directory ‘\TMP\’ nella stessa unità ‘C:’, mantenendo gli stessi nomi. C:\>COPY TESTA+CORPO+CODA LETTERA Copia, unendoli, i file ‘TESTA’, ‘CORPO’ e ‘CODA’, ottenendo il file ‘LETTERA’. C:\>COPY *.DOC *.TXT Copia tutti i file che nella directory corrente hanno un nome che termina con l’estensione ‘.DOC’, generando altrettanti file, con lo stesso prefisso, ma con l’estensione ‘.TXT’. C:\>COPY PROVA.PRN PRN: /B Copia il file ‘PROVA.PRN’ nel dispositivo ‘PRN:’, ovvero sulla stampante, assicurandosi che la copia avvenga senza alterare alcunché. 361.7.7 DEL, ERASE DEL file ERASE file ‘DEL’, o ‘ERASE’, è un comando interno dell’interprete dei comandi, che consente di cancellare uno o più file (sono escluse le directory). È da considerare che i file che hanno l’attributo di sola lettura attivo, non possono essere modificati e nemmeno cancellati. Dos: introduzione 4080 Esempi C:\TMP>DEL *.* Cancella tutti i file nella directory corrente. C:\TMP>DEL ESEMPIO.* Cancella tutti i file contenuti nella directory corrente, il cui nome inizia per ‘ESEMPIO’ e termina con qualunque estensione. C:\TMP>DEL *.BAK Cancella tutti i file contenuti nella directory corrente, il cui nome termina con l’estensione ‘.BAK’. 361.7.8 REN, RENAME REN file_origine nome_nuovo RENAME file_origine nome_nuovo ‘REN’, o ‘RENAME’, è un comando interno dell’interprete dei comandi, che consente di cambiare il nome di uno o più file (sono escluse le directory). Il primo argomento può essere un percorso relativo o assoluto, completo anche dell’indicazione dell’unità, mentre il secondo argomento è il nuovo nome, che implicitamente non può essere collocato altrove. Esempi C:\>REN ESEMPIO PROVA Cambia il nome del file ‘ESEMPIO’, che si trova nella directory corrente, in ‘PROVA’. C:\>REN *.TXT *.DOC Cambia il nome di tutti i file che, nella directory corrente, hanno l’estensione ‘.TXT’, trasformandoli in modo tale da avere un’estensione ‘.DOC’. 361.7.9 SET SET [variabile_di_ambiente =stringa] ‘SET’ è un comando interno dell’interprete dei comandi che ha lo scopo di assegnare un valore a una variabile di ambiente, oppure di leggere lo stato di tutte le variabili di ambiente esistenti. Quando si assegna un valore a una variabile, questa viene creata simultaneamente; quando non si assegna nulla a una variabile, la si elimina. Esempi C:\>SET Elenca le variabili di ambiente esistenti assieme al loro valore. C:\>SET PROMPT=$P$G$G Assegna alla variabile di ambiente ‘PROMPT’ la stringa ‘$P$G$G’. Questo si traduce nella modifica dell’aspetto dell’invito dell’interprete dei comandi. C:\>SET PATH=.;C:\BIN;D:\BIN Dos: introduzione 4081 Assegna alla variabile di ambiente ‘PATH’ la stringa ‘.;C:\BIN;D:\BIN’. C:\>SET PROMPT= Elimina la variabile di ambiente ‘PROMPT’, assegnandole la stringa nulla. 361.7.10 TYPE TYPE file ‘TYPE’ è un comando interno dell’interprete dei comandi, che consente di leggere ed emet- tere il contenuto di un file attraverso lo standard output. Questo si traduce in pratica nella visualizzazione del file in questione. Esempi C:\>TYPE LETTERA Emette il contenuto del file ‘LETTERA’ che si trova nella directory e nell’unità corrente. C:\>TYPE C:\DOC\MANUALE Emette il contenuto del file ‘MANUALE’ che si trova nella directory ‘\DOC\’ dell’unità ‘C:’. 361.8 Flussi standard Il Dos ha ereditato da Unix anche i concetti legati ai flussi standard. In pratica, i programmi hanno a disposizione tre flussi predefiniti: uno in lettura rappresentato dallo standard input, due in scrittura rappresentati dallo standard output e dallo standard error. Il meccanismo è lo stesso di Unix, anche se non funziona altrettanto bene; infatti, non è possibile ridirigere lo standard error attraverso l’interprete dei comandi. Secondo la tradizione delle shell Unix, la ridirezione dello standard output si ottiene con il simbolo ‘>’ posto alla fine del comando interessato, seguito poi dal nome del file che si vuole generare in questo modo. Per esempio, C:\>TYPE LETTERA > PRN: invece di visualizzare il contenuto del file ‘LETTERA’, lo invia al dispositivo di stampa corrispondente al nome ‘PRN:’; inoltre, C:\>DIR *.* > ELENCO invece di visualizzare l’elenco dei file che si trovano nella directory corrente, crea il file ‘ELENCO’ con questi dati. La ridirezione dello standard output fatta in questo modo, va a cancellare completamente il contenuto del file di destinazione, se questo esiste già; al contrario, si può utilizzare anche ‘>>’, con il quale, il file di destinazione viene creato se non esiste, oppure viene solo esteso. Lo standard input viene ridiretto utilizzando il simbolo ‘<’, con il quale è possibile inviare un file a un comando utilizzando il flusso dello standard input. Alcuni comandi hanno la caratteristica di utilizzare esclusivamente i flussi standard. Si parla in questi casi di programmi filtro. Il programma di servizio tipico che si comporta in questo modo è ‘SORT’, il quale riceve un file di testo dallo standard input e lo riordina restituendolo attraverso lo standard output. Si osservi l’esempio seguente: Dos: introduzione 4082 C:\>SORT < ELENCO > ORDINATO In questo modo, ‘SORT’ riceve dallo standard input il file ‘ELENCO’ e genera attraverso la ridirezione dello standard output il file ‘ORDINATO’. Per mettere in contatto lo standard output di un comando con lo standard input del successivo, si utilizza il simbolo ‘|’. L’esempio seguente mostra un modo alternativo di ottenere l’ordinamento di un file: C:\>TYPE ELENCO | SORT > ORDINATO In generale, tutti i comandi che generano un risultato visuale che scorre sullo schermo, utilizzano semplicemente lo standard output, che può essere ridiretto in questo modo. Si osservi ancora l’esempio seguente che riordina il risultato del comando ‘DIR’, mostrandolo comunque sullo schermo: C:\>DIR *.DOC | SORT Nelle sezioni seguenti vengono mostrati alcuni comandi filtro. 361.8.1 SORT SORT [opzioni] < file_da_ordinare > file_ordinato Il comando ‘SORT’, che dovrebbe corrispondere a un programma di servizio vero e proprio, riordina il file di testo che ottiene dallo standard input, generando un risultato che emette attraverso lo standard output. Alcune opzioni /R Riordina in modo decrescente /+n_colonna Riordina in base al testo che inizia a partire dalla colonna indicata come argomento (si tratta di un numero a partire da uno, per indicare la prima colonna). Esempi C:\>DIR *.DOC | SORT /+10 Emette l’elenco della directory corrente riordinato in base all’estensione, che è un’informazione collocata a partire dalla decima colonna. 361.8.2 MORE MORE < file_da_leggere MORE file_da_leggere Il comando ‘MORE’ legge un file, fornito come argomento o attraverso lo standard input, mostrandolo poi sullo schermo una pagina dopo l’altra. In questo modo, è possibile leggere il contenuto dei file più lunghi delle righe a disposizione sullo schermo. Per passare alla pagina successiva, basta premere un tasto qualunque, oppure ciò che viene indicato espressamente. Dos: introduzione 4083 Esempi C:\>DIR | MORE Permette di controllare lo scorrimento a video del risultato del comando ‘DIR’. C:\>MORE LETTERA.TXT Permette di controllare lo scorrimento a video del contenuto del file ‘LETTERA.TXT’. C:\>TYPE LETTERA.TXT | MORE Si ottiene lo stesso risultato dell’esempio precedente, attraverso l’uso di una pipeline. 361.9 Accesso diretto ai dispositivi Il Dos offre poche occasioni per accedere direttamente ai dispositivi. Si tratta generalmente solo della console e della porta parallela. L’esempio seguente mostra come «copiare» un file sul dispositivo di stampa, per ottenere così la sua stampa diretta: C:\>COPY LETTERA PRN: La stessa cosa avrebbe potuto essere ottenuta con la ridirezione dei flussi standard: C:\>TYPE LETTERA > PRN: Può essere interessante la possibilità di copiare il flusso di ingresso della console in un file: C:\>COPY CON: LETTERA In questo caso, l’inserimento nel file ‘LETTERA’ prosegue fino a quando viene ricevuto un codice EOF, che si ottiene qui con la combinazione di tasti [ Ctrl+z ] seguita da [ Invio ]. È bene ricordare che la console, ovvero il dispositivo ‘CON:’, riceve dati in ingresso attraverso la tastiera ed emette dati in uscita utilizzando lo schermo. In pratica, quando un programma attende dati dallo standard input non ridiretto, li riceve dalla console, cioè dalla tastiera; nello stesso modo, quando un programma emette dati attraverso lo standard output non ridiretto, li invia alla console, cioè sullo schermo. 361.10 Riferimenti • FreeDOS <http://www.freedos.org> • OpenDOS Unofficial Home Page <http://www.deltasoft.com/opendos.htm> Appunti di informatica libera 2003.01.01 --- Copyright © 2000-2003 Daniele Giacomini -- daniele @ swlibero.org Capitolo 362 Dos: dischi, file system, directory e file La gestione dei dischi, ovvero delle unità di memorizzazione di massa, è molto particolare nel Dos. In generale, si fa riferimento a queste cose attraverso un lettera che ne rappresenta il dispositivo; tuttavia, tale dispositivo può indicare un disco intero o solo una partizione, riferendosi sempre solo a dischi e partizioni Dos. 362.1 Suddivisione in partizioni Nel Dos, tutti i dischi rimovibili, come i dischetti, non vanno suddivisi in partizioni, mentre i dischi fissi devono essere preparati in questo modo. Il programma che si usa per queste cose è ‘FDISK’. Secondo il Dos, le partizioni di un disco possono essere solo quattro. Tuttavia, una partizione normale può essere suddivisa in sottopartizioni, che vengono definite tradizionalmente «estese», dove anche queste possono essere al massimo quattro. Una tale struttura ha condizionato in pratica anche altri sistemi operativi, per esempio GNU/Linux. Bisogna tenere in considerazione l’origine storica per comprendere che altri sistemi operativi possono comportarsi in modo completamente differente. Di solito, ‘FDISK’ ha una visione delle partizioni tutta orientata verso il Dos. Infatti, consente di creare una sola partizione primaria (ovvero una partizione normale) e altre partizioni estese (ovvero altre sottopartizioni di una seconda partizione primaria). Bisogna considerare che il settore di avvio del Dos viene collocato nel primo settore della partizione primaria utilizzata per il Dos. In questo modo, manca la sistemazione del primo settore del disco, l’MBR, che deve contenere il codice necessario a raggiungere il settore di avvio. FDISK [/MBR] In generale, sembra che le varie edizioni di ‘FDISK’ per Dos funzionino solo con il primo disco fisso. Fondamentalmente, il programma è interattivo, per cui si avvia una maschera con la quale si interviene per mezzo di un menù. Di norma viene consentito di cancellare le partizioni, di crearne una primaria e probabilmente una sola di estesa. Di solito, è possibile riscrivere il settore di avvio MBR attraverso l’opzione ‘/MBR’. 362.2 Inizializzazione di un’unità di memorizzazione L’inizializzazione di un’unità di memorizzazione, intesa come un dischetto o una partizione, si ottiene con il comando ‘FORMAT’. Questo si occupa anche di predisporre il file system Dos-FAT ed eventualmente anche di trasferire il kernel, per renderlo avviabile. FORMAT lettera_unità : [/N:settori] [/T:cilindri] [/S] [/U] In alcune edizioni del Dos, questo comando non inizializza l’unità di memorizzazione, ma si limita a sovrascrivere la parte iniziale. Ciò viene fatto per accelerare il procedimento e per permettere eventualmente il recupero dei dati, in caso di ripensamenti. In generale, sarebbe meglio evitare questa scorciatoia quando si tratta di unità corrispondenti ai dischetti; così, per confermare la richiesta di un’inizializzazione tradizionale, si può aggiungere l’opzione ‘/U’. 4084 Dos: dischi, file system, directory e file 4085 Esempi C:\>FORMAT A: /U Inizializza l’unità ‘A:’, corrispondente a un dischetto. L’inizializzazione avviene in modo completo, essendo stata usata l’opzione ‘/U’; inoltre, dal momento che non sono state indicate altre cose, il formato usato è quello predefinito in base alla configurazione del firmware. C:\>FORMAT A: /N:9 /T:40 /U Come nell’esempio precedente, con l’aggiunta dell’indicazione della geometria: nove settori per traccia e 40 cilindri; si sottintende la presenza di due tracce per cilindro. Pertanto, dal momento che ogni settore è di 512 byte: 2 * 40 * 9 * 512 byte = 360 Kibyte. C:\>FORMAT A: /N:9 /T:80 /U Come nell’esempio precedente, ma con 80 cilindri: 2 * 80 * 9 * 512 byte = 720 Kibyte. C:\>FORMAT A: /N:15 /T:80 /U Come nell’esempio precedente, ma con 15 settori per traccia: 2 * 80 * 15 * 512 byte = 1200 Kibyte. C:\>FORMAT A: /N:18 /T:80 /U Come nell’esempio precedente, ma con 18 settori per traccia: 2 * 80 * 18 * 512 byte = 1440 Kibyte. C:\>FORMAT A: /S Inizializza il dischetto corrispondente all’unità ‘A:’, trasferendo successivamente il kernel e probabilmente anche l’interprete dei comandi (‘COMMAND.COM’). Ciò avviene perché è stata usata l’opzione ‘/S’. 362.3 Etichetta di un’unità di memorizzazione Tradizionalmente, il Dos prevede la possibilità di attribuire un nome a un’unità di memorizzazione. Questo nome viene definito solitamente «etichetta» e di fatto viene annotato come un file speciale nella directory radice (anche se poi non appare nell’elenco). Per modificare o attribuire questo nome si utilizza il comando ‘LABEL’: LABEL [lettera_unità :][nome] Se non si indica la lettera dell’unità di memorizzazione su cui intervenire, si tratta implicitamente di quella da cui è stato avviato il sistema; se non si indica il nome da attribuire, ‘LABEL’ funziona in modo interattivo, chiedendo il da farsi. In linea di principio, l’etichetta di un’unità non serve, salvo il caso di qualche programma che potrebbe utilizzarla per uno scopo particolare (per esempio i programmi di installazione per identificare i dischetti). Esiste anche un altro comando interno per la verifica del nome di un’unità; si tratta di ‘VOL’: VOL [lettera_unità :] Il risultato è solo l’informazione del nome stesso, con l’aggiunta del numero di serie se questo dato è disponibile. Dos: dischi, file system, directory e file 4086 362.4 Analisi e correzione del file system Esistono pochi strumenti di analisi e correzione degli errori nel file system. In origine si tratta del comando ‘CHKDSK’, a cui in seguito si è aggiunto ‘SCANDISK’. CHKDSK lettera_unità : [/F] ‘CHKDSK’ può essere usato solo con l’indicazione di un’unità di memorizzazione; in tal caso restituisce le informazioni disponibili su questa. Se si aggiunge l’opzione ‘/F’, si richiede esplicitamente la correzione, per quanto possibile, degli errori rilevati. L’errore tipico di un file system Dos-FAT si traduce in «concatenamenti perduti», ovvero file, interi o parziali, di cui non si può conoscere il nome. Questi file potrebbero essere solo dati temporanei che è bene siano cancellati, ma questa non è la regola. ‘CHKDSK’ tende a salvare questi file assegnando loro un nome più o meno casuale, lasciando all’utilizzatore l’onere di decidere cosa farne. 362.5 Copia Nei sistemi Dos la copia è un’attività piuttosto articolata. In pratica, il comando interno ‘COPY’ consente solo di copiare file puri e semplici. Per copiare un dischetto occorre il comando ‘DISKCOPY’; per copiare file e directory occorre il comando ‘XCOPY’. 362.5.1 DISKCOPY DISKCOPY unità_di_origine : unità_di_destinazione : ‘DISKCOPY’ permette di eseguire la copia di un’unità di memorizzazione, purché si tratti di un dischetto. Il dischetto di destinazione dovrebbe essere inizializzato preventivamente. L’unità indicata come secondo argomento, che rappresenta la destinazione, può essere la stessa di quella di origine. In questo caso, i dischetti andranno alternati nel dispositivo che li ospita, seguendo le istruzioni che dà ‘DISKCOPY’ stesso. Esempi C:\>DISKCOPY A: A: Esegue la copia di un dischetto usando lo stesso dispositivo fisico. 362.5.2 XCOPY XCOPY percorso_origine [percorso_destinazione] [/E] [/S] [/H] [/V] ‘XCOPY’ consente di copiare uno o più file assieme alla struttura di directory. In altri termini, ciò significa che è possibile copiare anche una directory intera. Alcune opzioni /S Copia solo le directory e le sottodirectory non vuote. /E Copia tutte le sottodirectory, anche se vuote. Dos: dischi, file system, directory e file 4087 /H Copia anche i file nascosti e di sistema. /V Verifica la copia. Esempi C:\>XCOPY \PIPPO\*.* \PAPPA\*.* /E /S /H /V Copia tutta la struttura che si articola a partire dalla directory ‘\PIPPO\’, nella directory ‘\PAPPA\’, includendo anche i file nascosti e quelli di sistema. 362.6 Trasferimento del sistema Il Dos è un sistema operativo elementare. L’essenziale in assoluto è costituito dal kernel e dall’interprete dei comandi. Per rendere «avviabile» un dischetto o una partizione basta copiare questi file e sistemare il settore di avvio, in modo che punti correttamente al kernel. Questo si può ottenere con il comando ‘FORMAT’, quando lo si usa con l’opzione ‘/S’ (cosa che naturalmente implica anche l’inizializzazione dell’unità), oppure con il comando ‘SYS’, fatto appositamente per questo: FORMAT lettera_unità : /S SYS lettera_unità : A seconda del tipo di Dos, vengono copiati solo i file del kernel, oppure anche l’interprete dei comandi (necessario per avviare il sistema). 362.7 Modifica delle unità La caratteristica del Dos per cui si distinguono le unità di memorizzazione, introduce l’esigenza di comandi particolari, che vengono descritti brevemente nelle sezioni seguenti. In particolare, si tratta della possibilità di attribuire una lettera di unità differente e di poter inserire un’unità in una directory come avviene con l’innesto di un file system nei sistemi Unix. 362.7.1 ASSIGN [] [] ASSIGN lettera_unità_1 : =lettera_unità_2 : ASSIGN /STATUS ASSIGN Il comando ‘ASSIGN’ permette di modifica il nome di un’unità di memorizzazione. Per ottenere questo risultato, rimane attivo come programma residente in memoria. Quando si usa senza argomenti, ‘ASSIGN’ elimina tutte le ridefinizioni; con l’opzione ‘/STATUS’ si ottiene lo stato attuale delle ridefinizioni; quando si indicano le lettere di unità, la prima è l’unità virtuale che viene creata come riproduzione della seconda. Esempi C:\>ASSIGN E:=A: Dopo questo comando, per accedere all’unità corrispondente al primo dischetto, si potrà indicare l’unità ‘E:’. C:\>ASSIGN Cancella tutte le ridefinizioni delle unità di memorizzazione. Dos: dischi, file system, directory e file 4088 362.7.2 JOIN JOIN lettera_unità : percorso JOIN lettera_unità : /D JOIN Il comando ‘JOIN’ permette di attaccare un’unità di memorizzazione in corrispondenza di un percorso (una directory). Si tratta in pratica di montare l’unità, come avviene nei sistemi Unix. Quando si usa ‘JOIN’ senza argomenti, si ottiene un elenco degli innesti attivi; quando si usa l’opzione ‘/D’, si vuole annullare il collegamento dell’unità. Esempi C:\>JOIN A: C:\MNT\A Innesta l’unità ‘A:’ nella directory ‘C:\MNT\A\’. C:\>JOIN A: /D Distacca l’unità ‘A:’ da un collegamento precedente. 362.7.3 SUBST SUBST lettera_unità : percorso SUBST /D Il comando ‘SUBST’ permette di creare un’unità virtuale a partire da una directory di un’altra unità. In pratica, si fa in modo di permettere l’identificazione di una certa directory attraverso l’uso di una lettera di unità. Quando si usa ‘JOIN’ con l’opzione ‘/D’, si vuole annullare l’unità virtuale relativa. Esempi C:\>SUBST E: C:\EXTRA\E Crea l’unità virtuale ‘E:’ a partire dal contenuto delle directory ‘C:\EXTRA\E\’. C:\>JOIN E: /D Elimina l’unità virtuale ‘E:’. 362.8 Altre particolarità La gestione del Dos di file e directory è molto strana. Nelle sezioni seguenti vengono descritti alcuni programmi tipici dei sistemi Dos riguardanti la gestione di file e directory, che non hanno trovato un’altra collocazione in questo documento, a causa della loro particolarità. Dos: dischi, file system, directory e file 4089 362.8.1 VERIFY VERIFY [ON|OFF] Il comando interno ‘VERIFY’ permette di richiedere al sistema operativo di verificare la registrazione nelle unità di memorizzazione. Come si vede dallo schema sintattico, si attiva o si disattiva la modalità, attraverso l’uso delle parole chiave ‘ON’ oppure ‘OFF’. Di solito, questa modalità è disabilitata ed è difficile definire la reale importanza di questa impostazione. Se si usa il comando senza alcun argomento, si ottiene di sapere quale sia l’impostazione attuale. 362.8.2 APPEND APPEND directory APPEND ; APPEND Il comando ‘APPEND’ consente di definire un percorso per la ricerca dei file di dati. In pratica, si vuole permettere ai programmi di accedere a file di dati anche quando questi si trovano fuori della collocazione prevista. ‘APPEND’ può essere usato più volte, per aggiungere altre directory. Se viene usato con l’argomento ‘;’, si intende cancellare tutto l’elenco di directory di ricerca dei file di dati. Se viene usato senza argomenti, si ottiene l’elenco di queste directory. Esempi C:\>APPEND C:\DATI Aggiunge la directory ‘C:\DATI\’ all’elenco dei percorsi di ricerca per i file di dati. 362.8.3 ATTRIB ATTRIB [+R|-R] [+A|-A] [+S|-S] [+H|-H] file Il comando ‘ATTRIB’ permette di visualizzare o cambiare gli attributi del file. In pratica, utilizzando la forma ‘+x ’ si attiva l’attributo x , mentre con ‘-x ’ si disattiva l’attributo stesso. Esempi C:\>ATTRIB *.* Mostra gli attributi di tutti i file contenuti nella directory corrente. C:\>ATTRIB +R *.* Imposta l’attributo di sola lettura per tutti i file della directory corrente. 362.8.4 DELTREE DELTREE directory Il comando ‘DELTREE’ consente di eliminare una directory con tutto il suo contenuto, ricorsivamente. Esempi C:\>DELTREE C:\TEMP\CIAO Elimina la directory ‘C:\TEMP\CIAO\’ assieme a tutto il suo contenuto. Dos: dischi, file system, directory e file 4090 362.8.5 FIND FIND [opzioni] "stringa " [file] Il comando ‘FIND’ è uno dei più complessi nei sistemi Dos. Serve per fare una ricerca di una stringa in uno o più file, in base a quanto indicato nell’ultimo argomento, oppure all’interno dello standard input. Il risultato normale della ricerca è l’emissione delle righe che contengono la stringa cercata, assieme all’indicazione del file a cui appartengono. Alcune opzioni /V La ricerca avviene per le righe che non contengono la stringa cercata. /C Mostra solo il totale delle righe che contengono la stringa cercata. /N Mostra il numero di ogni riga che contiene la stringa cercata. /I Ignora la differenza tra maiuscole e minuscole per il confronto con la stringa di ricerca. In alcune edizioni del Dos, questa modalità di funzionamento è predefinita. Esempi C:\>FIND "ciao" *.* Cerca la stringa ‘ciao’ in tutti i file della directory corrente. C:\>FIND "ciao" < MIO.TXT Cerca la stringa ‘ciao’ nel file ‘MIO.TXT’ che viene fornito attraverso lo standard input. 362.8.6 MOVE MOVE file_origine directory_destinazione MOVE directory_origine directory_destinazione Il comando ‘MOVE’ consente di spostare file o directory in altre collocazioni. In generale, ‘MOVE’ si occupa di spostare e non di rinominare i file, che invece è una funzione del comando ‘REN’. Il comando ‘MOVE’ è ambiguo e si comporta in maniera differente da una realizzazione all’altra dei sistemi Dos. In generale bisogna considerare che la destinazione può esistere o meno, implicando dei comportamenti differenti da valutare. Esempi C:\>MOVE C:\CIAO\*.* C:\MIA Sposta i file e le directory contenute in ‘C:\CIAO\’ nella directory ‘C:\MIA\’. Se la directory di destinazione non c’è, questa dovrebbe essere creata automaticamente, ma la cosa va verificata. Dos: dischi, file system, directory e file 4091 362.8.7 TREE TREE [directory] Il comando ‘TREE’ consente di visualizzare la struttura della directory corrente, oppure di un’altra directory indicata come argomento. Esempi C:\>TREE C:\CIAO Mostra la struttura della directory ‘C:\CIAO\’. 362.8.8 COMP e FC [opzioni] [opzioni] COMP file_1 file_2 FC file_1 file_2 I comandi ‘COMP’ e ‘FC’ permettono di verificare se due file sono identici, oppure no. Non sono molto facili da utilizzare, specialmente il primo; probabilmente vale la pena di sapere che ci sono, senza poi pretendere di sfruttare tutte le loro possibilità. ‘FC’ assomiglia molto vagamente a un comando ‘diff’ di Unix, dal momento che di fronte a file di testo cerca di comprendere quale cambiamento è stato fatto. In questo senso, è probabile che ‘FC’ sia il più utile tra questi due. Appunti di informatica libera 2003.01.01 --- Copyright © 2000-2003 Daniele Giacomini -- daniele @ swlibero.org Capitolo 363 Dos: configurazione Nel Dos è un po’ difficile scindere i concetti di configurazione e script, perché per configurare il sistema, occorre predisporre degli script. Si tratta dei file ‘\CONFIG.SYS’ e ‘\AUTOEXEC.BAT’, collocati nell’unità di avvio. Questo fatto è già stato accennato nel capitolo introduttivo; in questo si vuole approfondire un po’ la cosa. 363.1 CONFIG.SYS Il file ‘CONFIG.SYS’, collocato nella directory radice dell’unità di avvio, è uno script speciale avviato dal kernel prima dell’interprete dei comandi. In linea di massima, si tratta di una sequenza di direttive che occupano ognuna una riga; alcune versioni recenti del Dos consentono di suddividere le direttive in sezioni da scegliere in base a un menù iniziale. Le direttive di ‘CONFIG.SYS’ hanno la forma seguente: nome =valore In pratica, si assegna una stringa (senza delimitatori espliciti) a un nome che ha un significato particolare. In questo file, vengono ignorate le righe vuote, quelle bianche e quelle che iniziano con la parola chiave ‘REM’: REM annotazione È importante osservare che i nomi delle direttive non fanno differenza tra lettere maiuscole e minuscole. In generale, questo vale anche per le stringhe che vengono assegnate a questi nomi. 363.1.1 BREAK { | } BREAK= ON OFF Teoricamente, questa istruzione consente di attivare o di disattivare la funzionalità abbinata alla combinazione di tasti [ Ctrl+c ]. In condizioni normali, quando si assegna la parola chiave ‘ON’, si attiva il funzionamento della combinazione [ Ctrl+c ]. 363.1.2 BUFFERS BUFFERS=n_buffer [,n_buffer_secondari ] Questa istruzione consente di definire la quantità di memoria tampone per gli accessi ai dischi. Si assegnano uno o due valori numerici, separati da una virgola. Il primo valore va da 1 a 99 ed esprime il numero di aree da usare come memoria tampone; il secondo valore, facoltativo, indica delle memorie tampone secondarie, con valori che vanno da uno a otto. 4092 Dos: configurazione 4093 363.1.3 COUNTRY [[ COUNTRY=n_codice_paese , n_codifica ][,file_informazioni_nazionali ]] Questa istruzione, attraverso quanto contenuto in un file che tradizionalmente si chiama ‘COUNTRY.SYS’, permette di configurare il sistema in base alla nazionalità. Per la precisione, si può specificare un codice riferito alla nazionalità, attraverso il quale si ottiene una forma particolare per le date e gli orari, con l’aggiunta eventuale di un altro codice che specifica la codifica dei caratteri prescelta (codepage). La tabella 363.1 riepiloga questi codici che fanno riferimento tradizionalmente anche a paesi che non esistono più. Si può osservare che la stringa assegnata alla direttiva ‘COUNTRY’ può contenere l’indicazione di un file (con il percorso, completo di unità o meno). Questo file è quello che contiene poi le indicazioni relative alla nazionalità prescelta; come già accennato, di solito si tratta del file ‘COUNTRY.SYS’. Tabella 363.1. Codici di nazionalità. Località USA Canada francese America latina Russia Olanda Belgio Francia Spagna Ungheria Jugoslavia Italia Svizzera Cecoslovacchia Regno unito Danimarca Svezia Norvegia Polonia Germania Brasile Australia Giappone Corea Cina Turchia Asia (inglese) Portogallo Islanda Finlandia Codice di nazionalità 001 002 003 007 031 032 033 034 036 038 039 041 042 044 045 046 047 048 049 055 061 081 082 088 090 099 351 354 358 Codifiche utili 437, 850 863, 850 850, 437 866, 437 850, 437 850, 437 850, 437 850, 437 850, 852 850, 852 850, 437 850, 437 850, 852 850, 437 850, 865 850, 437 850, 865 850, 852 850, 437 850, 860 850, 437 932, 437, 850, 942 934, 437, 850, 944 938, 437, 850, 948 857, 850 850, 437 850, 860 850, 861 850, 437 Esempi COUNTRY=039,850,C:\DOS\COUNTRY.SYS Predispone l’impostazione nazionale per l’Italia, utilizzando la codifica 850, che ha il vantaggio di essere quella più comune dei paesi che usano l’alfabeto latino. Dos: configurazione 4094 363.1.4 DEVICE, DEVICEHIGH [opzioni] DEVICEHIGH=programma_di_gestione_dispositivo [opzioni] DEVICE=programma_di_gestione_dispositivo Si tratta di un modo per avviare un programma speciale che ha lo scopo di rimanere residente in memoria. In generale, tali programmi servono per la gestione di qualche dispositivo, indispensabile prima di avviare l’interprete dei comandi. La differenza tra le due direttive sta nel fatto che la seconda cerca di caricare il programma nella memoria «alta». Le opzioni riguardano il programma. Esempi DEVICE=C:\MOUSE\MOUSE.SYS /2 Avvia il programma ‘MOUSE.SYS’ che presumibilmente gestisce il mouse (l’opzione ‘/2’ serve probabilmente a utilizzare il mouse collegato alla seconda porta seriale). 363.1.5 DOS { | }[,{UMB|NOUMB}] DOS=[{HIGH|LOW},]{UMB|NOUMB} DOS= HIGH LOW Questa istruzione richiede al kernel di allocarsi nella memoria convenzionale, ‘LOW’, o in quella alta, ‘HIGH’. La parola chiave ‘UMB’ richiede di mantenere un collegamento tra la UMB e la memoria convenzionale; la parola chiave ‘NOUMB’ fa sì che questo collegamento non abbia luogo. 363.1.6 DRIVEPARM [ ] DRIVEPARM= opzioni Si tratta di una direttiva attraverso cui si possono definire i parametri relativi ai dispositivi a blocchi, per la precisione si tratta solo di dischi, se questo può essere necessario. Le opzioni assomigliano a quelle dei programmi di servizio, iniziando con una barra obliqua normale: ‘/x ...’. Alcune opzioni /d:n_dispositivo_fisico Consente di indicare il dispositivo attraverso un numero, da 0 a 255. Lo zero corrisponde alla prima unità a dischetti. /c Se si utilizza questa opzione, si intende che l’unità fisica è in grado di sapere se il disco è inserito o meno. /f:n_formato Stabilisce il formato del dispositivo fisico; in pratica, fissa la geometria: • 0 dischetto 160 Kibyte, 180 Kibyte, 320 Kibyte, 360 Kibyte • 1 dischetto 1200 Kibyte • 2 dischetto 720 Kibyte Dos: configurazione 4095 • 5 disco fisso • 6 nastro • 7 dischetto 1440 Kibyte • 9 dischetto 2880 Kibyte /h:n_testine Definisce il numero di testine. /i Indica che si tratta di un dischetto da 3,5 pollici. /n Si tratta di un disco fisso. /s:n_settori Definisce il numero di settori per traccia. /t:n_cilindri Definisce il numero dei cilindri (in altri termini: il numero di tracce per faccia). 363.1.7 FCBS FCBS=n_blocchi Permette di definire il numero di blocchi di controllo dei file (file control block). Il valore va da 1 a 255, mentre il valore normale è di quattro blocchi. 363.1.8 FILES FILES=n_blocchi Permette di indicare il numero massimo di file aperti. Il numero che può essere assegnato va da 8 a 255. Il valore predefinito dovrebbe essere di otto file. 363.1.9 INSTALL INSTALL=programma [opzioni] Si tratta di un’istruzione con la quale si può avviare preventivamente un programma (che dovrebbe essere residente in memoria), prima dell’avvio dell’interprete dei comandi. In questo caso, a differenza della direttiva ‘DEVICE’, o ‘DEVICEHIGH’, si tratta di un programma normale. Le opzioni riguardano il programma. 363.1.10 LASTDRIVE LASTDRIVE=lettera_unità_finale Consente di specificare l’ultima lettera di unità che può essere richiesta. Questo consente di risparmiare risorse, se si è consapevoli del fatto che non servono lettere oltre un certo punto. La lettera in questione può essere indifferentemente maiuscola o minuscola, senza che ciò possa fare differenza. Dos: configurazione 4096 363.1.11 SHELL SHELL=programma [opzioni] Permette di indicare esplicitamente il programma da avviare alla fine della procedura di avvio del kernel. In generale si tratta dell’interprete dei comandi. Questa direttiva può consentire di avviare un interprete alternativo a quello normale, oppure permette di avviarlo da una collocazione insolita; inoltre permette di dare al programma in questione delle opzioni particolari. Esempi SHELL=C:\DOS\COMMAND.COM Avvia il programma ‘COMMAND.COM’ che si trova nella directory ‘C:\DOS\’. 363.1.12 STACK [ STACK=n_livelli ,dimensione_in_byte ] Con questa istruzione è possibile fissare la dimensione dello stack, utilizzando valori da 8 a 64, oltre allo zero. Il valore dopo la virgola indica la dimensione in byte di ogni livello dello stack. In questo caso i valori vanno da 32 a 512. 363.2 AUTOEXEC.BAT Il file ‘AUTOEXEC.BAT’ collocato nella directory radice dell’unità di avvio, è inteso essere uno script che viene eseguito dall’interprete dei comandi, ‘COMMAND.COM’, dopo l’avvio del sistema. Questo script viene realizzato normalmente in modo sequenziale, senza strutture di controllo. In generale è importante per due cose: impostare alcune variabili di ambiente fondamentali, per esempio ‘PATH’; avviare dei programmi che poi restano residenti in memoria, quando questo non si ottiene già attraverso il file ‘\CONFIG.SYS’. 363.3 Comandi ridondanti Anche nel Dos è molto importante l’uso delle variabili di ambiente. È già stato mostrato il comando ‘SET’, attraverso il quale si impostano o si annullano le variabili di ambiente: SET nome_variabile =stringa_assegnata Alcune variabili hanno un’importanza particolare, per cui esiste un comando interno apposito (dell’interprete dei comandi), che serve a inizializzarle senza nemmeno l’uso del comando ‘SET’. • PROMPT stringa_di_invito Il comando interno ‘PROMPT’ rappresenta un modo alternativo per impostare la variabile di ambiente con lo stesso nome. Se si usa il comando senza l’argomento, si ripristina l’invito predefinito. • PATH [percorsi_degli_eseguibili] Il comando interno ‘PATH’ rappresenta un modo alternativo per impostare la variabile di ambiente con lo stesso nome. Se non si indica l’argomento, si ottiene la visualizzazione dell’elenco dei percorsi attivo. Dos: configurazione 4097 Esistono altri comandi particolari che si sovrappongono alle istruzioni del file ‘CONFIG.SYS’. • BREAK [ON|OFF] Abilita o disabilita la funzionalità abbinata alla combinazione di tasti [ Ctrl+c ]. Utilizzando il comando senza argomento, si ottiene la visualizzazione dello stato attuale. 363.4 Localizzazione La localizzazione del Dos si riduce alla configurazione della mappa della tastiera e alla definizione dell’insieme di caratteri. L’insieme di caratteri dipende dalla scelta della nazionalità, fatta nel file ‘CONFIG.SYS’, attraverso la direttiva ‘COUNTRY’. Nelle sezioni seguenti vengono mostrati alcuni comandi utili per le impostazioni che riguardano la localizzazione. 363.4.1 CHCP CHCP [n_codifica] Si tratta di un comando interno dell’interprete dei comandi che interviene nella definizione della codifica utilizzata. In pratica, se si utilizza senza argomenti, mostra il numero della codifica attiva; se si indica un numero come argomento, cambia la codifica attiva, purché questa sia una di quelle ammissibili in base alla nazionalità stabilita con la direttiva ‘COUNTRY’ nel file di configurazione ‘CONFIG.SYS’. Esempi C:\>CHCP 850 Fa in modo che sia attivata la codifica corrispondente al numero 850. 363.4.2 KEYB KEYB [sigla_nazionale[,[n_codifica][,file_informazioni_tastiere ]]] ‘KEYB’ è un comando esterno che consente di cambiare la configurazione della tastiera secondo alcuni modelli di nazionalità predefiniti. La sigla nazionale è un codice di due lettere che, assieme alla nazionalità, dovrebbe indicare anche la lingua utilizzata. La tabella 363.2 elenca queste sigle. Tabella 363.2. Sigle nazionali-linguistiche per l’impostazione della mappa della tastiera. Sigla US FR GR IT SP UK PO SG SF DK Corrispondenza USA (predefinito) Francia Germania Italia Spagna Gran Bretagna Portogallo Svizzera tedesca Svizzera francese Danimarca Dos: configurazione 4098 Sigla BE NL NO LA SV SU CF Corrispondenza Belgio Olanda (Nederland) Norvegia America latina Svezia Finlandia (Suomi) Canada francese Esempi C:\>KEYB Mostra la configurazione attuale. C:\>KEYB IT Predispone la mappa dei tasti per la disposizione italiana. C:\>KEYB IT,850,C:\DOS\KEYBOARD.SYS Predispone la mappa dei tasti per la disposizione italiana, specificando l’uso della codifica 850 e del file ‘C:\DOS\KEYBOARD.SYS’ per trovare le impostazioni standard delle tastiere. 363.4.3 GRAFTABL GRAFTABL [n_codifica] GRAFTABL /STATUS ‘GRAFTABL’ è un comando esterno che consente di cambiare la codifica per i caratteri visualizzati sullo schermo. L’opzione ‘/STATUS’ permette di conoscere la situazione attuale, mentre l’indicazione di un numero di codifica cambia l’impostazione. Esempi C:\>GRAFTABL 850 Imposta l’uso della codifica 850. 363.5 Orologio Il Dos consente di accedere all’orologio dell’elaboratore, per leggere la data e l’ora, o per cambiare tali informazioni. In generale, il Dos non prevede la gestione di un orologio hardware allineato al tempo universale; pertanto, l’orologio hardware deve corrispondere necessariamente all’ora locale, lasciando all’utente il problema legato alle variazioni dell’ora estiva. I comandi per accedere all’orologio sono ‘DATE’ e ‘TIME’: [data] TIME [orario] DATE Se non si indica la data o l’orario, viene mostrato quello attuale e viene richiesto all’utente di modificarlo o di confermarlo. Il modo in cui va scritta da data o l’ora, dipende dalla localizzazione. Per conoscere quello giusto, basta osservare in che modo vengono visualizzate tali informazioni. Appunti di informatica libera 2003.01.01 --- Copyright © 2000-2003 Daniele Giacomini -- daniele @ swlibero.org Capitolo 364 Dos: script dell’interprete dei comandi Uno script dell’interprete dei comandi, conosciuto solitamente con il nome di file batch, potrebbe essere definito come un file di testo normale in cui può essere indicato un elenco di comandi da eseguire. Tuttavia, questi script consentono l’uso anche di strutture di controllo elementari, per cui si possono realizzare dei programmi molto semplici, senza troppe pretese. È interessante osservare che questi script vengono individuati solo attraverso l’estensione che ha il nome: ‘.BAT’. Inoltre, non esiste la necessità di renderli «eseguibili» come si fa nei sistemi Unix. 364.1 Parametri, variabili ed espansione Gli script dell’interprete dei comandi hanno accesso agli argomenti che vengono loro forniti. Si possono gestire solo nove di questi argomenti alla volta, attraverso i parametri posizionali relativi, da ‘%1’ a ‘%9’. Come avviene nelle shell Unix, è disponibile il comando interno ‘SHIFT’ per fare scorrere in avanti gli argomenti nei parametri disponibili. Bisogna ricordare che in Dos i caratteri jolly non vengono espansi dalla shell, per cui la limitazione a soli nove parametri posizionali, non dovrebbe costituire un problema. Nell’ambito di uno script possono essere dichiarate e utilizzate delle variabili di ambiente. È già stato mostrato in precedenza l’uso del comando ‘SET’ per impostare o eliminare le variabili di ambiente. Per fare riferimento al contenuto di una variabile, si usa la notazione seguente: %nome_variabile % L’esempio seguente rappresenta il caso tipico di estensione di un percorso di ricerca degli eseguibili, quando si ritiene che la variabile ‘PATH’ sia già stata usata: SET PATH=%PATH%;C:\PIPPO 364.2 Chiamate di altri script Tradizionalmente, il Dos ha un baco molto grave, ormai divenuto una caratteristica fondamentale, riguardante l’avvio di script all’interno di altri script. In generale, quando si chiama un programma che in realtà corrisponde a uno script, al termine di questo non riprende l’esecuzione di quello chiamante. Per ottenere la ripresa dell’interpretazione dello script di partenza occorre usare il comando speciale ‘CALL’. CALL nome_script [argomenti_dello_script] 364.3 Strutture di controllo Le strutture di controllo per la programmazione attraverso gli script dell’interprete dei comandi sono molto limitate. È disponibile una struttura condizionale semplificata e un ciclo di scansione di file, che vengono descritti brevemente. 4099 Dos: script dell’interprete dei comandi 4100 364.3.1 IF [NOT] IF [NOT] IF [NOT] IF ERRORLEVEL valore_di_uscita_ultimo_comando comando stringa_1 ==stringa_2 comando EXIST file comando La struttura condizionale degli script dell’interprete dei comandi Dos è in pratica un comando interno dello stesso interprete. Come si può vedere dagli schemi sintattici, viene fornita una condizione che può essere invertita con la parola chiave ‘NOT’ e il risultato è solo l’esecuzione di un altro comando se la condizione risulta vera. Nel primo caso, la condizione si riferisce alla verifica del valore di uscita dell’ultimo comando eseguito. La condizione si verifica se il numero indicato è inferiore o uguale al valore restituito effettivamente da tale comando; nel secondo, la condizione si verifica se le due stringhe (non delimitate) sono identiche; nel terzo si verifica la condizione se il file indicato esiste effettivamente. Esempi IF ERRORLEVEL 1 GOTO :errore Se il comando precedente ha restituito un valore maggiore o uguale a uno, salta all’etichetta ‘:errore’. IF %1==ciao ECHO L’argomento è corretto In questo caso, se l’espansione del parametro ‘%1’, corrispondente al primo argomento ricevuto all’avvio, si traduce nella stringa ‘ciao’, viene emesso un messaggio per mezzo del comando ‘ECHO’. IF %1x==x ECHO L’argomento è mancante Quello che si vede è il trucco necessario per poter verificare se un parametro contiene la stringa nulla: si aggiunge una lettera, in questo caso una «x», verificando che la corrispondenza avvenga solo con la stessa lettera. IF NOT EXIST LETTERA.TXT ECHO Scrivi! > LETTERA.TXT Qui, se non esiste il file ‘LETTERA.TXT’ nella directory corrente, questo file viene creato attraverso il comando ‘ECHO’ che invia il suo standard output verso un file con lo stesso nome. 364.3.2 FOR FOR [%]%x IN (nome ...) DO comando [argomenti_del_comando ] Si tratta di un comando interno che svolge un ciclo di scansione di un gruppo di nomi, generalmente file, attraverso il quale viene creato un parametro variabile speciale, il cui nome si compone di una sola lettera, a cui viene assegnato a ogni ciclo uno dei nomi contenuti tra parentesi tonde. A ogni ciclo viene eseguito il comando, che a sua volta può fare uso del parametro.1 Quando viene usato all’interno di uno script dell’interprete dei comandi, il parametro viene indicato con due simboli di percentuale (‘%%x ’); al contrario, se il comando viene impartito dalla riga di comando, se ne usa uno solo. 1 Questo parametro assomiglia a una variabile di ambiente, ma non si comporta allo stesso modo. Si tratta di una particolarità del comando ‘FOR’. Dos: script dell’interprete dei comandi 4101 Esempi FOR %A IN (uno due tre) DO ECHO %A In questo modo, si ottiene la visualizzazione delle parole ‘uno’, ‘due’ e ‘tre’. In pratica, è come se fosse stato fatto: ECHO uno ECHO due ECHO tre Volendo fare la stessa cosa dalla riga di comando, è necessario il raddoppio del simbolo ‘%’: C:\>FOR %%A IN (uno due tre) DO ECHO %%A FOR %A IN (*.TMP *.BAD) DO DEL %A Cancella, uno a uno, tutti i file che terminano con le estensioni ‘.TMP’ e ‘.BAD’. 364.3.3 GOTO GOTO etichetta Gli script dell’interprete dei comandi dispongono dell’istruzione di salto incondizionato, non avendo di meglio. Anche questa istruzione può essere presa come un comando interno dell’interprete, con la differenza che non c’è modo di utilizzarlo al di fuori di uno script. Nel corso di uno script del genere, possono apparire delle righe che contengono solo un’etichetta, nella forma: :nome_etichetta La posizione corrispondente a queste etichette può essere raggiunta con il comando ‘GOTO’, che può fare riferimento solo al nome dell’etichetta, oppure a tutta l’etichetta, includendo anche i due punti. Esempi IF EXIST LETTERA.TXT GOTO riprendi ECHO Il file LETTERA.TXT è assente :riprendi In questo esempio, se il file ‘LETTERA.TXT’ esiste, si salta all’etichetta ‘:riprendi’; altrimenti si esegue il comando ‘ECHO’. IF EXIST LETTERA.TXT GOTO :riprendi ECHO Il file LETTERA.TXT è assente :riprendi Esattamente come nell’esempio precedente, con la differenza che il comando ‘GOTO’ indica l’etichetta con i suoi due punti iniziali. 364.3.4 Emulazione di un ciclo iterativo Dal momento che non è disponibile una struttura di controllo per il ciclo iterativo, questo può essere ottenuto solo attraverso l’uso del comando ‘GOTO’. Vale la pena di mostrare in che modo si può ottenere tale risultato. :etichetta_di_ingresso IF condizione GOTO :etichetta_di_uscita ... ... ... GOTO etichetta_di_ingresso :etichetta_di_uscita Dos: script dell’interprete dei comandi 4102 :etichetta_di_ingresso ... ... ... IF condizione GOTO :etichetta_di_ingresso :etichetta_di_uscita I due modelli sintattici mostrano due esempi di cicli iterativi. Nel primo caso si verifica una condizione, in base alla quale si decide se proseguire o se terminare il ciclo; nel secondo si esegue una volta il ciclo e quindi si verifica una condizione per decidere se ripeterlo o se uscire. 364.4 Comandi utili negli script Alcuni comandi sono particolarmente utili all’interno di script dell’interprete dei comandi. Vengono descritti brevemente nelle sezioni seguenti. 364.4.1 REM REM commento I commenti negli script dell’interprete dei comandi si indicano attraverso un comando apposito: ‘REM’. Il funzionamento è evidente: tutto quello che segue il comando, fino alla fine della riga, viene ignorato. Alcune edizioni del Dos hanno introdotto anche l’uso del punto e virgola, come simbolo per indicare l’inizio di un commento. Segue un esempio tipico di utilizzo di questo comando: REM REM (c) 2000 Pinco pallino REM 364.4.2 ECHO ECHO [ON|OFF] ECHO stringa Il comando interno ‘ECHO’ ha un significato duplice: da una parte consente di visualizzare un testo; dall’altra controlla la visualizzazione dei comandi contenuti in uno script. Infatti, si distingue il fatto che l’eco dei comandi sia attivo o meno. utilizzando il comando ‘ECHO’ senza argomenti, si ottiene l’informazione sul suo stato di attivazione. Di solito si disattiva l’eco dei comandi negli script. Per disattivare l’eco di un comando particolare, senza disattivare l’eco in generale, basta inserire inizialmente il simbolo ‘@’. Esempi @ECHO OFF Disattiva l’eco dei comandi, facendo in modo che anche questo comando non venga visualizzato (si usa per questo il simbolo ‘@’). ECHO Premi un tasto per continuare Mostra un messaggio per spiegare come comportarsi. Dos: script dell’interprete dei comandi 4103 364.4.3 PAUSE PAUSE Il comando interno ‘PAUSE’ sospende l’esecuzione di uno script in attesa della pressione di un tasto. Il comando emette attraverso lo standard output un messaggio di avvertimento in tal senso. Di solito, per evitare di vedere tale messaggio, si ridirige lo standard output in un file nullo. Esempi ECHO Premere un tasto per proseguire PAUSE > C:\NULL Prima mostra un messaggio in cui si avverte che per proseguire occorre premere un tasto, quindi si usa il comando ‘PAUSE’ che sospende l’esecuzione dello script, senza però mostrare altri messaggi. 364.4.4 CLS CLS Il comando interno ‘CLS’ ripulisce lo schermo. Si utilizza senza argomenti. 364.4.5 CHOICE CHOICE [opzioni] [testo_di_invito] Il comando ‘CHOICE’ serve a presentare una richiesta per l’inserimento di una lettera, tra un elenco determinato. La pressione del tasto corrispondente alla lettera scelta, da parte dell’utilizzatore, provoca la conclusione del funzionamento di ‘CHOICE’ che restituisce un valore corrispondente alla scelta: zero per la prima lettera, uno per la seconda,... Si osservi che l’ultimo argomento rappresenta un messaggio che serve all’utente per comprendere il senso della scelta che sta facendo. Alcune opzioni [ ] /C:lettera lettera ... Permette di fissare l’elenco di lettere che possono essere usate nella risposta. Se non si indica questa opzione, la scelta sarà solo tra ‘y’ e ‘n’. /N Questa opzione fa in modo di escludere la visualizzazione delle lettere che possono essere scelte. In questo modo si fa affidamento esclusivamente sul testo indicato come ultimo argomento. /S Distingue tra maiuscole e minuscole per quanto riguarda le lettere tra cui scegliere. Esempi CHOICE /C:abcdef Inserisci una lettera IF ERRORLEVEL 5 GOTO :f IF ERRORLEVEL 4 GOTO :e IF ERRORLEVEL 3 GOTO :d IF ERRORLEVEL 2 GOTO :c IF ERRORLEVEL 1 GOTO :b Dos: script dell’interprete dei comandi 4104 IF ERRORLEVEL 0 GOTO :a ... In base alla scelta di una lettera da «a» a «f», salta a un punto differente dello script. Si osservi che non sarebbe possibile eseguire l’analisi secondo una sequenza differente, perché ‘IF ERRORLEVEL’ prende in considerazione tutti i valori di uscita maggiori o uguali a quanto indicato nella condizione. Appunti di informatica libera 2003.01.01 --- Copyright © 2000-2003 Daniele Giacomini -- daniele @ swlibero.org Capitolo 365 Dos: gestione della memoria centrale Quando è nato il Dos non si prevedeva l’uso di memoria centrale oltre il singolo mebibyte (1 Mibyte). In base a questa considerazione veniva articolata l’architettura hardware degli elaboratori «XT» e poi «AT», dove si prevedeva l’uso di un massimo di 640 Kibyte di memoria centrale, riservando la parte successiva, fino alla fine di 1 Mibyte, per la memoria video e altri dispositivi fisici. In questo senso, il Dos tradizionale può operare con un massimo di 640 Kibyte di memoria centrale; per sfruttarne di più occorrono degli accorgimenti non facili da applicare. 365.1 Gestione particolare Per sfruttare la memoria oltre il primo mebibyte, si fa uso normalmente di due programmi, avviati attraverso ‘CONFIG.SYS’, prima ancora dell’interprete di comandi. Si tratta di ‘HIMEM.SYS’ e di ‘EMM386.EXE’. In generale, le cose si fanno nel modo seguente: DEVICE=C:\DOS\HIMEM.SYS DEVICE=C:\DOS\EMM386.EXE Il primo dei due programmi può essere utilizzato a partire da architetture i286, mentre il secondo si può inserire solo a partire da architetture i386. ‘HIMEM.SYS’ è in grado di utilizzare solo una piccola parte di memoria aggiuntiva, mentre ‘EMM386.EXE’ permette teoricamente di sfruttare tutto il resto. In generale, è molto difficile la gestione ottimale della memoria centrale, perché le applicazioni si comportano in maniera differente. Di solito si possono solo fare dei tentativi. 365.2 Comandi appositi Per sfruttare la memoria centrale che supera la soglia convenzionale, sono disponibili alcuni comandi specifici. In generale, si comincia dalla configurazione con il file ‘CONFIG.SYS’: dopo l’attivazione dei gestori speciali della memoria, è possibile indicare di collocare parte dell’interprete dei comandi e dello spazio richiesto dai programmi residenti in memoria, oltre il limite della memoria convenzionale: DOS=HIGH,UMB In seguito, sempre nell’ambito del file ‘CONFIG.SYS’, si può richiedere esplicitamente l’avvio di programmi nella memoria alta attraverso la direttiva ‘DEVICEHIGH’, come si vede nell’esempio seguente: DEVICEHIGH=C:\MOUSE\MOUSE.SYS /2 Per quanto riguarda i programmi avviati attraverso l’interprete dei comandi, è disponibile il comando ‘LH’, ovvero ‘LOADHIGH’: [argomenti_del_programma ] programma [argomenti_del_programma ] LH programma LOADHIGH Per esempio, si potrebbe tentare di avviare in questo modo il programma di gestione della tastiera: C:\>LH KEYB IT 4105 Dos: gestione della memoria centrale 4106 365.3 Verifica Il Dos offre un solo programma molto semplice per la verifica dell’utilizzo della memoria: ‘MEM’. MEM [opzioni] Se ‘MEM’ viene usato senza opzioni, visualizza brevemente la quantità di memoria utilizzata rispetto al totale disponibile. È interessante l’opzione ‘/CLASSIFY’, attraverso la quale è possibile distinguere l’utilizzo della memoria da parte dei programmi residenti; inoltre è interessante l’opzione ‘/FREE’, con cui si hanno informazioni dettagliate sulla memoria libera. Le opzioni disponibili del comando ‘MEM’ variano molto da una realizzazione all’altra. In generale conviene verificare prima di utilizzarlo, per conoscere le possibilità effettive. Appunti di informatica libera 2003.01.01 --- Copyright © 2000-2003 Daniele Giacomini -- daniele @ swlibero.org Capitolo 366 FreeDOS FreeDOS è il nome di un progetto per la realizzazione di un sistema operativo libero compatibile con il Dos. Il Dos, per quanto limitato, ha delle caratteristiche che lo possono rendere ancora interessante per elaboratori con architettura i86 particolarmente poveri di risorse, come nel caso dei sistemi cosiddetti embedded. 366.1 Installazione L’installazione della distribuzione standard di FreeDOS è abbastanza semplice. Si parte da un dischetto di avvio, con il quale si predispone la partizione e la si inizializza, quindi si prosegue con il programma di installazione che chiede l’inserimento dei dischetti successivi. La riproduzione del dischetto di avvio a partire dalla sua immagine avviene come al solito attraverso il programma ‘RAWRITE.EXE’, oppure per mezzo di un sistema Unix nei modi già mostrati per GNU/Linux e altri sistemi simili. C:\>RAWRITE FULL.BIN A: L’esempio mostra l’uso di ‘RAWRITE.EXE’ per ottenere un dischetto dall’immagine rappresentata dal file ‘FULL.BIN’. La distribuzione standard di FreeDOS si compone di un file-immagine del dischetto di avvio, che potrebbe chiamarsi ‘FULL.BIN’, e da una serie di file con estensione ‘.ZIP’ che servono per ottenere i dischetti successivi. Ognuno di questi file compressi rappresenta il contenuto di un dischetto, che quindi deve essere prima estratto: C:\>A: A:\>UNZIP C:\TMP\BASE1.ZIP L’esempio mostra in breve il procedimento: ci si sposta nell’unità ‘A:’ e da lì si estrae il file compresso che probabilmente si trova da qualche parte nel disco fisso. Questi file compressi rappresentano una raccolta di applicativi e hanno una struttura particolare che viene descritta nel seguito. • nome_raccolta .1 L’archivio compresso deve contenere un file che rappresenta il nome della raccolta, con un’estensione numerica. La raccolta potrebbe essere suddivisa in più archivi ed è per questo che si usa l’estensione numerica, che indica il numero di sequenza dell’archivio nell’ambito della raccolta. Il file contiene l’elenco dei pacchetti contenuti, con l’indicazione dell’opzione di installazione predefinita o meno. Si osservi l’estratto seguente (la lettera «Y» rappresenta la conferma all’installazione predefinita): asgn14x: Y attr063x: Y bwb210x: Y choic20x: Y • nome_raccolta .END Si tratta di un file vuoto, che rappresenta la conclusione della raccolta, nel senso che non ci sono altri dischetti ulteriori. 4107 FreeDOS 4108 • nome_pacchetto .LSM Si tratta di un file che descrive un pacchetto applicativo. Quello che segue è l’esempio del contenuto del file ‘DELTR10X.LSM’: Begin3 Title: Version: Entered-date: Description: Keywords: Author: Maintained-by: Primary-site: Alternate-site: Original-site: Platforms: Copying-policy: End • deltree 1.02b 27 Jul 1999 Delete a directory and all directories under it freedos delete [email protected] [email protected] http://www.highfiber.com/~raster/freeware.htm www.freedos.org http://www.highfiber.com/~raster/freeware.htm dos GPL nome_pacchetto .ZIP Si tratta dell’archivio compresso che contiene i file dell’applicativo. In base alla struttura standard di FreeDOS, potrebbe distribuirsi nelle directory ‘BIN\’, ‘DOC\’ e ‘HELP\’. Dopo aver preparato i dischetti, si può procedere con l’avvio del sistema attraverso il dischetto di avvio; quindi si passa a predisporre la partizione: A:\>FDISK Purtroppo, il kernel di FreeDOS non è in grado di gestire partizioni più grandi di 512 Mibyte, per cui occorre tenerne conto durante l’uso di ‘FDISK’. Dopo aver preparato la partizione la si inizializza: A:\>FORMAT C: /U Successivamente si trasferisce il sistema, con il comando ‘SYS’: A:\>SYS C: Infine si avvia il programma di installazione che provvederà a chiedere la sostituzione dei dischetti: A:\>INSTALL 366.2 Impostazione e configurazione Da quanto è stato descritto sull’installazione di FreeDOS si intende che, pur trattandosi di un sistema Dos, si cerca di introdurre qualche buona idea proveniente da Unix. In particolare, è prevista una struttura per la collocazione dei file: • ‘BIN\’ per contenere i file eseguibili; • ‘DOC\’ per contenere la documentazione che si articola in altre sottodirectory successive, come avviene con GNU/Linux • ‘HELP\’ per contenere i file della guida interna relativa. Questa struttura potrebbe essere collocata anche a partire da un punto differente della radice dell’unità, in base alle scelte fatte in fase di installazione. In ogni caso, occorre poi predisporre FreeDOS 4109 coerentemente alcune variabili di ambiente: ‘PAGER’ per indicare il programma da utilizzare per lo scorrimento dei file delle guide; ‘HELPPATH’ per indicare la directory contenente i file delle guide; ‘EMACS’ per indicare la directory contenente i file di Emacs. In condizioni normali, gli applicativi FreeDOS vengono installati a partire dalla directory ‘\FDOS\’, per cui la configurazione si traduce nelle istruzioni seguenti nel file ‘AUTOEXEC.BAT’: SET PAGER=MORE SET HELPPATH=C:\FDOS\HELP SET EMACS=C:\FDOS\EMACS\ In base alla documentazione originale, nel caso della variabile di ambiente ‘EMACS’ deve essere indicata la barra obliqua inversa finale. A seconda della distribuzione di FreeDOS, può darsi che il file ‘CONFIG.SYS’ debba essere sostituito con uno avente un nome differente. Potrebbe trattarsi del file ‘FDCONFIG.SYS’. 366.3 RxDOS RxDOS è un altro progetto analogo a FreeDOS, scritto in maniera indipendente. È provvisto di un proprio interprete dei comandi e non ha ancora un suo sistema di installazione. Per provare il funzionamento di RxDOS ci si può avvalere solo di un dischetto, realizzato nel modo seguente: 1. si inizializza il dischetto in qualche modo, assicurando che alla fine sia disponibile un file system Dos-FAT;1 2. si esegue lo script ‘MAKEBOOT.BAT’, il cui scopo è la predisposizione del settore di avvio nel dischetto; 3. si copiano ordinatamente nel dischetto i file elencati qui sotto. • ‘RXDOSBIO.SYS’ • ‘RXDOS.SYS’ • ‘RXDOSCMD.EXE’ • ‘RXDVDISK.SYS’ • ‘AUTOEXEC.DEF’ • ‘CONFIG.DEF’ 366.4 Riferimenti • FreeDOS <http://www.freedos.org> Appunti di informatica libera 2003.01.01 --- Copyright © 2000-2003 Daniele Giacomini -- daniele @ swlibero.org 1 Il dischetto non deve avere l’etichetta, ovvero non deve avere un nome. Capitolo 367 Progetto GNUish Il progetto «GNUish» è una sorta di derivazione povera del progetto GNU, con lo scopo di rendere disponibile parte del software che compone il sistema GNU anche nei sistemi Dos. Il progetto ha un’importanza molto piccola, ma viene ancora mantenuto. Evidentemente, date le peculiarità dei sistemi Dos, il software che viene adattato non può avere le stesse potenzialità che ha invece in un sistema Unix. I siti principali da cui si può ottenere copia del materiale prodotto dal progetto GNUish sono quelli seguenti, da cui poi si articolano anche una serie di riproduzioni speculari: • <ftp://ftp.simtel.net/pub/simtelnet/gnu/gnuish/> In questo capitolo viene mostrato il funzionamento di alcuni programmi, nell’ambito del sistema Dos, per i quali è il caso di spendere qualche parola. 367.1 Programmi di servizio vari Molti dei programmi di servizio del progetto GNU sono disponibili anche per Dos. Tuttavia, è il caso di osservare alcune particolarità che possono confondere chi è abituato a usare sistemi Dos. La prima cosa da notare è il fatto che i percorsi si possono indicare secondo lo stile Unix, utilizzando barre oblique normali. Per esempio: C:\>MV C:/PRIMO/SECONDO C:/TERZO Diversamente, utilizzando lo stesso comando, ma secondo l’indicazione tipica del Dos, la cosa può funzionare ugualmente, oppure si possono presentare delle segnalazioni di errore. Bisogna tenere presente la possibilità. Un’altra cosa da notare è l’uso dei caratteri jolly, che con questi programmi segue la logica di Unix, dove l’asterisco indica qualunque nome, senza trattare in modo speciale il punto di separazione dell’estensione: C:\>CP C:/PRIMO/SECONDO/* C:/TERZO L’esempio mostra proprio questo fatto: vengono copiati tutti i file contenuti nella directory ‘C:\ PRIMO\SECONDO\’, nella directory ‘C:\TERZO\’. 367.2 Gnuplot Il funzionamento generale di Gnuplot è descritto nel capitolo 331. Per funzionare, questa edizione di Gnuplot richiede due file: ‘GNUPLOT.EXE’ e ‘GNUPLOT.GIH’. Il primo dei due è l’eseguibile in grado di gestire la grafica VGA, mentre il secondo contiene le informazioni della guida interna. Se si vuole accedere alla guida interna, è necessario che il file ‘GNUPLOT.GIH’ si trovi nella directory corrente. Forse è sufficiente utilizzare il comando ‘APPEND’ del Dos per risolvere il problema. 4110 Progetto GNUish 4111 367.3 Spreadsheet Calculator Il funzionamento generale di SC (Spreadsheet Calculator) è descritto nel capitolo 330. La versione per Dos funziona correttamente (è sufficiente disporre dell’eseguibile ‘SC.EXE’), riconoscendo anche l’uso dei tasti freccia, per cui non si è più costretti a utilizzare le lettere ‘h’, ‘j’, ‘k’ e ‘l’. 367.4 Ispell Ispell è descritto in generale nel capitolo 269. Questa edizione di Ispell richiede due file: ‘ISPELL.EXE’ e ‘ISPELL.DIC’. Come si intuisce, il primo è l’eseguibile, mentre il secondo è il file del dizionario. Purtroppo, il file ‘ISPELL.DIC’ non è sostituibile o eliminabile; l’unica cosa che si può fare è predisporre un dizionario personalizzato che si richiama con l’opzione ‘-p’. ISPELL [-d dizionario_standard ] [-p dizionario_personale ] file Quella che si vede è la sintassi essenziale su cui si può contare nell’edizione di Ispell per Dos. Il file del dizionario standard, ‘ISPELL.DIC’, può essere collocato nella stessa directory in cui si trova il file eseguibile; altrimenti si deve usare l’opzione ‘-d’ per indicarlo esplicitamente. Il dizionario personale è un file di testo normale (Dos), che può anche essere creato inizialmente dallo stesso Ispell. L’esempio seguente, mostra il caso in cui si voglia analizzare il file ‘LETTERA.TXT’ attraverso il dizionario standard e il dizionario personale ‘VOCAB.TXT’. Se il file ‘VOCAB.TXT’ non dovesse esistere, verrebbe creato per l’occasione. C:\LETTERE>ISPELL -p VOCAB.TXT LETTERA.TXT 367.5 Perl Perl è un linguaggio di programmazione descritto in generale a partire dal capitolo 295. L’edizione Dos dell’interprete Perl richiede due file: ‘PERL.EXE’ e ‘PERLGLOB.EXE’. È sufficiente che questi siano disponibili nei percorsi degli eseguibili della variabile di ambiente ‘PATH’. Bisogna tenere a mente che si tratta di una versione molto vecchia del linguaggio, per cui alcune novità non saranno disponibili. Inoltre, l’avvio dei programmi può avvenire solo richiamando direttamente l’interprete: C:\ESERCIZI>PERL FATT.PL 5 L’esempio mostra l’avvio del programma Perl contenuto nel file ‘FATT.PL’, che riceve un argomento costituito dal numero cinque. 367.6 Riferimenti • Darrel Hankerson, François Pinard, The GNUish Project <ftp://ftp.simtel.net/pub/simtelnet/gnu/gnuish/gnuish_t.htm> Appunti di informatica libera 2003.01.01 --- Copyright © 2000-2003 Daniele Giacomini -- daniele @ swlibero.org Chapter 368 The valuable DOS Freeware page Links to valuable free Dos programs working on low equipped computers. This material appeared originally at ‘http://www.geocities.com/SiliconValley/ 7737/’, in 1996. Now it is incorporated inside the Italian document ‘‘Appunti di informatica libera’’, and it might be reached at the URI <http://a2.swlibero.org/ the_valuable_dos_freeware_page.html>. Questo materiale è apparso in origine, nel 1996, presso ‘http://www.geocities.com/ SiliconValley/7737/’. Adesso viene incorporato nel documento «Appunti di informatica libera» e può essere raggiunto attraverso l’URI <http://a2.swlibero.org/ the_valuable_dos_freeware_page.html>. L’intento dell’autore è solo quello di continuare a curare un vecchio lavoro che potrebbe essere ancora utile, nonostante si tratti di riferimenti a software in parte libero e in parte solo gratuito, oltre che evidentemente obsoleto. 368.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4112 368.2 OS and GUI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4113 368.3 Utility . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4113 368.4 Network . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4114 368.5 Compilers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4115 368.6 Typesetting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4116 368.7 More Dos software sources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4116 368.8 Search engines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4117 368.1 Introduction The Dos operating system meant much for many people. Today, proprietary Dos-like operating systems seem to be no more developed. In this situation, the only possible future for Dos is the ‘‘free’’ software, and it is not just a matter of money anymore. Unfortunately, ‘‘free’’ is a word with many meanings. Today, this is still the biggest obstacle to the future of the Dos world. There is so much software for Dos, with so many different license agreements. The typical Dos user doesn’t mind to it. But this problem prevents the realization of big serious projects based on it. Today, the Dos world needs philosophy, and the GNU idea is still the right one (<http:// www.gnu.org>). The author of this space would like to list here only ‘‘free software’’ in the sense stated by the Free Software Foundation, but it is impossible, as there isn’t enough good real free software for Dos. The listed software is meant to work on i286 and below. It is attempted to give some kind of classification about the legal condition of the software presented here. The definition used might be outdated, or there might be other wrong assumption. In particular, the definition ‘‘public domain’’ means here, in most cases, that there is the source, but there is no clear license statement. 4112 The valuable DOS Freeware page 4113 Beside the URI links of some FTP services there is an additional ‘‘search link’’ that queries a FTP search engine for the same file. These additional links should be used when there are troubles with the main links. Anyone can link this document anywhere, so, there is no need to ask for it. Anyway, it is better to link to this document using at the file name <http://a2.swlibero.org/the_valuable_dos_freeware_page.html>. In the future, many links may disappear on this page, because of more selective choices concerning software license. 368.2 OS and GUI • FreeDOS 1 <http://www.freedos.org> • FreeGEM 2 <http://www.cableone.net/eathan/gem.htm> 368.3 Utility archive, backup • Gzip - ‘.GZ’ archive compressor and extractor. <ftp://ftp.simtel.net/pub/simtelnet/msdos/ <http://www.alltheweb.com/search?cat=ftp&q=gzip124.zip> 3 compress/gzip124.zip> • TAR 4 - portable TAR - DOS/UNIX backup, compressor, with hardware support. <ftp://ftp.simtel.net/pub/simtelnet/msdos/arcers/tar320g.zip> <http://www.alltheweb.com/ search?cat=ftp&q=tar320g.zip> • Untgz - ‘.TGZ’, ‘.TAR’, ‘.GZ’, ‘.ZIP’ file extractor. <ftp://ftp.simtel.net/pub/simtelnet/ <http://www.alltheweb.com/search?cat=ftp&q=untgz095.zip> 5 msdos/arcers/untgz095.zip> • Info-ZIP 6 - ‘.ZIP’ compatible compression and extraction utility. <http://www.info<ftp://ftp.info-zip.org/pub/infozip/MSDOS/> zip.org/pub/infozip/> - Replacement for Dos Restore, <ftp://ftp.simtel.net/pub/simtelnet/msdos/diskutil/ restaur1.zip> <http://www.alltheweb.com/search?cat=ftp&q=restaur1.zip> • Restaur 7 communication • DosFax 8 - Send a fax using Dos command line <http://www.Adr.de/speicherplatz/cs/dosfax.htm> • Bgfax 9 <http://www.blkbox.com/~bgfax/> - Disk sharing over a serial line, <ftp://ftp.simtel.net/pub/simtelnet/msdos/lan/dosrifs2.zip> <http://www.alltheweb.com/search?cat=ftp&q=dosrifs2.zip> • Rifs 10 directory, file 1 FreeDOS GNU GPL FreeGEM GNU GPL 3 Gzip GNU GPL 4 TAR (Dos) public domain 5 Untgz GNU GPL 6 Info-ZIP free software with special license 7 Restaur cannot be sold for profit 8 DosFax public domain 9 Bgfax promised to become free software 10 Rifs cannot be sold for profit 2 The valuable DOS Freeware page 4114 • WCD 11 - Powerful chdir for Dos and Unix <http://www.xs4all.nl/~waterlan/> disk • Fips 12 - Non-destructive splitting of hard disk partitions <ftp://ftp.simtel.net/pub/simtelnet/ msdos/diskutil/fips15.zip> <http://www.alltheweb.com/search?cat=ftp&q=fips15.zip> • Part 13 - MBR partition manager <http://www.intercom.com/~ranish/> help • NG_clone 14 - Norton Guides clone <ftp://ftp.simtel.net/pub/simtelnet/msdos/txtutl/ngclon11.zip> <http://www.alltheweb.com/search?cat=ftp&q=ngclon11.zip> shell • DC 15 - The Dos Controller - A Norton Commander clone <ftp://ftp.simtel.net/pub/simtelnet/ msdos/fileutil/dc-sk.zip> <http://www.alltheweb.com/search?cat=ftp&q=dc-sk.zip> system • Cmos 16 - Save/Restore extended C/MOS <ftp://ftp.simtel.net/pub/simtelnet/msdos/sysutl/ <http://www.alltheweb.com/search?cat=ftp&q=cmos93cd.zip> cmos93cd.zip> • KGB 17 - Utility to monitor some Dos functions and reporting into a log file <ftp://ftp.simtel.net/pub/simtelnet/msdos/sysutl/kgb104.zip> <http://www.alltheweb.com/ search?cat=ftp&q=kgb104.zip> text • Vim 18 - VI improved, a small text editor that can handle very big files with low RAM <ftp://ftp.simtel.net/pub/simtelnet/msdos/editor/vim53d16.zip> <http://www.alltheweb.com/ search?cat=ftp&q=vim53d16.zip> See also: • Richard L. Green, Free software for Dos <http://www.geocities.com/rlcgreen/softlib1.htm> 368.4 Network packet driver • PC/TCP Packet Driver Collection drivers/pktd11.zip> 11 WCD GNU GPL Fips GNU GPL 13 Part public domain 14 NG_clone public domain 15 DC public domain (no license at all, and no sources) 16 Cmos public domain 17 KGB public domain 18 Vim free software with special license 19 Crynwr packet driver collection GNU GPL 12 19 <ftp://ftp.crynwr.com/drivers/> <ftp://ftp.crynwr.com/ The valuable DOS Freeware page 4115 • WATTCP 20 - TCP/IP library routines <http://www.wattcp.com/> - Dos port of Linux PPP packet driver <ftp://ftp.simtel.net/pub/simtelnet/ msdos/pktdrvr/dosppp05.zip> <http://www.alltheweb.com/search?cat=ftp&q=dosppp05.zip> • DOS PPPD • Comring 22 21 - packet driver emulating ethernet over serial link(s) <http://wiz- ard.ae.krakow.pl/~jb/ComRing/> TCP/IP - some common client application using WATTCP library <http:// www.smashco.com/wattcp/apps.zip> • WATTCP apps 23 • MiniTelnet 24 - TELNET client <http://www.smashco.com/wattcp/mt.zip> • Bobcat 25 - Text based web browser <http://www.fdisk.com/doslynx/bobcat.htm> (derived from DosLynx, <ftp://ftp2.cc.ukans.edu/pub/WWW/DosLynx/>) • PCroute 26 - IP routing program for IBM PC <ftp://ftp.simtel.net/pub/simtelnet/msdos/network/ pcrte224.zip> <http://www.alltheweb.com/search?cat=ftp&q=pcrte224.zip> • PPRD 27 - Turn a dedicated PC (XT/AT) into a LPD server <ftp://ftp.simtel.net/pub/simtelnet/msdos/lan/pprd200.zip> <http://www.alltheweb.com/search?cat=ftp&q=pprd200.zip> • NCSA Telnet 28 - Telnet, Ftp,... NCSA <http://archive.ncsa.uiuc.edu/SDG/Software/PCTelnet/> <ftp://ftp.ncsa.uiuc.edu/Telnet/DOS/> <ftp://ftp.simtel.net/pub/simtelnet/msdos/ncsatlnt/> • NOS (KA9Q) 29 - A complete mini TCP/IP system <ftp://ftp.simtel.net/pub/simtelnet/msdos/ tcpip/> <http://www.alltheweb.com/search?cat=ftp&q=e920603.zip> To use NOS you need documentation, for example the package <ftp://ftp.simtel.net/pub/ simtelnet/msdos/tcpip/intronos.zip> • SSHDOS 30 - SSH client for Dos <http://sourceforge.net/projects/sshdos> • Talk 31 - Talk client for Dos <http://www.smashco.com/wattcp/talk-13.zip> • ABC-nslookup 32 - DNS query clients for Dos <http://www.smashco.com/wattcp/nslb01a.zip> See also: • Marc S. Ressl, Dos Internet Pages <http://www.fdisk.com/doslynx/> • Smash-Co Communications, TCP/IP for MS-DOS <http://www.smashco.com/wattcp.asp> • The U-M Software Archive <http://www.umich.edu/~archive/msdos/communications/wattcp/> <http://www.umich.edu/~archive/msdos/communications/packet/> 20 WATTCP free of charge library DOS PPPD mixed licenses 22 Comring GNU GPL 23 WATTCP apps cannot be sold 24 MiniTelnet free software with a special license 25 Bobcat GNU GPL 26 PCroute cannot distribute modifications 27 PPRD software non libero: licenza Artistic 28 NCSA Telnet public domain 29 NOS public domain 30 SSHDOS GNU GPL 31 Talk GNU GPL 32 ABC-nslookup UCB BSD 21 The valuable DOS Freeware page 4116 368.5 Compilers assembler See the FreeDOS project (<http://www.freedos.org>) for assembler compilers. batch • BAT2EXE 33 - Compile batch files for speed <ftp://ftp.simtel.net/pub/simtelnet/msdos/batchutl/ bat2ex15.zip> <http://www.alltheweb.com/search?cat=ftp&q=bat2ex15.zip> C/C++ See the FreeDOS project (<http://www.freedos.org>) for C and C++ compilers. Perl Perl 34 - Practical Extraction Report Language <ftp://ftp.simtel.net/pub/simtelnet/msdos/perl/> Rexx • BREXX 35 - Rexx interpreter for Dos/Unix <http://ftp.gwdg.de/pub/languages/rexx/brexx/html/ rx.html> xBase • nanoBase 36 - Mini, but nearly complete xBase <http://a2.swlibero.org/nanobase_1997.html> See also: • David Muir Shamoff, Catalog of free compilers and interpreters <http://www.idiom.com/free-compilers/> 368.6 Typesetting • Nro 37 - A Nroff implementation for Dos <ftp://ftp.simtel.net/pub/simtelnet/msdos/txtutl/nroff1.zip> <http://www.alltheweb.com/search?cat=ftp&q=nroff1.zip> • Ghostscript 38 - ‘‘GNU’’ original edition - PostScript previewing, conversion, and printing <ftp://mirror.cs.wisc.edu/pub/mirrors/ghost/gnu/> • emTeX tex/> 39 - TeX-LaTeX distribution for Dos <ftp://www.ctan.org/tex-archive/systems/msdos/em- 368.7 More Dos software sources • <ftp://ftp.simtel.net/pub/simtelnet/msdos/> • <http://garbo.uwasa.fi/pc/> 33 BAT2EXE public domain Perl GNU GPL or Artistic 35 BREXX public domain 36 nanoBase GNU GPL 37 Nro public domain 38 Ghostscript GNU GPL 39 emTeX LPPL but some files have different conditions 34 The valuable DOS Freeware page 368.8 Search engines • <http://www.alltheweb.com/?c=ftp> • <http://www.shareware.com/> Appunti di informatica libera 2003.01.01 --- Copyright © 2000-2003 Daniele Giacomini -- daniele @ swlibero.org 4117 Chapter 369 Clean the Clipper 5.2 A different way to program using Clipper 5.2 without commands, that is, without the file ‘STD.CH’. This material appeared originally at ‘http://www.geocities.com/SiliconValley/ 7737/clipper52clean.html’, in 1996. Now it is incorporated inside the Italian document ‘‘Appunti di informatica libera’’, and might be reached at the URI <http://a2.swlibero.org/ clean_the_clipper_5_2.html>. Questo materiale è apparso in origine, nel 1996, presso ‘http://www.geocities.com/ SiliconValley/7737/clipper52clean.html’. Adesso viene incorporato nel documento «Appunti di informatica libera» e può essere raggiunto attraverso l’URI <http:// a2.swlibero.org/clean_the_clipper_5_2.html>. L’intento dell’autore è solo quello di conservare un vecchio lavoro che potrebbe essere ancora utile, nonostante si tratti di considerazioni su un compilatore proprietario, ormai obsoleto. 369.1 Step 1: try to compile with the /P parameter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4120 369.2 Step 2: understand well the use of code blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4120 369.3 Step 3: understand the object programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4121 369.3.1 Classes and methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4121 369.3.2 Class definition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4121 369.3.3 Object creation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4121 369.3.4 Instantiating an object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4121 369.3.5 The ‘‘send’’ symbol . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4122 369.3.6 More about objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4122 369.4 Step 4: understand the get object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4122 369.5 Step 5: trying to stop using commands . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4124 369.5.1 ?/?? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4124 369.5.2 @...BOX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4124 369.5.3 @...GET . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4124 369.5.4 @...SAY . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4125 369.5.5 @...TO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4125 369.5.6 APPEND . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4125 369.5.7 APPEND FROM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4125 369.5.8 CLEAR . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4126 369.5.9 CLOSE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4126 369.5.10 COMMIT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4126 369.5.11 CONTINUE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4126 369.5.12 COPY . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4126 369.5.13 COUNT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4127 369.5.14 CREATE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4127 369.5.15 DEFAULT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4127 4118 Clean the Clipper 5.2 369.5.16 369.5.17 369.5.18 369.5.19 369.5.20 369.5.21 369.5.22 369.5.23 369.5.24 369.5.25 369.5.26 369.5.27 369.5.28 369.5.29 369.5.30 369.5.31 369.5.32 369.5.33 369.5.34 369.5.35 369.5.36 369.5.37 369.5.38 369.5.39 369.5.40 369.5.41 369.5.42 369.5.43 369.5.44 369.5.45 369.5.46 369.5.47 369.5.48 369.5.49 369.5.50 369.5.51 369.5.52 4119 DELETE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4127 EJECT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4128 ERASE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4128 FIND . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4128 GO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4128 INDEX ON . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4128 JOIN . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4128 KEYBOARD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4128 LABEL FORM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4129 LIST . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4129 LOCATE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4129 PACK . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4129 QUIT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4129 READ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4129 RECALL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4129 REINDEX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4130 RELEASE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4130 RENAME . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4130 REPLACE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4130 REPORT FORM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4130 RESTORE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4131 RESTORE FROM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4131 RUN . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4131 SAVE SCREEN TO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4131 SAVE TO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4131 SEEK . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4131 SELECT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4131 SET . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4131 SKIP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4135 SORT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4135 STORE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4135 SUM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4135 TOTAL ON . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4135 UNLOCK . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4135 UPDATE FROM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4136 USE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4136 ZAP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4136 369.6 Step 6: free yourself from STD.CH - /U . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4136 369.7 Step 7: take control over all include files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4136 Clipper 5.2, as the xBase tradition imposes, is not an ordered, clear, simple programming language. The question is: which is the right way to write a Clipper program? If the intention is not to make a xBase program, but a Clipper program, maybe it can be decided that it is better to use Clipper without commands. Clean the Clipper 5.2 4120 369.1 Step 1: try to compile with the /P parameter Supposing to compile the file ‘TEST.PRG’ this way: C:\>CLIPPER TEST.PRG /P It generates a preprocessed output file (‘test.PPO’), that is a source file without comments, where commands are translated into real Clipper instructions. That is, all the ‘#COMMAND’ substitution are executed and the translation is sent to the ‘.PPO’ file. It may be difficult to read this file the first time. 369.2 Step 2: understand well the use of code blocks The code block is a small piece of executable program code that can be stored inside a variable, or can be used as a literal constant. The good of it, is that pieces of code may be sent to functions. A code block is something like a little user defined function where only a sequence of expressions (functions and/or assignments) may appear: no loops, no conditional structures. A code block may receive arguments and return a value after execution, just like a function. The syntax is the following, where curly brackets are part of the code block: { | [argument_list ] | exp_list } That is: the argument_list is optional; the exp_list may contain one or more expressions separated with a comma. For example, calling the following code block will give the string ‘‘hello world’’ as result. { || "hello world" } The following code block requires a numeric argument and returns the number passed as argument incremented: { | n | n+1 } The following code block requires two numeric arguments and returns the sum of the two square radix: { | nFirst, nSecond | SQRT(nFirst) + SQRT(nSecond) } But code blocks may contain more expressions and the result of the execution of the code block is the result of the last expression. The following code block executes in sequence some functions and gives ‘‘hello world’’ as a result. { | a, b | functionOne(a), functionTwo(b), "hello world" } To start the execution of a code block a function is used: ‘EVAL()’. For example, a code block is assigned to a variable and then executed. B := { || "hello world" } EVAL( B ) == "hello world" Another example with one parameter. B := { | n | n+1 } EVAL( B, 1 ) == 2 Another example with two parameters. B := { | nFirst, nSecond | SQRT(nFirst) + SQRT(nSecond) } EVAL( B, 2, 4 ) == 20 And so on. Clean the Clipper 5.2 4121 369.3 Step 3: understand the object programming Clipper 5.2 do not permit to create objects, but it gives some good objects to use: ‘GET’ and ‘TBROWSE’. Before starting to clean programming from commands, it is necessary to understand how to use well the Clipper objects. 369.3.1 Classes and methods A class defines the structure of a ‘‘black box’’, that is a data container; a method is an action to make on a piece of data contained inside the black box. There is no way to reach the data contained inside the black box without a method. The black box can be called object. The methods may be seen as they where special functions which interact with a specific piece of data contained inside the object. 369.3.2 Class definition Supposing that Clipper permits to define classes (unluckily only predefined classes can be used), the hypothetical syntax could be: [ ] CLASS ClassName FROM ParentClass VAR Var1 ,Var2 ,... VarN METHOD {method_definition_1 } , ENDCLASS [ [ ]] [ ...{method_definition_n } ] This way, the class defines a group of variables and a group of method to use with these variables. 369.3.3 Object creation The presence of classes permits to create objects: the black boxes. Variable_name := ClassName This way, a variable contains (is) an object. Please note that inside Clipper, an object may be generated also from a function, that is, a function can return an object. This way the example can be: Variable_name := classfunction( ... ) The next problem is to handle this object. 369.3.4 Instantiating an object As already stated before, methods are used to handle data contained inside an object. This is said to be instantiating an object. Clipper permits also to handle directly (apparently without methods) some variables contained inside objects. These are called ‘‘Exported Instance Variables’’. So, an object can be instantiated this way: object:exported_instance_variable := new_value object:method() An exported instance variable may be read and/or modified depending on the allowed access to it; a method, inside Clipper, is something like a function with or without parameters (if parameters are present, these are usually used to modify data inside the object), that normally returns a value. 4122 Clean the Clipper 5.2 369.3.5 The ‘‘send’’ symbol To instantiate an object or simply to access an exported instance variable, the ‘‘send’’ symbol (colon) is used. 369.3.6 More about objects If this is not enough to understand objects inside Clipper, the following document should be read: Peter M. Freese, o:Clip - An Object Oriented Extension to Clipper 5.01 1991, CyberSoft <ftp://ftp.simtel.net/pub/simtelnet/msdos/clipper/oclip.zip> 369.4 Step 4: understand the get object What happens with a command like the following: @ nTop , nLeft GET Var A get object is created containing all the necessary information for editing the variable Var at the screen position nTop , nLeft . After that, this get object is added to a get objects array (usually called ‘GetList’). The get objects array will contain all the get objects used during a ‘READ’. So, what happens when a ‘READ’ command is encountered. The get objects array (‘GetList’) is read and the editing of all get objects is executed. After that, the get objects array is cleared. This method hides what Clipper really makes. The suggestion here is to create a ‘GET()’ function that will substitute the ‘@...GET’ command and to use the ‘READMODAL()’ function to read the get objects array. Here is an example of it: function GET( aoGet, nRow, nCol, bVar, cGetPicture, cColorString, bPreValid, bPostValid ) // declare a local get object local oGet // create the get object using the function GETENV() oGet := GETENV( nRow, nCol, bVar, NIL, cGetPicture, cGetColor ) // send to the get object the pre-validation condition code block (WHEN) oGet:preBlock := bPreValid // send to the get object the post-validation condition code block (VALID) oGet:postBlock := bPostValid // display the get on the screen using the display() method oGet:display() // add the get object to the get objects array AADD( aoGet, oGet ) return NIL • ‘aoGet’ is the get objects array (so here is explicitly passed). This get objects array is modified (grown) and there is no need to return it as inside Clipper, arrays are always passed by reference to functions. • ‘nRow’ and ‘nCol’ are the screen coordinates where the get field should appear at, as it works with the ‘@...GET’ command. Clean the Clipper 5.2 4123 • ‘bVar’ is a special code block that permits the editing of a variable. If the variable ‘Var’ is to be edited, the code block is: { |x| iif( pcount() > 0, Var := x, Var } • ‘cGetPicture’ is the picture to use: same as the ‘@...GET’ command. • ‘cColorString’ is the color string to use: same as the ‘@...GET’ command. • ‘bPreValid’ is a code block containing the condition that must be valid before the cursor can reach this get field. It is equivalent to the ‘WHEN’ condition used with the ‘@...GET’ command, but it must be converted into a code block. For example, if the condition is ‘A > B’, the code block is ‘{|| A > B}’ • ‘bPostValid’ is a code block containing the condition that must be valid before the cursor can leave this get field. It is equivalent to the ‘VALID’ condition used with the ‘@...GET’ command, but it must be converted into a code block. For example, if the condition is ‘A > B’, the code block is ‘{|| A > B}’ If there is a get function like the above one, screen I/O may be performed like the following example: function do_some_editing() // define a variable to use as a get objects array // and initialise it to the empty array local aoGet := {} ... ... // add a new get object to the get objects array get(; aoGet,; 10, 10,; { |x| iif( pcount() > 0, myVariable := x, myVariable },; "@s30@",; "gb+/b, n/w, n, n, w/n",; { || .T. },; { || .T. }; ) ... // read the get objects array readmodal( aoGet ) // clear the get objects array aoGet := {} ... return ... If the function ‘GET()’ is not liked, the above I/O may be done as it follows: function do_some_editing() // define a variable to use as a get object local aoGet // define a variable to use as a get objects array // and initialise it to the empty array local aoGet := {} ... Clean the Clipper 5.2 4124 ... // add a new get object to the get objects array oGet :=; GETENV(; 10, 10,; { |x| iif( pcount() > 0, myVariable := x, myVariable },; NIL,; "@s30@",; "gb+/b, n/w, n, n, w/n",; ) AADD( aoGet, oGet ) ... // read the get objects array readmodal( aoGet ) // clear the get objects array aoGet := {} ... return ... 369.5 Step 5: trying to stop using commands To stop using commands, it is important to understand how commands are or may be translated into functions. Sometimes Clipper uses some undocumented functions: these are functions that start with a underline. 369.5.1 ?/?? [exp_list] qout([exp_list]) ?? [exp_list] qqout([exp_list]) ? 369.5.2 @...BOX [COLOR cColorString] nRight , [cnBoxString ], [cColorString ]) @ nTop , nLeft , nBottom , nRight BOX cnBoxString dispbox(nTop , nLeft , nBottom , 369.5.3 @...GET [ ] ] [COLOR @ nTop , nLeft GET Var PICTURE cGetPicture ,→ VALID lPostExpression [ cColorString ] [WHEN lPreExpression ] ←- setpos(nTop , nLeft ) [ ] aadd( GetList, _GET_( Var , "Var ", cGetPicture , {|| lPostExpression } ,←,→ {|| lPreExpression } ):display() ) atail(GetList):colorDisp(cColorString ) [ ] This is the command substitution made automatically, but it shouldn’t be used to make clean programs. The step 4 (369.1) suggests to create a get function. Clean the Clipper 5.2 4125 369.5.4 @...SAY @ nTop , nLeft SAY exp [COLOR cColorString ] devpos(nTop , nLeft ) devout(exp [, ] cColorString ) @ nTop , nLeft SAY exp PICTURE cSayPicture [COLOR cColorString ] devpos(nTop , nLeft ) devoutpic(exp , cSayPicture , [cColorString]) 369.5.5 @...TO [COLOR cColorString] dispbox(nTop , nLeft , nBottom , nRight , 2 [,cColorString ]) @ nTop , nLeft TO nBottom , nRight [COLOR cColorString ] dispbox(nTop , nLeft , nBottom , nRight , 1 [,cColorString ]) @ nTop , nLeft CLEAR [TO nBottom , nRight ] scroll([nTop ], [nLeft ], [nBottom , nRight ]) @ nTop , nLeft TO nBottom , nRight DOUBLE setpos(nRow , nCol ) 369.5.6 APPEND APPEND BLANK dbappend() 369.5.7 APPEND FROM [FIELDS idField_list] [scope] [WHILE lCondition]←] [VIA xcDriver] __dbApp( cFileName , [acFields], [bForCondition ], [bWhileCondition ], [nNextRecords ],←,→[nRecord ], [lRest], [cDriver] ) APPEND FROM xcFile [FIELDS idField_list] [scope] [WHILE lCondition ] [FOR lCondition ]←,→ APPEND FROM xcFile ,→ FOR lCondition [ DELIMITED xcDelimiter [ ] [ ] [acFields], [bForCondition ], [bWhileCondition ] __dbDelim( .f., cFileName , cDelimiter , ,←,→ nNextRecords , nRecord , lRest ) [ ] [ ] APPEND FROM xcFile [FIELDS idField_list] [scope] [WHILE lCondition ]←,→[FOR lCondition ] SDF __dbSDF( .f., cFileName , [acFields], [bForCondition ], [bWhileCondition ], [nNextRecords ] ,←,→[nRecord ], [lRest] ) Clean the Clipper 5.2 4126 369.5.8 CLEAR CLEAR Scroll() SetPos(0,0) ReadKill(.T.) GetList := {} CLEAR GETS ReadKill(.T.) GetList := {} CLEAR SCREEN | CLS Scroll() SetPos(0,0) 369.5.9 CLOSE CLOSE dbCloseArea() CLOSE idAlias idAlias ->( dbCloseArea() ) CLOSE ALTERNATE Set(19, "") CLOSE DATABASES dbCloseAll() CLOSE INDEXES dbClearIndex() 369.5.10 COMMIT COMMIT dbCommitAll() 369.5.11 CONTINUE CONTINUE __dbContinue() 369.5.12 COPY |xcDevice __CopyFile( cSourceFile , cTargetFile |cDevice ) COPY STRUCTURE [FIELDS idField_list] TO xcDatabase __dbCopyStruct( cDatabase, [acFields] ) COPY FILE xcSourceFile TO xcTargetFile COPY STRUCTURE EXTENDED TO xcExtendedDatabase __dbCopyXStruct( cExtendedDatabase ) [ ][ ] [scope] [WHILE ] COPY TO xcFile FIELDS idField_list ,→ FOR lCondition VIA xcDriver [ ] lCondition ←- Clean the Clipper 5.2 4127 [ ] [ ], [bWhileCondition ], [nNextRecords ],←] [ ] [ ] xcFile [FIELDS idField_list] [scope] [WHILE lCondition ] [FOR lCondition ]←- __dbCopy( cFileName , acFields , bForCondition ,→ nRecord , lRest , cDriver ) [ COPY TO ,→DELIMITED xcDelimiter [ ] [ ] [acFields], [bForCondition ], [bWhileCondition ] __dbDelim( .t., cFileName , cDelimiter , ,←,→ nNextRecords , nRecord , lRest ) [ ] [ COPY TO xcFile [FIELDS ,→[FOR lCondition ] SDF ] idField_list] [scope] [WHILE __dbSDF( .t., cFileName , ,←,→ nRecord , lRest ) [ ] [ ] lCondition ←- [acFields], [bForCondition ], [bWhileCondition ], [nNextRecords ] ] 369.5.13 COUNT [ ] [WHILE ][ ] COUNT TO idVar FOR lForCondition ,→ RECORD nRecord REST ALL [ ][ lWhileCondition ] [NEXT nNextRecords ]←- dbeval( {||idVar :=idVar +1}, {||lForCondition }, {||lWhileCondition },←,→ nNextRecords , nRecord , lRest ) 369.5.14 CREATE [NEW] [ALIAS cAlias] [VIA cDriver] cExtendedDatabase , [cDriver], [lNew], [cAlias] ) CREATE xcDatabase FROM xcExtendedDatabase __dbCreate( cDatabase, 369.5.15 DEFAULT DEFAULT xVar TO xDefaultValue if xVar == NIL xVar := xDefaultValue end 369.5.16 DELETE DELETE dbDelete() [ ] [WHILE lWhileCondition ] [NEXT ] [ ] [ALL] DELETE FOR lForCondition ,→ RECORD nRecord REST [ nNextRecords dbeval( {||dbDelete()}, {||lForCondition }, {||lWhileCondition },←,→ nNextRecords , nRecord , lRest ) DELETE FILE xcFile ferase( cFile ) ]←- Clean the Clipper 5.2 4128 369.5.17 EJECT EJECT qqout( chr(13) ) 369.5.18 ERASE ERASE xcFile ferase( cFile ) 369.5.19 FIND FIND xcSearchString dbSeek( cSearchString ) 369.5.20 GO [ ] GO TO nRecord dbgoto(nRecord ) [ ] GO TO BOTTOM dbGoBottom() [ ] GO TO TOP dbgotop() 369.5.21 INDEX ON [ ][ ] INDEX ON expKey TO xcIndexName UNIQUE FOR lForCondition ←,→ WHILE lWhileCondition EVAL lEvalCondition EVERY nRecords [ ] [[ ][ ]] [ASCENDING| ] DESCENDING [ ] [ ] ] [ ordCondSet( cForCondition , bForCondition , , bWhileCondition ,→ bEvalCondition , nRecords , RECNO(), , , , lDescending ) [ ] [ ],←- ordCreate( cIndexName , , cExpKey , bExpKey , lUnique ) 369.5.22 JOIN ] [FIELDS idField_list] cDatabase, [acFields], [bForCondition ] ) JOIN WITH xcAlias TO xcDatabase __dbJoin( cAlias , [FOR 369.5.23 KEYBOARD KEYBOARD cString __Keyboard( [cString] ) --> NIL lCondition Clean the Clipper 5.2 4129 369.5.24 LABEL FORM [TO PRINTER] [TO FILE xcFile] [NOCONSOLE] [scope]←[ ] [FOR lCondition] [SAMPLE] __LabelForm( cLabel , [lToPrinter ], [cFile], [lNoConsole ],←,→[bForCondition ], [bWhileCondition ], [nNextRecords ], [nRecord ],←,→[lRest], [lSample ] ) LABEL FORM xcLabel ,→ WHILE lCondition 369.5.25 LIST [ ][ ] [scope]←][ ][ ] __dbList( [lToDisplay], abListColumns , [lAll], [bForCondition ], [bWhileCondition ],←,→[nNextRecords ], [nRecord ], [lRest], [lToPrinter], [cFileName ] ) LIST exp_list TO PRINTER TO FILE xcFile ,→ WHILE lCondition FOR lCondition OFF [ 369.5.26 LOCATE [scope] FOR lCondition [WHILE lCondition] __dbLocate( [bForCondition ], [bWhileCondition ], [nNextRecords ], [nRecord ], [lRest] LOCATE 369.5.27 PACK PACK __dbPack() 369.5.28 QUIT QUIT __Quit() 369.5.29 READ READ ReadModal(GetList) GetList := {} READ SAVE ReadModal(GetList) 369.5.30 RECALL RECALL dbRecall() [ ] [WHILE lWhileCondition ] [NEXT ] [ALL] RECALL FOR lForCondition ,→ RECORD nRecord REST [ ][ nNextRecords dbeval( {||dbRecall()}, {||lForCondition }, {||lWhileCondition },←,→ nNextRecords , nRecord , lRest ) ]←- ) Clean the Clipper 5.2 4130 369.5.31 REINDEX REINDEX [EVAL ] [EVERY nRecords] , [bEvalCondition ], [nRecords ], lEvalCondition ordCondSet(, , , , , , , , , ) ordListRebuild() 369.5.32 RELEASE RELEASE idMemvar __MXRelease( "idMemvar " ) RELEASE ALL __MRelease("*", .t.) RELEASE ALL LIKE skeleton __MRelease( "skeleton ", .t. ) RELEASE ALL EXCEPT skeleton __MRelease( "skeleton ", .F. ) 369.5.33 RENAME RENAME xcOldFile TO xcNewFile frename( cOldFile , cNewFile ) 369.5.34 REPLACE [ ][ ][ ][ ] idField1 := exp1 [, idField2 ] REPLACE idField1 WITH exp1 , idField2 WITH exp2 ... ←,→ FOR lForCondition WHILE lWhileCondition NEXT nNextRecords ,→ RECORD nRecord REST ALL [ [ ][ ]←- ] dbeval( {|| := exp2 ... },←,→{||lForCondition }, {||lWhileCondition }, nNextRecords ,←,→ nRecord , lRest ) REPLACE idField1 WITH exp1 idField1 := exp1 369.5.35 REPORT FORM [TO PRINTER] [TO FILE xcFile] [NOCONSOLE] [scope]←] [FOR lCondition] [PLAIN | HEADING cHeading] [NOEJECT] [SUMMARY REPORT FORM xcReport ,→ WHILE lCondition [ ] [ ] [ ] [ ] [ ] [ __ReportForm( cForm , lToPrinter , cToFile , lNoConsole , bForCondition , bWhileCondition ,←,→ nNext , nRecord , lRest , lPlain , cbHeading , lBeforeEject , lSummary [ ] ] [ ] [ ] [ ] [ ] [ ] [ ] ) Clean the Clipper 5.2 4131 369.5.36 RESTORE RESTORE SCREEN FROM cScreen restscreen( 0, 0, Maxrow(), Maxcol(), cScreen ) 369.5.37 RESTORE FROM [ADDITIVE] cMemFileName , [lAdditive] RESTORE FROM xcMemFile __MRestore( ) 369.5.38 RUN RUN xcCommandLine __Run( cCommand ) 369.5.39 SAVE SCREEN TO SAVE SCREEN TO cScreen cScreen := savescreen( 0, 0, maxrow(), maxcol() ) 369.5.40 SAVE TO [ALL [LIKE|EXCEPT skeleton]] cMemFileName , [cSkeleton ], [lLike ] ) SAVE TO xcMemFile _MSave( 369.5.41 SEEK [SOFTSEEK] expSearch [, lSoftSeek ] SEEK expSearch dbSeek( ) 369.5.42 SELECT SELECT xnWorkArea | idAlias dbSelectArea( nWorkArea | cIdAlias ) 369.5.43 SET Most of the ‘SET...’ commands are translated into the ‘SET()’ function that distinguishes different modes depending on a number. As this number is difficult to handle during programming (essentially because it is difficult to remember the meaning of it), Clipper offers the ‘SET.CH’ include file that helps with manifest constants. #define #define #define #define #define #define _SET_EXACT _SET_FIXED _SET_DECIMALS _SET_DATEFORMAT _SET_EPOCH _SET_PATH 1 2 3 4 5 6 Clean the Clipper 5.2 4132 #define _SET_DEFAULT #define #define #define #define 7 _SET_EXCLUSIVE _SET_SOFTSEEK _SET_UNIQUE _SET_DELETED 8 9 10 11 #define _SET_CANCEL #define _SET_DEBUG #define _SET_TYPEAHEAD 12 13 14 #define #define #define #define #define #define #define #define #define #define #define _SET_COLOR _SET_CURSOR _SET_CONSOLE _SET_ALTERNATE _SET_ALTFILE _SET_DEVICE _SET_EXTRA _SET_EXTRAFILE _SET_PRINTER _SET_PRINTFILE _SET_MARGIN 15 16 17 18 19 20 21 22 23 24 25 #define #define #define #define #define #define #define #define #define _SET_BELL _SET_CONFIRM _SET_ESCAPE _SET_INSERT _SET_EXIT _SET_INTENSITY _SET_SCOREBOARD _SET_DELIMITERS _SET_DELIMCHARS 26 27 28 29 30 31 32 33 34 #define #define #define #define _SET_WRAP _SET_MESSAGE _SET_MCENTER _SET_SCROLLBREAK 35 36 37 38 SET ALTERNATE TO xcFile [ADDITIVE] Set( _SET_ALTFILE, cFile , lAdditive ) SET ALTERNATE ON | OFF | Set( _SET_ALTERNATE, "ON" SET BELL ON | OFF | Set( _SET_BELL, "ON" SET COLOR | COLOUR xlToggle | "OFF" | lToggle ) xlToggle | "OFF" | lToggle ) TO (cColorString) SetColor( cColorString ) SET CONFIRM ON | OFF | Set( _SET_CONFIRM, "ON" SET CONSOLE ON | OFF | Set( _SET_CONSOLE, "ON" xlToggle | "OFF" | lToggle ) xlToggle | "OFF" | | OFF | xlToggle SetCursor( 1 | 0 | iif( lToggle , SET DATE FORMAT [TO] cDateFormat lToggle ) SET CURSOR ON 1, 0 ) ) Clean the Clipper 5.2 4133 Set( _SET_DATEFORMAT, cDateFormat ) SET DECIMALS TO Set( _SET_DECIMALS, 0 ) SET DECIMALS TO nDecimals Set( _SET_DECIMALS, nDecimals ) SET DEFAULT TO Set( _SET_DEFAULT, "" ) SET DEFAULT TO xcPathspec Set( _SET_DEFAULT, cPathspec ) SET DELETED ON | OFF | xlToggle | "OFF" | lToggle ) SET DELIMITERS ON | OFF | xlToggle Set( _SET_DELIMITERS, "ON" | "OFF" | lToggle SET DELIMITERS TO [DEFAULT] Set( _SET_DELETED, "ON" ) Set( _SET_DELIMCHARS, "::" ) SET DELIMITERS TO cDelimiters Set( _SET_DELIMCHARS, cDelimiters ) | PRINTER "SCREEN" | "PRINTER" SET DEVICE TO SCREEN Set( _SET_DEVICE, ) SET EPOCH TO nYear Set( _SET_EPOCH, nYear ) SET ESCAPE ON | OFF | xlToggle Set( _SET_ESCAPE, "ON" SET EXACT ON | OFF | | "OFF" | lToggle ) xlToggle | "OFF" | lToggle ) SET EXCLUSIVE ON | OFF | xlToggle Set( _SET_EXCLUSIVE, "ON" | "OFF" | lToggle Set( _SET_EXACT, "ON" SET FILTER TO dbclearfilter() SET FILTER TO lCondition dbsetfilter( bCondition , cCondition ) SET FIXED ON | OFF | Set( _SET_FIXED, "ON" xlToggle | "OFF" | lToggle ) SET FUNCTION nFunctionKey TO cString __SetFunction( nFunctionKey , cString ) SET INDEX TO [xcIndex [, xcIndex1 ... ordListClear() ordListAdd( cIndex ) ordListAdd( cIndex1 ) ... SET INTENSITY ON | OFF | xlToggle ]] ) Clean the Clipper 5.2 4134 Set( _SET_INTENSITY, "ON" SET KEY nInkeyCode | "OFF" | lToggle ) [TO] SetKey( nInkeyCode , NIL ) SET KEY nInkeyCode TO [idProcedure] SetKey( nInkeyCode , { |p, l, v| idProcedure(p, l, v)} ) SET MARGIN TO Set( _SET_MARGIN, 0 ) SET MARGIN TO [nPageOffset ] Set( _SET_MARGIN, nPageOffset ) SET MESSAGE TO Set( _SET_MESSAGE, 0 ) Set( _SET_MCENTER, .F. ) SET MESSAGE TO [nRow [CENTER | CENTRE]] Set( _SET_MESSAGE, nRow ) Set( _SET_MCENTER, lCenter ) SET ORDER TO [nIndex ] ordSetFocus( nIndex ) SET PATH TO Set( _SET_PATH, "" ) [xcPathspec [, cPathspec1 ... ] ] Set( _SET_PATH, cPathspec [, cPathspec1 ... ] ) SET PRINTER ON | OFF | xlToggle Set( _SET_PRINTER, "ON" | "OFF" | lToggle SET PATH TO ) SET PRINTER TO Set( _SET_PRINTFILE, "" ) [xcDevice|xcFile [ADDITIVE]] _SET_PRINTFILE, cDevice|cFile , lAdditive SET PRINTER TO Set( ) SET RELATION TO dbclearrelation() SET RELATION TO [expKey1 INTO xcAlias1 ] [, [TO] dbClearRel() ["expKey1 "] ) dbSetRelation( cAlias2 , {||expKey2 }, ["expKey1 "] ) SET RELATION TO [expKey1 INTO xcAlias1 ]←,→[, [TO] expKey2 INTO xcAlias2 ...] ADDITIVE dbSetRelation( cAlias1 , {||expKey1 }, ["expKey1 "] ) dbSetRelation( cAlias2 , {||expKey2 }, ["expKey1 "] ) SET SCOREBOARD ON | OFF | xlToggle Set( _SET_SCOREBOARD, "ON" | "OFF" | lToggle ) SET SOFTSEEK ON | OFF | xlToggle Set( _SET_SOFTSEEK, "ON" | "OFF" | lToggle ) dbSetRelation( cAlias1 , {||expKey1 }, ] expKey2 INTO xcAlias2 ... Clean the Clipper 5.2 4135 SET TYPEAHEAD TO nKeyboardSise Set( _SET_TYPEAHEAD, nKeyboardSise ) | OFF | xlToggle Set( _SET_UNIQUE, "ON" | "OFF" | lToggle SET WRAP ON | OFF | xlToggle Set( _SET_WRAP, "ON" | "OFF" | lToggle ) SET UNIQUE ON ) 369.5.44 SKIP [nRecords] [ALIAS idAlias|nWorkArea ] [idAlias|nWorkArea -> ]( dbSkip([nRecords]) SKIP ) 369.5.45 SORT [/[A|D][C]] [, idField2 [/[A|D][C]] ...]←[ ][ ] [FOR lCondition] __dbSort( cDatabase, [acFields], [bForCondition ], [bWhileCondition ],←,→[nNextRecords ], [nRecord ], [lRest] ) SORT TO xcDatabase ON idField1 ,→ scope WHILE lCondition 369.5.46 STORE STORE value TO variable variable := value 369.5.47 SUM [ ] [ ] [FOR lForCondition ]←] [RECORD nRecord] [REST] [ALL] [, idVar2 :=idVar2 +nExp2 ...] },←- SUM nExp1 , nExp2 ... TO idVar1 , idVar2 ... ,→ WHILE lWhileCondition NEXT nNextRecords [ ][ dbeval( {||idVar1 :=idVar1 +nExp1 ,→{||lForCondition }, {||lWhileCondition }, nNextRecords , nRecord , lRest ) 369.5.48 TOTAL ON [ ][ ] TO xcDatabase [scope]←] __dbTotal( cDatabase, bKey, [acFields], [bForCondition ], [bWhileCondition ],←,→[nNextRecords ], [nRecord ], [lRest] ) TOTAL ON expKey FIELDS idField_list ,→ WHILE lCondition FOR lCondition [ 369.5.49 UNLOCK UNLOCK dbUnlock() UNLOCK ALL dbUnlockAll() Clean the Clipper 5.2 4136 369.5.50 UPDATE FROM [ ] UPDATE FROM xcAlias ON expKey RANDOM ,→WITH exp , idField2 WITH exp ... [ __dbUpdate( cAlias , REPLACE idField1 ←- ] bKey, [lRandom ], [bReplacement ] ) Example: __dbUpdate( "INVOICE", {|| LAST}, .T.,; {|| FIELD->TOTAL1 := ←,→INVOICE->SUM1,; FIELD->TOTAL2 := INVOICE->SUM2 } ) 369.5.51 USE USE dbclosearea() [ ] USE xcDatabase ←,→ INDEX xcIndex1 , xcIndex2 ... [ [ ] [ALIAS ] [VIA cDriver]] dbUseArea( [lNewArea ], [cDriver], [dbSetIndex( cIndex1 )] [dbSetIndex( cIndex2 )] xcAlias ] [EXCLUSIVE|SHARED] [NEW] [ READONLY cDatabase, [cAlias], [lShared], [lReadOnly] ) ... 369.5.52 ZAP ZAP dbZap() 369.6 Step 6: free yourself from STD.CH - /U Now that no command is used, the standard include file ‘STD.CH’ is no more necessary. Clipper uses ‘STD.CH’ automatically, unless specified differently. Just compile this way: C:>CLIPPER TEST.PRG /U 369.7 Step 7: take control over all include files Clipper comes with so many include files (‘*.CH’). To avoid confusion, a single ‘STANDARD.CH’ file containing all what is needed for the application may be prepared. At least, it is necessary the following. *================================================================= * DISPBOX() *================================================================= * Single-line box #define BOX_SINGLE; (; CHR(218) +; CHR(196) +; CHR(191) +; CHR(179) +; Clean the Clipper 5.2 4137 CHR(217) +; CHR(196) +; CHR(192) +; CHR(179); ) * Double-line box #define BOX_DOUBLE; (; CHR(201) +; CHR(205) +; CHR(187) +; CHR(186) +; CHR(188) +; CHR(205) +; CHR(200) +; CHR(186); ) * Single-line top, double-line sides #define BOX_SINGLE_DOUBLE; (; CHR(214) +; CHR(196) +; CHR(183) +; CHR(186) +; CHR(189) +; CHR(196) +; CHR(211) +; CHR(186); ) * Double-line top, single-line sides #define BOX_DOUBLE_SINGLE; (; CHR(213) +; CHR(205) +; CHR(184) +; CHR(179) +; CHR(190) +; CHR(205) +; CHR(212) +; CHR(179); ) *================================================================= * ERRORS *================================================================= * Severity levels (e:severity) #define ERROR_SEVERITY_WHOCARES #define ERROR_SEVERITY_WARNING #define ERROR_SEVERITY_ERROR #define ERROR_SEVERITY_CATASTROPHIC * Generic error codes (e:genCode) #define ERROR_GENERIC_ARG #define ERROR_GENERIC_BOUND #define ERROR_GENERIC_STROVERFLOW #define ERROR_GENERIC_NUMOVERFLOW #define ERROR_GENERIC_ZERODIV #define ERROR_GENERIC_NUMERR #define ERROR_GENERIC_SYNTAX #define ERROR_GENERIC_COMPLEXITY 0 1 2 3 1 2 3 4 5 6 7 8 Clean the Clipper 5.2 4138 #define #define #define #define #define #define #define #define ERROR_GENERIC_MEM ERROR_GENERIC_NOFUNC ERROR_GENERIC_NOMETHOD ERROR_GENERIC_NOVAR ERROR_GENERIC_NOALIAS ERROR_GENERIC_NOVARMETHOD ERROR_GENERIC_BADALIAS ERROR_GENERIC_DUPALIAS 11 12 13 14 15 16 17 18 #define #define #define #define #define #define ERROR_GENERIC_CREATE ERROR_GENERIC_OPEN ERROR_GENERIC_CLOSE ERROR_GENERIC_READ ERROR_GENERIC_WRITE ERROR_GENERIC_PRINT 20 21 22 23 24 25 #define #define #define #define #define #define #define #define #define #define ERROR_GENERIC_UNSUPPORTED ERROR_GENERIC_LIMIT ERROR_GENERIC_CORRUPTION ERROR_GENERIC_DATATYPE ERROR_GENERIC_DATAWIDTH ERROR_GENERIC_NOTABLE ERROR_GENERIC_NOORDER ERROR_GENERIC_SHARED ERROR_GENERIC_UNLOCKED ERROR_GENERIC_READONLY 30 31 32 33 34 35 36 37 38 39 #define ERROR_GENERIC_APPENDLOCK #define ERROR_GENERIC_LOCK 40 41 *================================================================= * INKEY() *================================================================= #define #define #define #define #define #define #define #define K_UP K_DOWN K_LEFT K_RIGHT K_HOME K_END K_PGUP K_PGDN 5 24 19 4 1 6 18 3 // // // // // // // // Up arrow, Ctrl-E Down arrow, Ctrl-X Left arrow, Ctrl-S Right arrow, Ctrl-D Home, Ctrl-A End, Ctrl-F PgUp, Ctrl-R PgDn, Ctrl-C #define #define #define #define #define #define #define #define K_CTRL_UP K_CTRL_DOWN K_CTRL_LEFT K_CTRL_RIGHT K_CTRL_HOME K_CTRL_END K_CTRL_PGUP K_CTRL_PGDN 397 401 26 2 29 23 31 30 // * Ctrl-Up arrow // * Ctrl-Down arrow // Ctrl-Left arrow, Ctrl-Z // Ctrl-Right arrow, Ctrl-B // Ctrl-Home, Ctrl-</synsqb> // Ctrl-End, Ctrl-W // Ctrl-PgUp, Ctrl-Hyphen // Ctrl-PgDn, Ctrl-^ #define #define #define #define #define #define #define #define K_ALT_UP K_ALT_DOWN K_ALT_LEFT K_ALT_RIGHT K_ALT_HOME K_ALT_END K_ALT_PGUP K_ALT_PGDN 408 416 411 413 407 415 409 417 // // // // // // // // #define #define #define #define K_ENTER K_RETURN K_SPACE K_ESC 13 13 32 27 // // // // * * * * * * * * Alt-Up arrow Alt-Down arrow Alt-Left arrow Alt-Right arrow Alt-Home Alt-End Alt-PgUp Alt-PgDn Enter, Ctrl-M Return, Ctrl-M Space bar Esc, Ctrl-<synsqb> Clean the Clipper 5.2 4139 #define #define #define #define #define K_CTRL_ENTER K_CTRL_RETURN K_CTRL_RET K_CTRL_PRTSCR K_CTRL_QUESTION 10 10 10 379 309 // Ctrl-Enter // Ctrl-Return // Ctrl-Return (Compat.) // * Ctrl-Print Screen // Ctrl-? #define #define #define #define K_ALT_ENTER K_ALT_RETURN K_ALT_EQUALS K_ALT_ESC 284 284 387 257 // // // // #define KP_ALT_ENTER 422 // * Keypad Alt-Enter #define #define #define #define #define KP_CTRL_5 KP_CTRL_SLASH KP_CTRL_ASTERISK KP_CTRL_MINUS KP_CTRL_PLUS 399 405 406 398 400 // // // // // * * * * * Keypad Keypad Keypad Keypad Keypad Ctrl-5 Ctrl-/ Ctrl-* Ctrl-Ctrl-+ #define #define #define #define #define KP_ALT_5 KP_ALT_SLASH KP_ALT_ASTERISK KP_ALT_MINUS KP_ALT_PLUS 5 420 311 330 334 // // // // // * * * * * Keypad Keypad Keypad Keypad Keypad Alt-5 Alt-/ Alt-* Alt-Alt-+ #define #define #define #define #define K_INS K_DEL K_BS K_TAB K_SH_TAB 22 7 8 9 271 // // // // // #define #define #define #define K_CTRL_INS K_CTRL_DEL K_CTRL_BS K_CTRL_TAB 402 403 127 404 // * Ctrl-Ins // * Ctrl-Del // Ctrl-Backspace // * Ctrl-Tab #define #define #define #define K_ALT_INS K_ALT_DEL K_ALT_BS K_ALT_TAB 418 419 270 421 // // // // #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define K_CTRL_A K_CTRL_B K_CTRL_C K_CTRL_D K_CTRL_E K_CTRL_F K_CTRL_G K_CTRL_H K_CTRL_I K_CTRL_J K_CTRL_K K_CTRL_L K_CTRL_M K_CTRL_N K_CTRL_O K_CTRL_P K_CTRL_Q K_CTRL_R K_CTRL_S K_CTRL_T K_CTRL_U K_CTRL_V K_CTRL_W K_CTRL_X K_CTRL_Y 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 // // // // // // // // // // // // // // // // // // // // // // // // // * * * * Alt-Enter Alt-Return Alt-Equals Alt-Esc Ins, Ctrl-V Del, Ctrl-G Backspace, Ctrl-H Tab, Ctrl-I Shift-Tab * * * * Alt-Ins Alt-Del Alt-Backspace Alt-Tab Ctrl-A, Ctrl-B, Ctrl-C, Ctrl-D, Ctrl-E, Ctrl-F, Ctrl-G, Ctrl-H, Ctrl-I, Ctrl-J Ctrl-K Ctrl-L Ctrl-M, Ctrl-N Ctrl-O Ctrl-P Ctrl-Q Ctrl-R, Ctrl-S, Ctrl-T Ctrl-U Ctrl-V, Ctrl-W, Ctrl-X, Ctrl-Y Home Ctrl-Right arrow PgDn, Ctrl-ScrollLock Right arrow Up arrow End Del Backspace Tab Return PgUp Left arrow Ins Ctrl-End Down arrow Clean the Clipper 5.2 4140 #define K_CTRL_Z 26 // Ctrl-Z, Ctrl-Left arrow Alt-A Alt-B Alt-C Alt-D Alt-E Alt-F Alt-G Alt-H Alt-I Alt-J Alt-K Alt-L Alt-M Alt-N Alt-O Alt-P Alt-Q Alt-R Alt-S Alt-T Alt-U Alt-V Alt-W Alt-X Alt-Y Alt-Z Alt-1 Alt-2 Alt-3 Alt-4 Alt-5 Alt-6 Alt-7 Alt-8 Alt-9 Alt-0 #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define K_ALT_A K_ALT_B K_ALT_C K_ALT_D K_ALT_E K_ALT_F K_ALT_G K_ALT_H K_ALT_I K_ALT_J K_ALT_K K_ALT_L K_ALT_M K_ALT_N K_ALT_O K_ALT_P K_ALT_Q K_ALT_R K_ALT_S K_ALT_T K_ALT_U K_ALT_V K_ALT_W K_ALT_X K_ALT_Y K_ALT_Z K_ALT_1 K_ALT_2 K_ALT_3 K_ALT_4 K_ALT_5 K_ALT_6 K_ALT_7 K_ALT_8 K_ALT_9 K_ALT_0 286 304 302 288 274 289 290 291 279 292 293 294 306 305 280 281 272 275 287 276 278 303 273 301 277 300 376 377 378 379 380 381 382 383 384 385 // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // #define #define #define #define #define #define #define #define #define #define #define #define K_F1 K_F2 K_F3 K_F4 K_F5 K_F6 K_F7 K_F8 K_F9 K_F10 K_F11 K_F12 28 -1 -2 -3 -4 -5 -6 -7 -8 -9 -40 -41 // F1, Ctrl-Backslash // F2 // F3 // F4 // F5 // F6 // F7 // F8 // F9 // F10 // * F11 // * F12 #define #define #define #define #define #define #define #define #define #define #define #define K_CTRL_F1 K_CTRL_F2 K_CTRL_F3 K_CTRL_F4 K_CTRL_F5 K_CTRL_F6 K_CTRL_F7 K_CTRL_F8 K_CTRL_F9 K_CTRL_F10 K_CTRL_F11 K_CTRL_F12 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -44 -45 // Ctrl-F1 // Ctrl-F2 // Ctrl-F4 // Ctrl-F3 // Ctrl-F5 // Ctrl-F6 // Ctrl-F7 // Ctrl-F8 // Ctrl-F9 // Ctrl-F10 // * Ctrl-F11 // * Ctrl-F12 -30 // #define K_ALT_F1 Alt-F1 Clean the Clipper 5.2 4141 #define #define #define #define #define #define #define #define #define #define #define K_ALT_F2 K_ALT_F3 K_ALT_F4 K_ALT_F5 K_ALT_F6 K_ALT_F7 K_ALT_F8 K_ALT_F9 K_ALT_F10 K_ALT_F11 K_ALT_F12 -31 -32 -33 -34 -35 -36 -37 -38 -39 -46 -47 // Alt-F2 // Alt-F3 // Alt-F4 // Alt-F5 // Alt-F6 // Alt-F7 // Alt-F8 // Alt-F9 // Alt-F10 // * Alt-F11 // * Alt-F12 #define #define #define #define #define #define #define #define #define #define #define #define K_SH_F1 K_SH_F2 K_SH_F3 K_SH_F4 K_SH_F5 K_SH_F6 K_SH_F7 K_SH_F8 K_SH_F9 K_SH_F10 K_SH_F11 K_SH_F12 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -42 -43 // Shift-F1 // Shift-F2 // Shift-F3 // Shift-F4 // Shift-F5 // Shift-F6 // Shift-F7 // Shift-F8 // Shift-F9 // Shift-F10 // * Shift-F11 // * Shift-F12 *================================================================= * MEMOEDIT() *================================================================= * User function entry modes #define MEMOEDIT_IDLE #define MEMOEDIT_UNKEY #define MEMOEDIT_UNKEYX #define MEMOEDIT_INIT 0 1 2 3 // // // // idle, all keys processed unknown key, memo unaltered unknown key, memo altered initialization mode * User function return codes #define MEMOEDIT_DEFAULT 0 #define MEMOEDIT_IGNORE 32 #define MEMOEDIT_DATA 33 #define MEMOEDIT_TOGGLEWRAP 34 #define MEMOEDIT_TOGGLESCROLL 35 #define MEMOEDIT_WORDRIGHT 100 #define MEMOEDIT_BOTTOMRIGHT 101 // // // // // // // perform default action ignore unknown key treat unknown key as data toggle word-wrap mode toggle scrolling mode perform word-right operation perform bottom-right operation *================================================================= * SET() *================================================================= #define #define #define #define #define #define #define _SET_EXACT _SET_FIXED _SET_DECIMALS _SET_DATEFORMAT _SET_EPOCH _SET_PATH _SET_DEFAULT #define #define #define #define _SET_EXCLUSIVE _SET_SOFTSEEK _SET_UNIQUE _SET_DELETED #define _SET_CANCEL #define _SET_DEBUG 1 2 3 4 5 6 7 8 9 10 11 12 13 Clean the Clipper 5.2 4142 #define _SET_TYPEAHEAD 14 #define #define #define #define #define #define #define #define #define #define #define _SET_COLOR _SET_CURSOR _SET_CONSOLE _SET_ALTERNATE _SET_ALTFILE _SET_DEVICE _SET_EXTRA _SET_EXTRAFILE _SET_PRINTER _SET_PRINTFILE _SET_MARGIN 15 16 17 18 19 20 21 22 23 24 25 #define #define #define #define #define #define #define #define #define _SET_BELL _SET_CONFIRM _SET_ESCAPE _SET_INSERT _SET_EXIT _SET_INTENSITY _SET_SCOREBOARD _SET_DELIMITERS _SET_DELIMCHARS 26 27 28 29 30 31 32 33 34 #define #define #define #define _SET_WRAP _SET_MESSAGE _SET_MCENTER _SET_SCROLLBREAK 35 36 37 38 *================================================================= * SETCURSOR() *================================================================= #define #define #define #define #define SETCURSOR_NONE SETCURSOR_NORMAL SETCURSOR_INSERT SETCURSOR_SPECIAL1 SETCURSOR_SPECIAL2 0 1 2 3 4 // // // // // No cursor Normal cursor (underline) Insert cursor (lower half block) Special cursor (full block) Special cursor (upper half block) *================================================================= * RDD REQUESTs *================================================================= external dbfndx external dbfntx // default Appunti di informatica libera 2003.01.01 --- Copyright © 2000-2003 Daniele Giacomini -- daniele @ swlibero.org Chapter 370 nanoBase 1997 A free xBase for Dos. This material appeared originally at ‘http://www.geocities.com/SiliconValley/ 7737/nanobase.html’, in 1997. Now it is incorporated inside the Italian document ‘‘Appunti di informatica libera’’, and might be reached at the URI <http://a2.swlibero.org/ nanobase_1997.html>. Questo materiale è apparso in origine, nel 1997, presso ‘http://www.geocities.com/ SiliconValley/7737/nanobase.html’. Adesso viene incorporato nel documento «Appunti di informatica libera» e può essere raggiunto attraverso l’URI <http://a2.swlibero.org/ nanobase_1997.html>. 370.1 What is it nanoBase 1 is a Dos program that works essentially as: • a dot command line xBase, • a menu driven xBase, • a xBase program interpreter. nanoBase 1997 is compiled in two versions: a small one to be used with old computers (i86 with 640 Kibyte RAM), and a second one to be used with better computers, at least i286 (or better) with 2 Mibyte RAM. 370.2 The dot command line Figure 370.1. The dot line. The dot command line is the first face of nanoBase, the one that appears starting the program normally. It recalls the dot line command of the old xBases. Please note that nanoBase recognise only expressions (that is: no commands). 1 nanoBase GNU GPL 4143 nanoBase 1997 4144 370.3 The menu Figure 370.2. The file menu. Pressing [ F10 ] the nanoBase menu appears. From this menu the operations are easier than writing all commands on a prompt line, but it is always possible to come back to the dot line to do an operation not available from the menu. 370.4 The macro recording, compiling and execution Figure 370.3. The macro menu. nanoBase is able to record some actions made with the menu and all what is correctly typed from the dot prompt. This may be the begin for a little program (called macro inside nanoBase) that can be executed as it is (ASCII), or compiled into another format, faster to execute. Macros for nanoBase are made with a reduced set of the Clipper syntax. The statements recognised from nanoBase are: PROCEDURE procedure_name statements ... RETURN statements ... ENDPROCEDURE [ ] DO PROCEDURE procedure_name BEGIN SEQUENCE statements ... BREAK statements ... END [ ] nanoBase 1997 DO CASE CASE lCondition1 statements ... CASE lCondition2 statements ... OTHERWISE statements ... END [ [ 4145 ] ] WHILE lCondition statements ... EXIT statements ... LOOP statements ... END [ [ ] ] IF lCondition1 statements ... ELSE statements ... END [ ] • the ‘FOR’ loop is not available (too difficult to implement), • there may be no user defined functions (code blocks may be created instead), • procedure calls cannot transfer variables, • there are only public (global) variables. Beside these limitations, there are many added functions to the standard language that make the programming easier. All you need is inside ‘NB.EXE’: • the utility to handle manually the data, • the macro compiler, • the macro executor. 370.5 The report system Figure 370.4. The report menu. nanoBase can handle label (‘.LBL’) and form (‘.FRM’) files in the dBaseIII format. Labels and nanoBase 1997 4146 forms may be created and edited inside nanoBase. Beside these old report system there is another way to make a little bit complicated reports without making a complex macro: it is called RPT. A RPT file is a ASCII file with text mixed with code. The text may contain variables (usually a field or an expression containing fields). To make a complex report some work is needed, but surely less than the time needed to make a report program. The main purpose of it was to be able to print text with variables (typically names and addresses) for every record of a particular ‘.DBF’ file. Now the RPT system makes something more. 370.6 The integrated text editor Figure 370.5. The integrated text editor. nanoBase contains an integrated text editor not particularly good, but very usefull for RPT files (as the expression insertion is very easy with the use of the [ F2 ] key) and whenever there isn’t any other editor there. 370.7 The internal documentation Figure 370.6. The internal documentation. nanoBase’s documentation si translated also inside the HTF format: ‘NB.HLP’. Pressing normally, a contextual piece of the manual appears. [ F1 ], Some standard functions have its own internal help, contained inside the ‘.EXE’ file. This was made to help programming with nanoBase. nanoBase 1997 4147 370.8 Download it Here is the 1997 edition of nanoBase. • EXE for small computers. <ftp://ftp.simtel.net/pub/simtelnet/msdos/database/nbase7a1.zip> <http://www.alltheweb.com/search?cat=ftp&q=nbase7a1.zip> • EXE for i286 with more than 2 Mibyte. <ftp://ftp.simtel.net/pub/simtelnet/msdos/database/nbase7a2.zip> <http://www.alltheweb.com/search?cat=ftp&q=nbase7a2.zip> • Runtime for small computers. <ftp://ftp.simtel.net/pub/simtelnet/msdos/database/nbase7a3.zip> <http://www.alltheweb.com/search?cat=ftp&q=nbase7a3.zip> • Documentation in many different formats. <ftp://ftp.simtel.net/pub/simtelnet/msdos/database/nbase7a4.zip> <http://www.alltheweb.com/search?cat=ftp&q=nbase7a4.zip> • Macro programming examples. <ftp://ftp.simtel.net/pub/simtelnet/msdos/database/nbase7a5.zip> <http://www.alltheweb.com/search?cat=ftp&q=nbase7a5.zip> • Source for version 96.06.16, without mouse support (1996). <ftp://ftp.simtel.net/pub/simtelnet/msdos/database/nbase7a6.zip> <http://www.alltheweb.com/search?cat=ftp&q=nbase7a6.zip> • Source for version 1997. <ftp://ftp.simtel.net/pub/simtelnet/msdos/database/nbase7a7.zip> <http://www.alltheweb.com/search?cat=ftp&q=nbase7a7.zip> 370.9 Bugs and known problems Here is the list of known bugs and problems. • Comparison with floating point numbers may fail. It is better to convert numbers into string before comparing them. • Macros may be contained inside ASCII files or a ‘‘compiled’’ ‘.DBF’ file. In the second case, when nanoBase executes the macro, a work area (the last available one) is used, so it should not be closed or the macro execution will be stopped. A ‘dbcloseall()’ will stop execution of the macro. In substitution of ‘dbcloseall()’, ‘DBCLOSE()’ should be used. • To simplify the macro interpretation, lines such as this: qqout( "You can’t do that // you can’t do that!" ) will generate an error as the interpreter will read only: qqout( "You can’t do that 4148 nanoBase 1997 • nanoBase works good also if you have a screen configuration that permits you to show more than the usual 80 columns and 25 lines, but the library used to handle the mouse is not able to work outside the 80×25 area. Appunti di informatica libera 2003.01.01 --- Copyright © 2000-2003 Daniele Giacomini -- daniele @ swlibero.org Chapter 371 nanoBase 1997 user manual This is the documentation of nanoBase 1997, with minor modifications, that appeared originally at ‘http://www.geocities.com/SiliconValley/7737/nb.htm’. Now it is incorporated inside the Italian document ‘‘Appunti di informatica libera’’, and might be reached at the URI <http://a2.swlibero.org/nanobase_1997.html>. Questo è il manuale di nanoBase 1997, modificato leggermente, che è apparso inizialmente presso ‘http://www.geocities.com/SiliconValley/7737/nb.htm’. Adesso viene incorporato nel documento «Appunti di informatica libera» e può essere raggiunto attraverso l’URI <http://a2.swlibero.org/nanobase_1997.html>. 371.1 Dos xBase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4150 371.2 Composition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4154 371.3 How to use nB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4156 371.4 Status line . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4157 371.5 The dot line . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4158 371.6 The menu system . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4158 371.7 The text editor DOC() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4169 371.8 The help text file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4170 371.9 Macro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4170 371.10 Data types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4174 371.11 Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4182 371.12 Delimiters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4183 371.13 Code blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4184 371.14 Standard functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4184 371.15 nB functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4232 371.16 Normal command substitution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4262 371.17 nB command substitution functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4269 371.18 RPT: the nB print function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4273 371.19 How can I... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4277 371.20 The source files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4277 nB (‘‘nano Base’’: ‘‘n’’ = ‘‘nano’’ = 10**(-9) = ‘‘very little’’) is a little Dos xBase written in Clipper 5.2 that can help to access ‘.DBF’ file created with different standards. nB is: • a dot command interpreter, • a menu driven xBase, • a xBase program interpreter. 4149 nanoBase 1997 user manual 4150 371.1 Dos xBase This section is a brief description of the functionality of a typical Dos xBase. The first purpose of a xBase program is to handle data inside a ‘.DBF’ file. These files may be indexed with the help of index files and more ‘.DBF’ files may be linked with a relation to obtain something like a relational database. 371.1.1 .DBF files ‘.DBF’ files are files organised in a table structure: ---------------------------| field1 | field2 | field3 | ---------------------------| | | | ---------------------------| | | | ---------------------------| | | | ---------------------------| | | | ---------------------------| | | | ---------------------------- record1 record2 record3 record4 record5 record6 The lines of this table are records and the columns are fields. Records are numbered starting from the first that is number 1. Columns are defined as fields and fields are distinguished by name and these names are saved inside the ‘.DBF’ file. Every field (column) can contain only one specified kind of data with a specified dimension: • ‘C’, character, originally the maximum dimension was 254 characters, minimum is 1; • ‘N’, numeric, a numeric field that can contain also sign and decimal values; • ‘D’, date, a field dedicated to date information; • ‘L’, logic, a filed that may contain only ‘T’ for True or ‘F’ for False used as a boolean variable; • ‘M’, memo, a character field with no predefined dimension, not allocated directly inside the ‘.DBF’, but inside a ‘.DBT’ file, automatically linked. No other field type is available for a typical xBase ‘.DBF’ file. To access the data contained inside a ‘.DBF’ file the following list of action may be followed: • Open a ‘.DBF’ file inside the current area, where these areas are something like file handlers. • After the ‘.DBF’ file is opened, it referenced only by the alias name that usually correspond to the original filename without extention. • Move the record pointer to the desired location. • Lock the current record to avoid access from other users. nanoBase 1997 user manual 4151 • Do some editing with the data contained inside the current record using the field names like they were variables. • Release the lock. • Move the record pointer to another desired location. • Lock the current record to avoid access from other users. • ... • Close the alias. Before you go further, you have to understand that: • A ‘.DBF’ file is opened using a free WORK AREA that may be associated to the concept of the file handler. • The ‘.DBF’ file is opened with a alias name that permit to open the same ‘.DBF’ file more times when using different alias names. • After the ‘.DBF’ file is opened, we don’t speak any more of file, but alias. • If the work area "n" is used from the alias "myAlias", speaking of work area "n" or of alias "myAlias" is the same thing. 371.1.2 Index files ‘.DBF’ files are organised with record number, that is, you can reach a specific record and not a specific information unless that you scan record by record. To obtain to "see" a ‘.DBF’ file somehow logically ordered (when physically it is not), index files are used. A index file, also called INDEX BAG, is a file that contains one or more indexes Indexes are rules by which a ‘.DBF’ file may be seen ordered. A typical index file may contain only one index. A index file may have the following extention: • ‘.NDX’, single index, dBase III and dBase III plus; • ‘.NTX’, single index, Clipper; • ‘.MBX’, multiple index, dBase IV; • ‘.CDX’, multiple index, FoxPro. Every index file may be used only in association with the ‘.DBF’ for what it was made. The problem is that normally there is no way to avoid errors when the user try to associate the right ‘.DBF’ file with the wrong index. To access the data contained inside a ‘.DBF’ file the following list of action may be followed: • Open a ‘.DBF’ file. 4152 nanoBase 1997 user manual • Open a index file. • Select a particular order. • Search for a key or move the record pointer on a different way. • Lock the current record to avoid access from other users. • Do some editing with the data contained inside the current record using the field names like they were variables. • Release the lock. • Move the record pointer to another desired location. • Lock the current record to avoid access from other users. • ... • Close the alias. Before you go further, you have to understand that: • As orders are contained inside a INDEX BAG file physically distinguished form the ‘.DBF’ file, it may happen that a ‘.DBF’ file is wrongly opened and edited without the index. In this case, the INDEX BAG is not updated and when the INDEX BAG will be opened, the records contained inside the ‘.DBF’ file may not correspond. • For the same reason, an improper program termination may result in an incomplete data update. That is: ‘.DBF’ file may be all right, INDEX BAG not. • This is why xBase programs are "weak" relational databases or they are not relational databases at all. • When troubles occurs, indexes must be rebuild. 371.1.3 Relations Many ‘.DBF’ files with indexes may be opened simultaneously. Data contained inside more ‘.DBF’ files may be somehow connected together. See the example. -----------------------------------------| Date | Time IN | Time OUT | Employee # | -----------------------------------------| xxxx | xxxxxxx | xxxxxxxx | 01 | -------->| -----------------------------------------| | yyyy | yyyyyyy | yyyyyyyy | 02 | | -----------------------------------------| | zzzz | zzzzzzz | zzzzzzzz | 01 | -------->| -----------------------------------------| [...] | | |<-----------------------------------------| | | | ---------------------------------------| | Employee # | Name | Address |.....| | ---------------------------------------|------->| 01 | aaaaaaa | aaaaaaa |.....| ---------------------------------------- nanoBase 1997 user manual 4153 | 02 | bbbbbbb | bbbbbbb |.....| ---------------------------------------| 03 | ccccccc | ccccccc |.....| ---------------------------------------- The first ‘.DBF’ file contains some data that refers to an Employee number that may appear repeated on more records. Employee informations are stored inside another ‘.DBF’ file that contains only one record for every employee. Establishing a relation from the first ‘.DBF’ file to the second, moving the record pointer of the first ‘.DBF’ file, that is the first alias, the record pointer of the second, the child alias, is moved automatically to the record containing the right data. The relation is an expression that should result in a number if the child alias is opened without index, or in a valid index key if the child alias is opened with an index. To relate two ‘.DBF’ files the following list of action may be followed: • Open the first ‘.DBF’ file. • Open a index file for the first alias. • Select a particular order. • Open the second ‘.DBF’ file. • Open a index file for the second alias. • Select a particular order. • Select the first alias. • Define a relation form the first alias and the second alias: the child alias. • Search for a key or move the record pointer of the first alias (don’t care about the Child alias). • Lock the current record to avoid access from other users. • If data contained inside the Child alias should be edited (usually it doesn’t happen), lock the current record of the Child alias. • Do some editing with the data contained inside the current record using the field names like they were variables. • Release the lock (also with the Child alias if a lock was made). • Move the record pointer to another desired location. • Lock the current record to avoid access from other users. • [...] • Release the relation. • Close the Child alias. • Close the first alias. As may be seen, relations are not saved inside files, but are obtained with lines of code. nanoBase 1997 user manual 4154 371.2 Composition nB is composed from the following files, where xx is the the version code. NBASExx1.ZIP NBASExx2.ZIP NBASExx3.ZIP NBASExx4.ZIP NBASExx5.ZIP NBASExx6.ZIP NBASExx7.ZIP EXEs for small PCs Runtime EXEs for small PCs EXEs for i286 with 2M+ DOCs EXAMPLEs SRCs for version 96.06.16 SRCs for the current version Every archive file contains: ‘COPYING.TXT’ ‘README.TXT’ ‘FILE_ID.DIZ’ GNU General Public License version 2 in Dos text format. the readme file. definition. The file ‘NBASExx1.ZIP’ contains also the following files. ‘NB.EXE’ ‘NB.HLP’ the executable program for DBFNTX and DBFNDX files, linked with RTLINK. this manual in "Help Text File" format. The file NBASExx2.ZIP contains also the following files. ‘NB.EXE’ the run-time to execute macro programs for DBFNTX and DBFNDX files handling, linked with RTLINK. The file ‘NBASExx3.ZIP’ contains also the following files. ‘NB.EXE’ ‘NB.HLP’ the executable program for DBFCDX, DBFMDX, DBFNDX and DBFNTX files, linked with EXOSPACE. the user manual in "Help Text File" format. The file ‘NBASExx4.ZIP’ contains also the following files. ‘NB.PRN’ ‘NB.RTF’ ‘NB.TXT’ ‘NB.HTM’ the user manual in printed text format. the user manual in RTF format. the user manual in ASCII text format. the user manual in HTML format. The file ‘NBASExx5.ZIP’ contains also the following files. ‘_ADDRESS.DBF’ ‘_ADDRESS.NTX’ ‘_ADDRESS.LBL’ ‘_ADDRESS.FRM’ ‘_ADDRESS.RPT’ an example database file. index file associated to ‘_ADDRESS.DBF’. a label form file used to print data contained inside ‘_ADDRESS.DBF’ . a report form file used to print data contained inside ‘_ADDRESS.DBF’ . a RPT text file used to print data contained inside ‘_ADDRESS.DBF’ . nanoBase 1997 user manual ‘_MAINMNU.&’ ‘0MAINMNU.&’ ‘0MAINMNU.NB’ ‘0MAINMNU.BAT’ ‘1ADDRESS.&’ ‘1ADDRESS.NB’ ‘2ADDRESS.&’ ‘2ADDRESS.NB’ ‘3ADDRESS.&’ ‘3ADDRESS.NB’ ‘4ADDRESS.&’ ‘4ADDRESS.NB’ ‘ABIORITM.&’ ‘ABIORITM.NB’ ‘_STUDENT.DBF’ ‘_STUDENT.NTX’ ‘_STUDSTD.DBF’ ‘_STUDENT.RPT’ ‘_STUDSTD.RPT’ ‘BSTUDENT.&’ ‘BSTUDENT.NB’ ‘CBATMAKE.&’ ‘CBATMAKE.NB’ ‘BROWSE.&’ ‘BROWSE.NB’ ‘BROWSE.BAT’ ‘MENU.&’ ‘MENU.NB’ ‘MENU.BAT’ 4155 a macro program source example of a menu that executes some others macro programs. This example is made to demonstrate how nB can execute directly a source code without compiling it. This example is made only to taste it: it is very slow and only a speedy machine can give the idea of it. a macro program source example of a menu that executes some others macro programs. It is the same as ‘_MAINMNU.&’ but it is made to start the execution of the compiled macros. compiled macro program ‘0MAINMNU.&’ a batch file to show how to run the execution of ‘0MAINMNU.NB’ a macro program source example for handling a ‘.DBF’ file containing addresses in various ways. compiled macro ‘1ADDRESS.&’ . a macro program source example for handling a ‘.DBF’ file containing addresses in various ways: a little bit more complicated than 1ADDRESS.&. compiled macro ‘2ADDRESS.&’ . a macro program source example for handling a ‘.DBF’ file containing addresses in various ways: a little bit more complicated than ‘2ADDRESS.&’ . compiled macro ‘3ADDRESS.&’. a macro program source example for handling a ‘.DBF’ file containing addresses in various ways: a little bit more complicated than ‘3ADDRESS.&’ . compiled macro ‘4ADDRESS.&’ . a macro program source example for calculating the personal bio wave. compiled macro ‘ABIORITM.&’ . a ‘.DBF’ file used inside the BSTUDENT macro example. index file used for ‘_STUDENT.DBF’ . a ‘.DBF’ file used inside the BSTUDENT macro example. a RPT text file used to print data contained inside ‘_STUDENT.DBF’ . a RPT text file used to print data contained inside ‘_STUDSTD.DBF’ . a macro program source example for students evaluation: a description about students is obtained linking other standard descriptions. compiled macro ‘BSTUDENT.&’ . a macro program source example to generate a batch file to be used to back up an entire hard disk. compiled macro ‘CBATMAKE.&’ . a macro program source example to start an automatic browse. compiled macro ‘BROWSE.&’ . batch file to start a ‘.DBF’ browse with the BROWSE macro program. a macro program source example for a Dos menu. compiled macro ‘MENU.&’ . batch file to use the MENU macro. The file ‘NBASExx6.ZIP’ contains also the following files: source code for the version 96.06.16. ‘NB.PRG’ ‘NB_REQ.PRG’ ‘NB.LNK’ the main source file for version 96.06.16. the source file containing links to all the standard functions. link file for compilation. nanoBase 1997 user manual 4156 rmake file to compile with RTLink. rmake file to compile with Exospace. rmake file to compile with RTLink defining RUNTIME to obtain a small nB runtime version. link file to compile and link a macro. rmake file to compile and link a macro. ‘NB_NRMAL.RMK’ ‘NB_EXOSP.RMK’ ‘NB_RUNTI.RMK’ ‘MACRO.LNK’ ‘MACRO.RMK’ The file ‘NBASExx7.ZIP’ contains also the following files: source code for the current version. the main source file. the source file containing links to all the Clipper functions. the source file for standard functions. the source file for other standard functions. general include file that substitutes all include file normally used for normal Clipper compilations. include file specific for nB. link file for compilation. link file for runtime compilation. rmake file to compile with RTLink. rmake file to compile with Exospace. rmake file to compile with RTLink defining RUNTIME to obtain a small nB runtime version. include file to compile and link a macro. link file to compile and link a macro. rmake file to compile and link a macro. a simple free library for mouse support under Clipper (c) 1992 Martin Brousseau. ‘NB.PRG’ ‘REQUEST.PRG’ ‘STANDARD.PRG’ ‘EXTRA.PRG’ ‘STANDARD.CH’ ‘NB.CH’ ‘NB.LNK’ ‘NB_RUNTI.LNK’ ‘NB_NRMAL.RMK’ ‘NB_EXOSP.RMK’ ‘NB_RUNTI.RMK’ ‘MACRO.CH’ ‘MACRO.LNK’ ‘MACRO.RMK’ ‘CLIPMOUSE.ZIP’ 371.3 How to use nB nB normal syntax is: nB [nB_parameters ] [macro_filename ] [macro_parameters] To run nB, just type the word "NB" and press [ Enter ] to execute. It will run in command mode, this means that it will look like an old xBASE command prompt. To run the program as a macro interpreter, type the word NB followed from the macro file name with extention (no default extention is supposed). If parameters are given, after the macro file name, these will be available inside the public variables: c_Par1, c_Par2, ..., c_Par9. c_Par0 will contain the macro file name (see the macro file BROWSE.&). nB will terminate execution when the macro terminates. These parameters are available for nB: Suppress the copyright notice. It is usefull when using nB for macro interpretation. Suppress the "Wait-Wheel" if not desired. It is the "Wheel" that appears at top-left when a macro is interpreted or other long elaborations are executed. Shows a short help. -c -w -? nB macro "compilation" syntax is: nB -m source_macro_filename [destination_macro_filename ] nanoBase 1997 user manual With the -m parameter, nB "compiles" the ASCII destination_macro_filename . 4157 source_macro_filename into 371.4 Status line nB shows a "status line" at the top of the screen when the nB command prompt or the menu system is active. It shows some important informations. | |DBFNTX ||*| 1|ADDRESS | 1/ 4|ADDRESS.NTX | | | | | | | | | | | | | | | | | | | | | Last record (7). | | | | | | | | | | | Record pointer position (6). | | | | | | | | | Active alias (5). | | | | | | | Current Work Area (4) | | | | | Deleted record appearance (3) | | | Actual default database driver (2). | Macro recorder indicator (1). | 1/ 4|ADDRESS.NTX | 1|ADDRESS | | | | | | | | | Order Tag Name (10) | | | Order number (9) | Order Bag name (8) (1) This is the place for the macro recorder indicator. The symbol used is "&". Blank means that the macro recorder is OFF; & blinking means that the macro recorder is ON; & fixed means that the macro recorder is PAUSED. (2) The name of the default database driver. It is not necessarily the database driver for the active alias; it is only the database driver that will be used for the next open/create operation. (3) An asterisk (*) at this position indicates that SET DELETED is OFF. This means that deleted records are not filtered. When a BLANK is in this place, SET DELETED is ON, so that deleted records are filtered. (4) The active work area number, that is, the area of the active alias. (5) The active alias name. Note that the alias name is not necessarily equal to the ‘.DBF’ file name. (6) The actual record pointer position for the active alias. (7) The number of records contained inside the active alias. (8) The Order Bag name; that is the index file name. (9) The order number. (10) The order tag (name). When DBFNTX database driver is used, it correspond to the Order Bag name. nanoBase 1997 user manual 4158 371.5 The dot line Starting nB without parameters, the dot line appears. This is the place where commands in form of functions may be written and executed like a old xBase. The functions written inside the command line that don’t result in an error, are saved inside a history list. This history list may be recalled with [ F2 ] and then the selected history line may be reused (eventually edited). Key [ up ]/[ down ] may be used to scroll inside the history list without showing the all list with [ F2 ]. [ Enter ] is used to tell nB to execute the written function. As the dot line is not an easy way to use such a program, a menu is available pressing [ F10 ] or [ Alt+M ]. The [ F10 ] key starts the ASSIST() menu. This menu may be started also entering the name of the function: "ASSIST()". nB includes a simple built-in text editor: DOC(). It may be started from the dot line entering "DOT()". No special key is dedicated to start this function. 371.6 The menu system The nB menu system appears differently depending on the place where it is "called". When available, the menu system appears pressing [ Alt+M ] or [ F10 ]. The Menu system is organised into horizontal menu, vertical menu, and pop-up menu. The horizontal menu contains selectable items organised horizontally: One Two Three Four Five The cursor may be moved on a different position using arrow keys [ Left ]/[ Right ]; [ Esc ] terminates the menu; [ Enter ] opens a vertical menu. The vertical menu contains selectable items organised vertically: One Two Three Four Five -----------|First | |Second | |Third | ------------ The cursor may be moved on a different position using arrow keys [ Up ]/[ Down ]; the arrow keys [ Left ]/[ Right ] change the vertical menu; [ Esc ] closes the vertical the menu; [ Enter ] starts the selected menu function. The vertical menu contains selectable items organised vertically: One Two Three Four Five -----------|First | |Second >|--------------|Third |Sub function 1| -----------|Sub function 2| ---------------- The cursor may be moved on a different position using arrow keys [ Up ]/[ Down ]; [ Esc ] closes the pop-up the menu; [ Enter ] starts the selected menu function. The following sections describe the menu system. nanoBase 1997 user manual 4159 371.6.1 Menu File The menu File contains important function on ‘.DBF’ file, indexes, relations and Replaceable database drivers. For database files are considered two aspects: the physical aspect, and the logical alias. When a ‘.DBF’ file is opened, it becomes a alias. Indexes are considered as index files and index orders. It follows a brief menu function description. Change directory Changes the actual drive and directory. File .DBF Contains a pop-up menu for ‘.DBF’ operations. New .DBF A ‘.DBF’ file is a table where columns, called Fields, must be specified and lines, called records, are added, edited and deleted by the program. Field characteristics are: NAME TYPE LENGTH DECIMAL the field name must be unique inside the same file, it is composed of letters, number and underscore (_), but it must start with a letter and it is not case sensitive. the field type determinates the type of data it can hold. is the field total length in characters; it doesn’t matter of the type of data. is the length of positions after decimal point. This information is used normally for numeric fields. In this case, take note that the DECIMAL length, together with the decimal point, will subtract space for the integer part of the number from the total LENGTH of the filed. Field Types: C Character N Numeric D Date L Logic M Memo it is a text field long LENGTH characters. it is a numeric field long LENGTH characters with DECIMAL characters for decimal positions. Note that if LENGTH is 4 and DECIMAL is 0 (zero), the field may contain integers from -999 to 9999; but if LENGTH is 4 and DECIMAL 1, the field may contain numbers from -9.9 to 99.9: two position for the integer part, one position for the decimal point and one position for decimal. it is a date field: it contains only dates; the length should not be specified as it is automatically 8. it is a logical (boolean) field: it contains only TRUE, represented by "Y" or "T", or FALSE, represented by "N" or "F". The length should not be specified as it is automatically 1. it is a character field with unknown dimension. It is recorded into a parallel file with ‘.DBT’ extention. The original ‘.DBF’ file holds a space for a pointer inside the ‘.DBT’ file. The length of a Memo field is automatically 10 and is referred to the memo pointer. After the function "NEW .DBF" is selected, a table for the field specifications appears. nanoBase 1997 user manual 4160 +--------------------------------+ | Database file structure | | | | Field Name Type Length Decimal | |--------------------------------| | | | 0| 0 | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | To navigate and to edit the table use the following keys: [ Up ]/[ Down ]/[ Left ][ Right ] [ PgUp ] [ PgDn ] [ Ctrl+PgUp ] [ Ctrl+PgDn ] [ Ctrl+Home ] [ Ctrl+End ] [ Ctrl+Enter ] [ Ctrl+F1 ] [ Ctrl+F2 ] [ Ctrl+F3 ] [ Enter ] [ Esc ] [x] move the cursor one position (up, down, left or right); move to previous screen page; move to next screen page; move to top of table; move to bottom of table; move to first column; move to last column; append a new empty line; delete (cut) the current line and save a copy into the "clipboard"; copy current line into the table "clipboard"; insert (paste) the content of the "clipboard" in the current position; start editing in the current position; terminate; any other key will be written in the current position. When the editing is terminated, press [ Esc ] and a dialog box will ask for the file name and the RDD. xBase files (.DBF) are not all equal, this way, when a new ‘.DBF’ file si created, the RDD (Replaceable Database Driver) is asked. The normal RDD is DBFNTX, the one used by Clipper. Modify .DBF structure The modification of a ‘.DBF’ file structure is a delicate matter if it contains data. In fact, it is a data transfer from a source ‘.DBF’ file to a destination ‘.DBF’ file with a different structure. This way, the destination ‘.DBF’ will be updated only for the fields with the same name of the source one. The position may be different, but names cannot be changed (not so easily). Mistakes may be dangerous, so, before doing it, it is recommended a backup copy of the original ‘.DBF’ file. Open .DBF When a ‘.DBF’ file is opened, it becomes a alias, a logical file, placed inside a work area. The same ‘.DBF’ file may be opened inside different areas with different alias names. The required information to open the file are: FILENAME ALIAS RDD SHARED the physical file name. the alias name. If not assigned, it becomes automatically the same of FILENAME without extention. the Replaceable Database Driver to use to access to this file. a logical value: TRUE means that the file will be accessible to other users, FALSE means use exclusive. nanoBase 1997 user manual READ ONLY 4161 a logical value: TRUE means that the file will be only readable and no modification will be allowed, FALSE means that no restriction on editing will be made. File .NTX Contains a pop-up menu for physical indexes operations. New .NTX / new tag If the active area is used we have an active alias. In this case a index may be created. The index is a way to see the active alias ordered without changing the physical position of records. There are two words to understand: ORDER and INDEX-BAG. The index bag is the file that contains the information on the record ordering, the order is the rule followed to order the records. A index bag may contains one or more orders depending on the Replaceable Database Driver in use. Typical ‘.NTX’ file are index bag containing only one order. Depending on the RDD in use the following field may be filled. INDEX FILENAME KEY EXPRESSION ORDER NAME FOR EXPRESSION this is the name of the index bag. the expression that defines the rule for the record ordering. this is the name to give to the order (tag) when the RDD permits to have a index bag containing more than one order. In the other case, the index bag name correspond to the order name. a FOR condition to filter records before indexing. Open index If a index file already exists, it can be associated to the active alias simply opening it. Take note that the system is not able to verify if the index belong the active alias and if it is not so a error will result. INDEX NAME is the name of the index bag file to open. Alias Contains a pop-up menu for logical databases (alias) operations. Select Only one may be the active alias and with this function the active alias may be changed choosing from the list of used areas. Selecting the area number zero, no alias is active. Display structure With this function the active alias structure may be viewed. Close active alias Selecting this function the active alias is closed. That is: the ‘.DBF’ file and eventual indexes are closed. Close all aliases With this function all Aliases are closed. Order Contains a pop-up menu for logical indexes (orders). nanoBase 1997 user manual 4162 Order list rebuild This function rebuild the indexes opened and associated to the active alias. Order set focus This function permits to change the active order selecting form the ones opened and associated to the active alias. Order list clear This function closes all orders associated to the active alias. Relation Contains a pop-up menu for relations (links with other Aliases). Set relation This function permits to establish a relation between a alias and a Child alias showing as a result a unique database. CHILD EXPRESSION is the alias name to connect to the active alias. is the relation expression that specify the rule for the relation. The value of this expression is the key to access the Child alias: if this Child alias is accessed without index, it must be the record number, if this Child alias is accessed via index, it must be a valid index key. Clear relation This function eliminates any relation that originate form the active alias. RDD default Contains a pop-up menu for Replaceable Database Driver defaults. Show actual RDD default It simply shows the actual Replaceable Database Driver. Set default RDD Select a new default Replaceable Database Driver. 371.6.2 Menu Edit The menu Edit contains functions to access data from the active alias (the actual area). View This function permits you to view the active alias with eventual relations as a table. No edit is allowed. To navigate the table use the following keys. [ Enter ] [ PgUp ] [ PgDn ] [ Ctrl+PgUp ] [ Ctrl+PgDn ] [ Ctrl+Home ] [ Ctrl+End ] start field editing. show previous screen page. show next screen page. show top of alias. show bottom of file. show the first column. show last column. nanoBase 1997 user manual 4163 Edit/browse This function permits you to edit the active alias with eventual relations as a table. To navigate and edit the table use the following keys. [ Enter ] [ PgUp ] [ PgDn ] [ Ctrl+PgUp ] [ Ctrl+PgDn ] [ Ctrl+Home ] [ Ctrl+End ] [ Ctrl+Enter ] [ Ctrl+F2 ] [ Ctrl+F3 ] [ Ctrl+F4 ] [ Ctrl+Y ] [ Ctrl+Del ] start field editing. show previous screen page. show next screen page. show top of alias. show bottom of file. show the first column. show last column. append a new empty record. copy the current record. append and paste a record. paste a previously copied record, overwriting the content of the current one. delete or recall the current record. delete or recall the current record. When a memo field is edited: [ Esc ] [ Ctrl+Y ] [ Ctrl+W ] cancel and close the memo window. line delete. save and close the memo window. Replace The content of a Field of the active alias may be replaced with an expression. The required data is: FIELD TO REPLACE NEW VALUE EXPRESSION WHILE EXPRESSION FOR EXPRESSION the Field name to be replaced. the expression that obtain the new value for the selected Field. the WHILE condition expression: the replacement continue until this expression results True. The constant ‘.T.’ is ever True and is the default. the FOR condition expression: the replacement is made for all records that satisfy the condition. The constant ‘.T.’ is ever True and is the default. Recall The records signed for deletion (deleted but still there), may be recalled (undeleted). The required data is: WHILE EXPRESSION FOR EXPRESSION the WHILE condition expression: the record recall continue until this expression results True. The constant ‘.T.’ is ever True and is the default. the FOR condition expression: the record recall is made for all records that satisfy the condition. The constant ‘.T.’ is ever True and is the default. Delete Deletes (sign for deletion) a group of record depending on the required conditions. The required data is: nanoBase 1997 user manual 4164 WHILE EXPRESSION FOR EXPRESSION the WHILE condition expression: the record deletion continue until this expression results True. The constant ‘.T.’ is ever True and is the default. the FOR condition expression: the record deletion is made for all records that satisfy the condition. The constant ‘.T.’ is ever True and is the default. Pack This function eliminates definitely records previously deleted (signed for deletion). It may work only if the active alias was opened in exclusive mode. 371.6.3 Menu Report The menu Report contains functions for data report (print). In particular, label files ‘.LBL’ and report file ‘.RPT’ may be created and used for printing. There is also another way to print, with the RPT() system that is available inside the nB internal editor DOC(). DBGOTOP() Moves the record pointer for the active alias at the first logical record. New label With this function can be created a standard label file (.LBL under the dBaseIII standard). Labels may be printed in more than one column and can contain 16 lines maximum. The label data is the following. REMARK HEIGHT WIDTH MARGIN LINES SPACES ACROSS LINE 1 LINE n LINE 16 a label remark that will not be printed. the label vertical dimension. the label horizontal dimension. the left margin in characters. the vertical spacing between labels. the horizontal spacing between labels in characters. the number of label columns. The first line inside labels. The n-th line inside labels. The 16th line inside labels. The number of lines inside the labels depend on the HEIGHT and the maximum value is 16. The label lines can contain constant string and/or string expressions. See the example below. nanoBase 1997 user manual 4165 Margin <--> XXXXXXX|XXXXXXXX XXXXXXX|XXXXXXXX XXXX Height XXXX XXXXXXX|XXXXXXXX XXXXXXX|XXXXXXXX XX XX XX XX XX Line Line Line Line Line 1 2 3 4 5 XXXXXX XXXXXX XXXXXX XXXXXX XXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX <--- Width ----> XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX | | Lines <--> | Spaces XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX | | | | | | | | | +---------------- Across ---------------+ Modify label This function permits you to modify a label file. Label form This function permits you to print labels with the data provided by the active alias: one label each record. The following data is required. LABEL FILENAME WHILE FOR the label filename. the WHILE condition: the label printing goes on as long as this condition remain True. the FOR condition: only the records from the active alias that satisfy the condition are used for the label print. New report This function permits you to create a standard report form file (.FRM under the dBaseIII standard). The informations to create a ‘.FRM’ file are divided into two parts: the head and groups; the columns. The first part: head and groups, requires the folliwing informations: PAGE WIDTH LINES PER PAGE LEFT MARGIN DOUBLE SPACED? PAGE EJECT BEFORE PRINT? PAGE EJECT AFTER PRINT? PLAIN PAGE? PAGE HEADER GROUP HEADER the page width in characters. the usable lines per per page. the left margin in characters. double spaced print, yes or no. form feed before print, yes or no. form feed after print, yes or no. plain page, yes or no. the page header, max 4 lines (the separation between one line and the other is obtained writing a semicolon, ";"). the group title. nanoBase 1997 user manual 4166 GROUP EXPRESSION SUMMARY REPORT ONLY? PAGE EJECT AFTER GROUP? SUB GROUP HEADER SUB GROUP EXPRESSION the group expression (when it changes, the group changes) only totals and no columns, yes or no. form feed when the group changes, yes or no. sub group title. the sub group expression. The second part: columns, requires the following informations structured in table form: COLUMN HEADER CONTENT WIDTH DEC. TOTALS column head description (it can contain 4 lines separated with a semicolon). the column expression. the column width. the decimal length for numeric columns. totals to be calculated, yes or no (usefull only for numeric columns). To navigate and to edit the table use the following keys: [ Up ]/[ Down ]/[ Left ][ Right ] [ PgUp ] [ PgDn ] [ Ctrl+PgUp ] [ Ctrl+PgDn ] [ Ctrl+Home ] [ Ctrl+End ] [ Ctrl+Enter ] [ Ctrl+F1 ] [ Ctrl+F2 ] [ Ctrl+F3 ] [ Enter ] [ Esc ] [x] move the cursor one position (up, down, left or right); move to previous screen page; move to next screen page; move to top of table; move to bottom of table; move to first column; move to last column; append a new empty line; delete (cut) the current line and save a copy into the "clipboard"; copy current line into the table "clipboard"; insert (paste) the content of the "clipboard" in the current position; start editing in the current position; terminate; any other key will be written in the current position. When the editing is terminated, press [ Esc ] and a dialog box will ask for the name to give to the report form file. Modify report This function permits you to modify a standard report form file (.FRM under the dBaseIII standard). Report form This function permits you to print a report form with the data provided by the active alias. The following data is required. REPORT FORM FILENAME WHILE FOR the label filename. the WHILE condition: the form printing goes on as long as this condition remain True. the FOR condition: only the records from the active alias that satisfy the condition are used for the report form print. Create/modify/print text This function activates the text editor. nanoBase 1997 user manual 4167 371.6.4 Menu HTF The menu Htf helps on creating and accessing the "Help Text Files". This name, help text file, is just the name given to it. A text (Ascii) file prepared like this manual may be transformed into a "Help Text File" that is a simple text with pointers. Open help text file This function permits to open a Help Text File and browse it. The Help Text File name is required. New help text file This function permits to create a new "Help Text File" that is a help file under the nB style. The source is an Ascii file where three kind of information are available: Normal text, Indexes and pointers. Indexes and Pointers are word or phrases delimited with user defined delimiters; indexes are placed inside the text to indicate an argument, pointers are placed inside the text to indicate a reference to indexes. Inside this manual, indexes are delimited with ## and ##, so the titles are here indexes; pointers are delimited with < and >. Only one index per line is allowed, only one pointer per line is allowed. The Delimiters used do identify indexes and pointers are user defined; the _start_ identifier symbol can be equal to the _end_ identifier symbol. The symbols used for indexes cannot be used for the pointers. So, the informations required are: SOURCE TEXT FILENAME DESTINATION FILENAME INDEX START CODE INDEX END CODE POINTER START CODE POINTER END CODE the filename of the text source file. the filename of the destination Help Text File (suggested ‘.HLP’ extention). the index start symbol; suggested ##. the index end symbol; suggested ##. the pointer start symbol; suggested <. the pointer end symbol; suggested >. New HTML file This function permits to create a new HTML file form a text file formatted to obtain a HTF file. The informations required are: SOURCE TEXT FILENAME DESTINATION FILENAME INDEX START CODE INDEX END CODE POINTER START CODE POINTER END CODE HTML TITLE the filename of the text source file. the filename of the destination Help Text File (suggested ‘.HLP’ extention). the index start symbol; suggested ##. the index end symbol; suggested ##. the pointer start symbol; suggested <. the pointer end symbol; suggested >. the title for the html page. nanoBase 1997 user manual 4168 371.6.5 Menu Macro The menu Macro helps on creating macros (programs) with a macro recorder, a macro "compiler" and a macro executor. Start recording This function simply starts or pause the macro recording. The menu items that end with "&", may be recorded by this macro recorder. Save recording A recorded macro may be saved into a ASCII file that may be later modified or simply used as it is. The filename is requested. Erase recording While recording or when the macro recorder is paused, it is possible to erase all previous recording with this function. Edit recording While recording or when the macro recorder is paused, it is possible to edit all previous recording, for example adding more comments or simply to see what the recorder does. Macro compilation A macro file (a program) contained inside a ASCII file, may be compiled into a different file format to speed up execution. The source filename and the destination filename are requested. Load + execute macro A macro file (a program) in ASCII form or compiled, may be executed. A macro file may require some parameters. This function asks for the macro filename to start and the possible parameter to pass to it. 371.6.6 Menu Info The menu Info is the information menu. ABOUT MANUAL BROWSE [F1] HELP [F3] ALIAS INFO [F5] SET OUTPUT TO a brief copyright notice. starts the browse of ‘NB.HLP’ , the nB Help Text File manual if it is present in the current directory or it is found in the PATH (the Dos SET PATH). [F1] reminder. [F3] reminder. It shows all the available information on the active alias. [F5] reminder. It defines the output peripheral or file. 371.6.7 Menu Doc This menu actually appears only inside the DOC() function, the nB text editor. New It starts the editing of a new empty text. nanoBase 1997 user manual 4169 Open It opens for editing a new textfile. Save It saves the text file under editing. Save as It saves the text file under editing asking for a new name. Set output to It permits to change the default output peripheral: the default is the screen. Print as it is It prints on the output peripheral the content of the text as it is. Print with RPT() once It prints on the output peripheral the content of the text only once replacing possible text variables. Print with RPT() std It prints on the output peripheral the content of the text repeating this print for every record contained inside the archive alias. Exit DOC() Terminates the use of DOC() the text/document editing/print function. 371.7 The text editor DOC() The function Doc() activates a simple text editor usefull to build some simple reports. Inside this function a menu is available and is activated pressing menu is part of the nB menu system. [ Alt+M ] or [ F10 ]. The Doc() DOC() may handle text files of a teorical maximum of 64K. DOC() may be particularly useful to create formatted text with variables identified by CHR(174) and CHR(175) delimiters: when an active alias exists, [ F2 ] gives a list of insertable fields. [ Esc ] [ F1 ] [ F2 ] [ up ] / [ Ctrl+E ] [ down ] / [ Ctrl+X ] [ left ] / [ Ctrl+S ] [ right ] / [ Ctrl+D ] [ Ctrl+right ] / [ Ctrl+A ] [ Ctrl+left ] / [ Ctrl+F ] [ Home ] [ End ] [ Ctrl+Home ] [ Ctrl+End ] [ PgUp ] [ PgDn ] [ Ctrl+PgUp ] Exit DOC(). Call the help. Field list. Line up. Line down. Character left. Character right. Word left. Word right. Line start. Line end. Top window. Bottom window. Previous window. Next window. Document start. nanoBase 1997 user manual 4170 [ Ctrl+PgDn ] [ Del ] [ Backspace ] [ Tab ] [ Ins ] [ Enter ] [ Ctrl+Y ] [ Ctrl+T ] [ F10 ] / [ Alt+M ] End document. Delete character (right). Delete character Left. Insert tab. Toggle insert/overwrite. Next line. Delete line. Delete word right. DOC() menu. 371.8 The help text file nB provides a basic hypertext system to build simple help files. A source text file with "indexes" and "pointers" to indexes is translated into a "help text file" (a ‘.DBF’ file); then, this file is browsed by nB. The source file can have a maximum line width of 80 characters; each line can terminate with CR or CR+LF. "Indexes" are string delimited by index delimiters (default "##"); "pointers" are string delimited by pointer delimiters (default "<" and ">") and refers to indexes. Inside a text, indexes must be unique; pointers can be repeated anywhere. A text can contain a maximum of 4000 indexes. Inside this manual, titles are delimited with "##" as they are indexes; strings delimited with "<" and ">" identify a reference to a title with the same string. To browse a previously created Help Text File, use the following keys: [ Esc ] [ UpArrow ] [ DownArrow ] [ PgUp ] [ PgDn ] [ Ctrl+PgUp ] [ Ctrl+PgDn ] [ Enter ] [ <- ] [ -> ] [ Shift+F3 ] [ F3 ] Exit. Move cursor up. Move cursor down. Move cursor PageUp. Move cursor Pagedown. Move cursor Top. Move cursor Bottom. Select a reference (pointer). Go to previous selected reference (pointer). Go to next selected reference (pointer). Search for a new pattern. Repeat previous search. 371.9 Macro nB can execute (run) macro files. There may be three kind of macro files: ASCII (usually with .& extention); "compiled" (usually with .NB extention); EXE files (compiled with Clipper and linked). "Compiled" macro files are executed faster then the ASCII source files. EXE macro files are the fastest. nanoBase 1997 user manual 4171 371.9.1 Macro statements The statements recognised from nB are very similar to Clipper, with some restrictions. Note that: the FOR statement is not included; there is no function declaration; procedure calls cannot transfer variables; only public variables are allowed. PROCEDURE Procedures are the basic building blocks of a nB macro. Procedures are visible only inside the current macro file. The procedure structure is as follows: PROCEDURE procedure_name statements ... RETURN statements ... ENDPROCEDURE [ ] A procedure definition begins with a PROCEDURE declaration followed with the procedure_name and ends with ENDPROCEDURE. Inside the PROCEDURE - ENDPROCEDURE declaration are placed the executable statements which are executed when the procedure is called. Inside the PROCEDURE - ENDPROCEDURE declaration, the RETURN statement may appear. In this case, encountering this RETURN statement, the procedure execution is immediately terminated and control is passed to the statement following the calling one. The procedure definition do not permit to receive parameters from the calling statement. DO PROCEDURE There is only one way to call a procedure: DO PROCEDURE procedure_name When the statement DO PROCEDURE is encountered, the control is passed to the begin of the called PROCEDURE. After the PROCEDURE execution, the control is returned to the statement following DO PROCEDURE. The procedure call do not permit to send parameters to the procedure. BEGIN SEQUENCE The BEGIN SEQUENCE - END structure permits to define a sequence of operation that may be broken. Inside nB, this control structure is useful only because there is the possibility to break the execution and pass control over the end of it. This way, encountering BREAK means: "go to end". BEGIN SEQUENCE statements ... BREAK statements ... END [ ] Inside nB, error exception handling is not supported. DO CASE This is a control structure where only the statements following a True CASE condition are executed. nanoBase 1997 user manual 4172 When the DO CASE statement is encountered, the following CASE statements are tested. The first time that a condition returns True, the CASE’s statements are executed and then control is passed over the END case. That is: only one CASE is taken into consideration. If no condition is True, the statements following OTHERWISE are executed. DO CASE CASE lCondition1 statements ... CASE lCondition2 statements ... OTHERWISE statements ... END [ [ ] ] WHILE The structure WHILE - END defines a loop based on a condition: the loop is repeated until the condition is True. The loop execution may be broken with the EXIT statement: it transfer control after the END while. The LOOP statement may be use to repeat the loop: it transfer the control to the beginning of the loop. WHILE lCondition statements ... EXIT statements ... LOOP statements ... END [ [ ] ] IF The IF - END control structure executes a section of code if a specified condition is True. The structure can also specify alternative code to execute if the condition is False. IF lCondition1 statements ... ELSE statements ... END [ ] 371.9.2 Variable declaration Inside nB, variables are created using a specific function: MEMPUBLIC( "cVarName " ) For example, MEMPUBLIC( "Name" ) creates the variable Name. The scope of the created variable is global and there is no way to restrict the visibility of it. When a variable is no more needed or desired, it can be released: MEMRELEASE( "cVarName " ) The variable declaration do not defines the variable type. Every variable may receive any kind of data; that is that the type depends on the type of data contained. nanoBase 1997 user manual 4173 371.9.3 Macro structure A nB macro must be organised as follow. There may be two situations: Macros with procedures and macros without procedures. Macro with procedures: PROCEDURE procedure_name_1 statements ... RETURN statements ... ENDPROCEDURE PROCEDURE procedure_name_2 statements ... RETURN statements ... ENDPROCEDURE [ ] [ ] ... ... DO PROCEDURE procedure_name_n Macro without procedures: statements ... statements ... statements ... statements ... statements ... nB Macros may be compiled with Clipper. To do so, the first structure example must be changed as follows: #INCLUDE MACRO.CH DO PROCEDURE procedure_name_nth ... PROCEDURE procedure_name_1 statements ... RETURN statements ... ENDPROCEDURE PROCEDURE procedure_name_2 statements ... RETURN statements ... ENDPROCEDURE [ ] [ ] ... ... To compile a macro with Clipper, the macro file name can be changed into ‘MACRO.PRG’ and RTLINK MACRO.RMK [Enter] should be started. 371.9.4 Macro comments A nB Macro source file can contain comments. only the "//" comment is recognised! This way: * and /*...*/ will generate errors! 4174 nanoBase 1997 user manual ATTENTION: to simplify the macro interpretation, lines such as this: qqout( "You can’t do that // you can’t do that!" ) will generate an error as the interpreter will read only: qqout( "You can’t do that Sorry! 371.9.5 Macro long lines split Inside a nB macro, long lines may be splitted using ";" (semicolon). Please note that: lines can only be splitted and not joined; a resulting command line cannot be longer then 254 characters. 371.9.6 The macro recorder Inside the functions ASSIST() and DOC() is available the Macro recorder menu. When a macro recording is started, a "&" appears on the left side of the status bar. It it blinks, the recording is active, if it is stable, the recording is paused. The macro recording is not exactly a step-by-step recording of all action taken, but a translation (as good as possible) of what you have done. The macro recorder is able to record only the menu functions that terminates with the "&" symbol and all what is inserted at the dot command line. The macro recording can be viewed and edited during the recording. The macro recording can be saved into a text file (a macro file). 371.10 Data types The data types supported in the nB macro language are the same as Clipper: Array Character Code Block Numeric Date Logical Memo NIL 371.10.1 Character The character data type identifies character strings of a fixed length. The character set corresponds to: CHR(32) through CHR(255) and the null character, CHR(0). Valid character strings consist of zero or more characters with a theoretical maximum of 65535 characters. The real maximum dimension depends on the available memory. Character string constants are formed by enclosing a valid string of characters within a designed pair of delimiters. There are three possible delimiter pairs: nanoBase 1997 user manual 4175 two single quotes like ‘’string_constant ’’; two double quotes like ‘"string_constant "’; left and right square brackets like ‘[string_constant ]’. These three different kind of delimiters are available to resolve some possible problems: I don’t want it -> "I don’t want it" She said, "I love hin" -> ’She said, "I love hin"’ He said, "I don’t want it" -> [He said, "I don’t want it"] The following table shows all operations available inside nB for character data types. These operations act on one or more character expressions and the result is not necessarily a character data type. + == !=, <>, # < <= > >= := $ ALLTRIM() ASC() AT() CTOD() DESCEND() EMPTY() ISALPHA() ISDIGIT() ISLOWER() ISUPPER() LEFT() LEN() LOWER() LTRIM() PADC() PADL() PADR() RAT() RIGHT() RTRIM() SOUNDEX() SPACE() STRTRAN() STUFF() SUBSTR() TRANSFORM() UPPER() VAL() VALTYPE() Concatenate. Concatenate without intervening spaces. Compare for exact equity. Compare for inequity. Compare for sorts before Compare for sorts before or same as. Compare for sorts after. Compare for sorts after or same as. In line assign. Test for substring existence. Remove leading and trailing spaces. Convert to numeric ASCII code equivalent. Locate substring position. Convert to date. Convert to complemented form. Test for null or blank string. Test for initial letter. Test for initial digit. Test for initial lowercase letter. Test for initial uppercase letter. Extract substring form the left. Compute string length in characters. Convert letters to lowercase. Remove leading spaces. Pad with leading and trailing spaces. Pad with leading spaces. Pad with trailing spaces. Locate substring position starting from the right. Extract substring form the right. Remove trailing spaces. Convert to soundex equivalent. Create a blank string of a defined length. Search and replace substring. Replace substring. Extract substring. Convert to formatted string. Convert letters to uppercase Convert to numeric. Evaluates data type directly. nanoBase 1997 user manual 4176 371.10.2 Memo The memo data type is used to represent variable length character data that can only exist in the form of a database field. Memo fields are not stored inside the main database file (.DBF) but inside a separate file (.DBT). A memo field can contain up to 65535 characters, that is the same maximum dimension of character fields. In fact, originally xBases, couldn’t have character string longer than 254 characters. As here memo fields are very similar to long character strings, you may forget that there is a difference. All the operations that may be applied to character strings, may be used with memo fields; the following functions may be use especially for memo fields or long character strings. HARDCR() MEMOEDIT() MEMOLINE() MEMOREAD() MEMOTRAN() MEMOWRIT() MLCOUNT() MLPOS() Replace soft with hard carriage returns. Edit contents. Extract a line of a text. Read form a disk text file. Replace soft and hard carriage returns. Write to disk text file. Count lines. Compute position. 371.10.3 Date The date data type is used to represent calendar dates. Supported dates are from 0100.01.01 to 2999.12.31 and null or blank date. The appearance of a date is controlled from SETVERB("DATEFORMAT"). The default is "dd/mm/yyyy" and it may easily changed for example with SETVERB("DATEFORMAT", "MM/DD/YYYY") to the US standard. There is no way to represent date constants; these must be replaced with the CTOD() function. For example if the date 11/11/1995 is to be written, the right way is: CTOD( "11/11/1995" ) The character string "11/11/1995" must respect the date format defined as before explained. The function CTOD() will accept only valid dates, and null dates: CTOD( "" ) A null date is ever less than any other valid date. The following table shows all operations available inside nB for date data types. These operations act on one or more date expressions and the result is not necessarily a character data type. + == !=, <>, # < <= > >= Add a number of days to a date. Subtract days to a date. Compare for equity. Compare for inequity. Compare for earlier Compare for earlier or same as. Compare for later. Compare for later or same as. nanoBase 1997 user manual := CDOW() CMONTH() DAY() DESCEND() DOW() DTOC() DOTOS() EMPTY() MONTH() VALTYPE() YEAR() 4177 In line assign. Compute day of week name. Compute month name. Extract day number. Convert to complemented form. Compute day of week. Convert to character string with the format defined with SETVERB( "DATEFORMAT" ). Convert to character string in sorting format (YYYYMMDD). Test for null date. Extract month number. Evaluates data type directly. Extract entire year number, including century. 371.10.4 Numeric The numeric data type identifies real number. The theoretical range is form 10^-308 to 10^308 but the numeric precision is guaranteed up to 16 significant digits, and formatting a numeric value for display is guaranteed up to a length of 32 (30 digits, a sign, and a decimal point). That is: numbers longer than 32 bytes may be displayed as asterisks, and digits other then most 16 significant ones are displayed as zeroes. Numeric constants are written without delimiters. The following are valid constant numbers: 12345 12345.678 -156 +1256.789 -.789 If a numeric constant is delimited like character strings, it becomes a character string. The following table shows all operations available inside nB for numeric data types. These operations act on one or more numeric expressions and the result is not necessarily a numeric data type. + * / % ^, ** == !=, <>, # < >= > >= := ABS() CHR() DESCEND() EMPTY() Add or Unary Positive. Subtract or Unary Negative. Multiply. Divide. Modulus. Exponentiate. Compare for equity. Compare for inequity. Compare for less than. Compare for less than or equal. Compare for greater than. Compare for greater than or equal. In line assign. Compute absolute value. Convert to ASCII character equivalent. Convert to complemented form. Test for zero. nanoBase 1997 user manual 4178 EXP() INT() LOG() MAX() MIN() ROUND() SQRT() STR() TRANSFORM() VALTYPE() Exponentiate with e as the base. Convert to integer. Compute natural logarithm. Compute maximum. Compute minimum. Round up or down() Compute square root. Convert to character. Convert to formatted string. Evaluates data type directly. Number appearence may be affected by SETVERB("FIXED") and consequently by SETVERB("DECIMALS"). If SETVERB("FIXED") is True, numbers are displayed with a fixed decimal position. The number of decimal positions is defined by SETVERB("DECIMALS"). For that reason, the default is SETVERB("FIXED", .F.) and SETVERB("DECIMALS", 2), that is, no fixed decimal position, but if they will be activated, the default is two decimal digits. 371.10.5 Logical The logical data type identifies Boolean values. Logical constants are: ‘.T.’ ‘.F.’ True. False. When editing a logical field, inputs may be: y, Y, t, T n, N, f, F for True for False The following table shows all operations available inside nB for logical data types. These operations act on one or more logical expressions and the result is not necessarily a logical data type. .AND. .OR. .NOT. or ! == !=, <>, or # And. Or. Negate. Compare for equity. Compare for inequity. Comparing two logical values, False (‘.F.’) is always less than True (‘.T.’). 371.10.6 NIL NIL is not properly a data type, it represent the value of an uninitialised variable. Inside nB (like what it happens inside Clipper), variables are not declared with the data type that they will contain. This means that a variable can contain any kind of data. In fact, nB variables are pointer to data and a pointer to "nothing" is NIL. NIL may be used as constant for assignment or comparing purpose: nanoBase 1997 user manual 4179 NIL Fields (database fields) cannot contain NIL. The following table shows all operations available inside nB for the NIL data type. Except for these operations, attempting to operate on a NIL results in a runtime error. == !=, <>, # < <= > >= := EMPTY() VALTYPE() Compare for equity. Compare for inequity. Compare for less than. Compare for less than or equal. Compare for greater than. Compare for greater than or equal. In line assign. Test for NIL. Evaluates data type directly. For the purpose of comparison, NIL is the only value that is equal to NIL. All other values are greater than NIL. Variables are created inside nB with MEMPUBLIC(). This function creates variables which will be automatically initialised to NIL. 371.10.7 Array The array data type identifies a collection of related data items that share the same name. Each value in an array is referred to as an element. Array elements can be of any data type except memo (memo is available only inside database fields). For example the first element can be a character string, the second a number, the third a date and so on. Arrays can contain other arrays and code blocks as elements. The variable containing the array does not contains the entire array, but the reference to it. When the NIL type was described, it was cleared that variables doesn’t contains real data, but pointer to data. But this happens in a transparent way, that is that when the a variable is assigned to another (for example A := B) the variable receiving the assignment will receive a pointer to a new copy of the source data. This is not the same with arrays: assigning to a variable an array, will assign to that variable a pointer to the same source array and not to a new copy of it. If arrays are to be duplicated, the ACLONE() function is to be used. An array constant may be expressed using curly brackets {}. See the examples below. A := { "first_element ", "second_element ", "third_element " } With this example, the variable A contain the reference to an array with three element containing character string. A[1] == "first_element " A[2] == "second_element " A[3] == "third_element " Arrays may contain also no element: empty array and may be expressed as: {} nanoBase 1997 user manual 4180 The array element is identified by a number enclosed with square brackets, following the variable name containing the reference to the array. The first array element is one. If an array contains arrays, we obtain a multidimensional array. For example: A := { { 1, 2 }, { 3, 4 }, { 5, 6 } } is equivalent to the following table. 1 2 3 4 5 6 With this example, the variable A contain the reference to a bidimensional array containing numbers. A[1,1] or A[1][1] contains 1 A[1,2] or A[1][2] contains 2 A[2,1] or A[2][1] contains 3 and so on. As arrays may contain mixed data, it is the user who have to handle correctly the element numbers. For example: A := { "hello", { 3, 4 }, 1234 } A[1] == "hello" A[2] == reference to { 3, 4 } A[3] == 1234 A[2,1] or A[2][1] contains 3 A[2,2] or A[2][2] contains 4 A[1,1] is an error! The following table shows all operations available inside nB for arrays. := AADD() ACLONE() ACOPY() ADEL() AFILL() AINS() ARRAY() ASCAN() ASIZE() ASORT() EMPTY() VALTYPE() In line assign. Add dynamically an element to an array. Create a copy of an array. Copy element by element an array to another. Delete one element inside an array. Fill all array elements with a value. Insert an element inside an array. Creates an array of empty elements. Scan the array elements. Resize an array. Sort the array elements. Test for no elements. Evaluates data type directly. nanoBase 1997 user manual 4181 371.10.8 Code block The code block data type identifies a small piece of executable program code. A code block is something like a little user defined function where only a sequence of functions or assignments may appear: no loops, no IF ELSE END. A code block may receive argument and return a value after execution, just like a function. The syntax is: { | [argument_list ] | exp_list } That is: the argument_list is optional; the exp_list may contain one or more expressions separated with a comma. For example, calling the following code block will give the string "hello world" as result. { || "hello world" } The following code block require a numeric argument an returns the number passed as argument incremented: { | n | n+1 } The following code block requires two numeric arguments and returns the sum of the two square radix: { | nFirst, nSecond | SQRT(nFirst) + SQRT(nSecond) } But code blocks may contains more expressions and the result of the execution of the code block is the result of the last expression. The following code block executes in sequence some functions and give ever "hello world" as a result. { | a, b | functionOne(a), functionTwo(b), "hello world" } To start the execution of a code block a function is used: EVAL() For example, a code block is assigned to a variable and then executed. B := { || "hello world" } EVAL( B ) == "hello world" Another example with a parameter. B := { | n | n+1 } EVAL( B, 1 ) == 2 Another example with two parameters. B := { | nFirst, nSecond | SQRT(nFirst) + SQRT(nSecond) } EVAL( B, 2, 4 ) == 20 And so on. The following table shows some operations available inside nB for code blocks: many functions use code blocks as argument. := AEVAL() BCOMPILE() DBEVAL() In line assign. Evaluate (execute) a code block for each element in an array. Convert (compile) a character string into a code block. Evaluate (execute) a code block for each record in the active alias. 4182 EVAL() VALTYPE() nanoBase 1997 user manual Evaluate a code block once. Evaluates data type directly. 371.11 Operators Here is a list with a brief description of the operators available inside nB. cString1 $ cString2 Substring comparison. If cString1 is contained inside cString2 the result is true (‘.T.’). nNumber1 % nNumber2 Modulus. The result is the remainder of nNumber1 divided by nNuber2 . () Function or grouping indicator. nNumber1 * nNumber2 Multiplication. nNumber1 ** nNumber2 nNumber1 ^ nNumber2 Exponentiation. nNumber1 + nNumber2 dDate + nNumber Addition, unary positive. cString1 + cString2 String concatenation. The result is a string beginning with the content of cString1 and following with the content of cString2 . nNumber1 - nNumber2 dDate1 - dDate2 dDate - nNumber Subtraction, unary negative. cString1 - cString2 String concatenation. The result is a string containing cString1 after trimming trailing blanks and cString2 . idAlias ->idField FIELD->idVar MEMVAR->idVar Alias assignment. The alias operator implicitly SELECTs the idAlias before evaluating idField . When the evaluation is complete, the original work area is SELECTed again. lCondition1 .AND. lCondition2 nanoBase 1997 user manual Logical AND. .NOT. lCondition Logical NOT. lCondition1 .OR. lCondition2 Logical OR. nNumber1 / nNumber2 Division. object:message [(argument list)] Send. idVar := exp Inline assign. exp1 <= exp2 Less than or equal. exp1 <> exp2 Not equal. exp1 = exp2 Equal. exp1 == exp2 Exactly equal. exp1 > exp2 Greater than. exp1 >= exp2 Greater than or equal. @idVar Pass-by-reference. [] aArray [nSubscript , ...] aArray [nSubscript1 ][nSubscript2 ] ... Array element indicator. 371.12 Delimiters Here is the delimiter list recognised from nB. { exp_list } Literal array delimiters. { |param_list | exp_list } Code block delimiters. 4183 nanoBase 1997 user manual 4184 "cString " ’cString ’ [cString ] String delimiters. 371.13 Code blocks A code block is a sequence of function, assignments and constant like the following: sqrt(10) nResult := 10 * nIndex Suppose that the above sequence of operations has a meaning for you. We want to create a box containing this sequence of operation. This box is contained inside a variable: bBlackBox := { || sqrt(10), nResult := 10 * nIndex } Note the comma used as separator. Now bBlackBox contains the small sequence seen before. To execute this sequence, the function EVAL() is used: EVAL(bBlackBox) The execution of the code block gives a result: the value of the last operation contained inside the code block. In this case it is the result of 10*nIndex . For that reason, if the execution of the code block must give a fixed result, it can terminate with a constant. A code block may receive parameters working like a function. Try to imagine that we need to do the following. function multiply( nVar1, nVar2 ) return nVar * nVar2 endfunction A code block that does the same is: bMultiply := { | nVar1, nVar2 | nVar1 * nVar2 } To evaluate it, for example trying to multiply 10 * 5: nResult := EVAL( bMultiply, 10, 5 ) and nResult will contain 50. 371.14 Standard functions With nB all Clipper standard functions may be used. Here follows a short description. AADD() Array add AADD(aTarget , expValue ) ⇒ Value aTarget expValue is the array to add a new element to. is the value assigned to the new element. It increases the actual length of the target array by one. The newly created array element is assigned the value specified by expValue . nanoBase 1997 user manual 4185 ABS() Absolute ABS(nExp ) ⇒ nPositive is the numeric expression to evaluate. nExp ABS() returns a number representing the absolute value of its argument. ACLONE() Array clone ACLONE(aSource ) ⇒ aDuplicate is the array to duplicate. aSource ACLONE() returns a duplicate of aSource . ACOPY() Array copy ACOPY(aSource , aTarget , nStart , nCount [ ] [ ], [nTargetPos]) ⇒ aTarget is the array to copy elements from. is the array to copy elements to. is the starting element position in the aSource array. If not specified, the default value is one. is the number of elements to copy from the aSource array beginning at the nStart position. If nCount is not specified, all elements in aSource beginning with the starting element are copied. is the starting element position in the aTarget array to receive elements from aSource . If not specified, the default value is one. aSource aTarget nStart nCount nTargetPos ACOPY() is an array function that copies elements from the aSource array to the aTarget array. The aTarget array must already exist and be large enough to hold the copied elements. ADEL() Array delete ADEL(aTarget , nPosition ) ⇒ aTarget is the array to delete an element from. is the position of the target array element to delete. aTarget nPosition ADEL() is an array function that deletes an element from an array. The contents of the specified array element is lost, and all elements from that position to the end of the array are shifted up one element. The last element in the array becomes NIL. AEVAL() Array evaluation AEVAL(aArray , bBlock , nStart , nCount [ ] [ ]) ⇒ aArray nanoBase 1997 user manual 4186 is the array to be evaluated. is a code block to execute for each element encountered. is the starting element. If not specified, the default is element one. is the number of elements to process from nStart . If not specified, the default is all elements to the end of the array. aArray bBlock nStart nCount AEVAL() is an array function that evaluates a code block once for each element of an array, passing the element value and the element index as block parameters. The return value of the block is ignored. All elements in aArray are processed unless either the nStart or the nCount argument is specified. AFILL() Array fill AFILL(aTarget , expValue , nStart , nCount ) ⇒ aTarget [ ] [ ] is the array to fill. is the value to place in each array element. It can be an expression of any valid data type. is the position of the first element to fill. If this argument is omitted, the default value is one. is the number of elements to fill starting with element nStart . If this argument is omitted, elements are filled from the starting element position to the end of the array. aTarget expValue nStart nCount AFILL() is an array function that fills the specified array with a single value of any data type (including an array, code block, or NIL) by assigning expValue to each array element in the specified range. AINS() Array insert AINS(aTarget , nPosition ) ⇒ aTarget is the array into which a new element will be inserted. is the position at which the new element will be inserted. aTarget nPosition AINS() is an array function that inserts a new element into a specified array. The newly inserted element is NIL data type until a new value is assigned to it. After the insertion, the last element in the array is discarded, and all elements after the new element are shifted down one position. ALERT() ALERT( cMessage , cMessage aOptions [aOptions] ) ⇒ nChoice is the message text displayed, centered, in the alert box. If the message contains one or more semicolons, the text after the semicolons is centered on succeeding lines in the dialog box. defines a list of up to 4 possible responses to the dialog box. ALERT() returns a numeric value indicating which option was chosen. If the Esc key is pressed, the value returned is zero. The ALERT() function creates a simple modal dialog. The user can respond by moving a highlight bar and pressing the Return or SpaceBar keys, nanoBase 1997 user manual 4187 or by pressing the key corresponding to the first letter of the option. If aOptions is not supplied, a single "Ok" option is presented. ALIAS() [ ALIAS( nWorkArea ]) ⇒ cAlias is any work area number. nWorkArea ALIAS() returns the alias of the specified work area as a character string. If nWorkArea is not specified, the alias of the current work area is returned. If there is no database file in USE for the specified work area, ALIAS() returns a null string (""). ALLTRIM() ALLTRIM(cString ) ⇒ cTrimmedString is the character expression to trim. cString ALLTRIM() returns a character string with leading and trailing spaces removed. ARRAY() ARRAY(nElements [, ] nElements ... ) ⇒ aArray is the number of elements in the specified dimension. nElements ARRAY() is an array function that returns an uninitialized array with the specified number of elements and dimensions. ASC() ASCII ASC(cExp ) ⇒ nCode is the character expression to convert to a number. cExp ASC() returns an integer numeric value in the range of zero to 255 , representing the ASCII value of cExp . ASCAN() Array scan ASCAN(aTarget , expSearch , nStart , nCount ) ⇒ nStoppedAt [ aTarget expSearch nStart nCount ] [ ] is the array to scan. is either a simple value to scan for, or a code block. If expSearch is a simple value it can be character, date, logical, or numeric type. is the starting element of the scan. If this argument is not specified, the default starting position is one. is the number of elements to scan from the starting position. If this argument is not specified, all elements from the starting element to the end of the array are scanned. ASCAN() returns a numeric value representing the array position of the last element scanned. If expSearch is a simple value, ASCAN() returns the position of the first matching nanoBase 1997 user manual 4188 element, or zero if a match is not found. If expSearch is a code block, ASCAN() returns the position of the element where the block returned true (‘.T.’). ASIZE() Array size ASIZE(aTarget , nLength ) ⇒ aTarget is the array to grow or shrink. is the new size of the array. aTarget nLength ASIZE() is an array function that changes the actual length of the aTarget array. The array is shortened or lengthened to match the specified length. If the array is shortened, elements at the end of the array are lost. If the array is lengthened, new elements are added to the end of the array and assigned NIL. ASORT() Array sort [ ] ] [ ] ASORT(aTarget , nStart , nCount , bOrder ) ⇒ aTarget [ aTarget nStart nCount bOrder is the array to sort. is the first element of the sort. If not specified, the default starting position is one. is the number of elements to sort. If not specified, all elements in the array beginning with the starting element are sorted. is an optional code block used to determine sorting order. If not specified, the default order is ascending. ASORT() is an array function that sorts all or part of an array containing elements of a single data type. Data types that can be sorted include character, date, logical, and numeric. If the bOrder argument is not specified, the default order is ascending. Each time the block is evaluated, two elements from the target array are passed as block parameters. The block must return true (‘.T.’) if the elements are in sorted order. AT() AT(cSearch , cTarget ) ⇒ nPosition cSearch cTarget is the character substring for which to search. is the character string to search. AT() returns the position of the first instance of cSearch within cTarget as an integer numeric value. If cSearch is not found, AT() returns zero. AT() is a character function used to determine the position of the first occurrence of a character substring within another string. ATAIL() Array TAIL ATAIL(aArray ) ⇒ Element aArray is the array. ATAIL() is an array function that returns the highest numbered element of an array. It can nanoBase 1997 user manual 4189 [ ] be used in applications as shorthand for aArray LEN(aArray ) when you need to obtain the last element of an array. BIN2I() Binary to integer BIN2I(cSignedInt ) ⇒ nNumber cSignedInt is a character string in the form of a 16-bit signed integer number--least significant byte first. BIN2I() returns an integer obtained converting the first two byte contained inside cSignedInt . BIN2L() Binary to long BIN2L(cSignedInt ) ⇒ nNumber cSignedInt is a character string in the form of a 32-bit signed integer number--least significant byte first. BIN2L() returns an integer obtained from the first tour characters contained in cSignedInt . BIN2W() Binary to word BIN2W(cUnsignedInt ) ⇒ nNumber cUnsignedInt is a character string in the form of a 16-bit unsigned integer number--least significant byte first. BIN2W() returns an integer obtained from the first two characters contained in cSignedInt . BOF() Begin of file BOF() ⇒ lBoundary BOF() returns true (‘.T.’) after an attempt to SKIP backward beyond the first logical record in a database file; otherwise, it returns false (‘.F.’). If there is no database file open in the current work area, BOF() returns false (‘.F.’). If the current database file contains no records, BOF() returns true (‘.T.’). CDOW() Character day of week CDOW(dExp ) ⇒ cDayName dExp is the date value to convert. CDOW() returns the name of the day of the week as a character string. The first letter is uppercase and the rest of the string is lowercase. For a null date value, CDOW() returns a null string (""). CHR() Character nanoBase 1997 user manual 4190 CHR(nCode ) ⇒ cChar is an ASCII code in the range of zero to 255. nCode CHR() returns a single character value whose ASCII code is specified by nCode . CMONTH() Character month CMONTH(dDate) ⇒ cMonth is the date value to convert. dDate CMONTH() returns the name of the month as a character string from a date value with the first letter uppercase and the rest of the string lowercase. For a null date value, CMONTH() returns a null string (""). COL() Column COL() ⇒ nCol COL() is a screen function that returns the current column position of the cursor. The value of COL() changes whenever the cursor position changes on the screen. COLORSELECT() COLORSELECT(nColorIndex ) ⇒ NIL is a number corresponding to the ordinal positions in the current list of color attributes, as set by SETCOLOR(). nColorIndex COLORSELECT() activates the specified color pair from the current list of color attributes (established by SETCOLOR()). CTOD() Character to date CTOD(cDate) ⇒ dDate is a character string consisting of numbers representing the month, day, and year separated by any character other than a number. The month, day, and year digits must be specified in accordance with the SET DATE format. If the century digits are not specified, the century is determined by the rules of SET EPOCH. cDate CTOD() returns a date value. If cDate is not a valid date, CTOD() returns an empty date. CURDIR() Current directory [ ] CURDIR( cDrivespec ) ⇒ cDirectory cDrivespec specifies the letter of the disk drive to query. If not specified, the default is the current DOS drive. CURDIR() returns the current DOS directory of the drive specified by cDrivespec as a nanoBase 1997 user manual 4191 character string without either leading or trailing backslash (\) characters. DATE() DATE() ⇒ dSystemDate DATE() returns the system date as a date value. DAY() DAY(dDate) ⇒ nDay is a date value to convert. dDate DAY() returns the day number from dDate. DBAPPEND() [ DBAPPEND( lReleaseRecLocks ]) lReleaseRecLocks ⇒ NIL is a logical data type that if true (‘.T.’), clears all pending record locks, then appends the next record. If lReleaseRecLocks is false (‘.F.’), all pending record locks are maintained and the new record is added to the end of the Lock List. The default value of lReleaseRecLocks is true (‘.T.’). DBAPPEND() adds a new empty record to the active alias. DBCLEARFILTER() DBCLEARFILTER() ⇒ NIL DBCLEARFILTER() clears the logical filter condition, if any, for the current work area. DBCLEARINDEX() DBCLEARINDEX() ⇒ NIL DBCLEARINDEX() closes any active indexes for the active alias. DBCLEARRELATION() DBCLEARRELATION() ⇒ NIL DBCLEARRELATION() clears any active relations for the active alias. DBCLOSEALL() DBCLOSEALL() ⇒ NIL DBCLOSEALL() releases all occupied work areas from use. It is equivalent to calling DBCLOSEAREA() on every occupied work area. Attention: DBCLOSEALL() cannot be used inside a "compiled" macro as this will stop the macro execution. In substitution, DBCLOSE() should be used. DBCLOSEAREA() DBCLOSEAREA() ⇒ NIL DBCLOSEAREA() releases the current work area from use. nanoBase 1997 user manual 4192 DBCOMMIT() DBCOMMIT() ⇒ NIL DBCOMMIT() causes all updates to the current work area to be written to disk. All updated database and index buffers are written to DOS and a DOS COMMIT request is issued for the database (.dbf) file and any index files associated with the work area. Inside a network environment, DBCOMMIT() makes database updates visible to other processes. To insure data integrity, issue DBCOMMIT() before an UNLOCK operation. DBCOMMITALL() DBCOMMITALL() ⇒ NIL DBCOMMITALL() causes all pending updates to all work areas to be written to disk. It is equivalent to calling DBCOMMIT() for every occupied work area. DBCREATE() DBCREATE(cDatabase, aStruct , cDatabase aStruct cDriver [cDriver]) ⇒ NIL is the name of the new database file, with an optional drive and directory, specified as a character string. If specified without an extension (.dbf) is assumed. is an array that contains the structure of cDatabase as a series of subarrays, one per field. Each subarray contains the definition of each field’s attributes and has the following structure: aStruct[n][1] == cName aStruct[n][2] == cType aStruct[n][3] == nLength aStruct[n][4] == nDecimals specifies the replaceable database driver (RDD) to use to process the current work area. cDriver is name of the RDD specified as a character expression. DBCREATE() is a database function that creates a database file from an array containing the structure of the file. DBCREATEINDEX() DBCREATEINDEX(cIndexName , cKeyExpr , bKeyExpr , ⇒ NIL cIndexName cKeyExpr bKeyExpr lUnique [lUnique]) is a character value that specifies the filename of the index file (order bag) to be created. is a character value that expresses the index key expression in textual form. is a code block that expresses the index key expression in executable form. is an optional logical value that specifies whether a unique index is to be created. If lUnique is omitted, the current global _SET_UNIQUE setting is used. DBCREATEINDEX() creates an index for the active alias. If the alias has active indexes, they are closed. DBDELETE() DBDELETE() ⇒ NIL DBDELETE() marks the current record as deleted (*). Records marked for deletion can be filtered using SET DELETED or removed from the file using the PACK command. nanoBase 1997 user manual 4193 DBEVAL() DB evaluate DBEVAL(bBlock , bForCondition , bWhileCondition , nNextRecords , nRecord , lRest ) ⇒ NIL [ [ [ [ [ ] ] ] ] ] bBlock bForCondition bWhileCondition nNextRecords nRecord lRest is a code block to execute for each record processed. the FOR condition expressed as code block. the WHILE condition expressed as code block. is an optional number that specifies the number of records to process starting with the current record. It is the same as the NEXT clause. is an optional record number to process. If this argument is specified, bBlock will be evaluated for the specified record. This argument is the same as the RECORD clause. is an optional logical value that determines whether the scope of DBEVAL() is all records, or, starting with the current record, all records to the end of file. DBEVAL() is a database function that evaluates a single block for each record within the active alias. DBFILTER() DBFILTER() ⇒ cFilter BFILTER() returns the filter condition defined in the current work area as a character string. If no FILTER has been SET, DBFILTER() returns a null string (""). DBGOBOTTOM() DBGOBOTTOM() ⇒ NIL DBGOBOTTOM() moves to last logical record in the active alias. DBGOTO() DBGOTO(nRecordNumber ) ⇒ NIL nRecordNumber is a numeric value that specifies the record number of the desired record. DBGOTO() moves to the record whose record number is equal to nRecordNumber . If no such record exists, the work area is positioned to LASTREC() + 1 and both EOF() and BOF() return true (‘.T.’). DBGOTOP() DBGOTOP() ⇒ NIL DBGOTOP() moves to the first logical record in the current work area. DBRECALL() DBRECALL() ⇒ NIL DBRECALL() causes the current record to be reinstated if it is marked for deletion. DBREINDEX() DBREINDEX() ⇒ NIL nanoBase 1997 user manual 4194 DBREINDEX() rebuilds all active indexes associated with the active alias. DBRELATION() DBRELATION(nRelation ) ⇒ cLinkExp is the position of the desired relation in the list of active alias relations. nRelation DBRELATION() returns a character string containing the linking expression of the relation specified by nRelation . If there is no RELATION SET for nRelation , DBRELATION() returns a null string (""). DBRLOCK() DB record lock [ DBRLOCK( nRecNo ]) ⇒ lSuccess is the record number to be locked. The default is the current record. nRecNo DBRLOCK() is a database function that locks the record identified by nRecNo or the current record. DBRLOCKLIST() DBRLOCKLIST() ⇒ aRecordLocks DBRLOCKLIST() returns a one-dimensional array of the locked records in the active alias. DBRSELECT() DB relation select DBRSELECT(nRelation ) ⇒ nWorkArea is the position of the desired relation in the list of current work area relations. nRelation DBRSELECT() returns the work area number of the relation specified by nRelation as an integer numeric value. If there is no RELATION SET for nRelation , DBRSELECT() returns zero. DBRUNLOCK() DB relation unlock [ DBRUNLOCK( nRecNo ]) ⇒ NIL is the record number to be unlocked. The default is all previously locked records. nRecNo DBRUNLOCK() is a database function that unlocks the record identified by nRecNo or all locked records. DBSEEK() DBSEEK(expKey , expKey [lSoftSeek ]) ⇒ lFound is a value of any type that specifies the key value associated with the desired record. nanoBase 1997 user manual 4195 is an optional logical value that specifies whether a soft seek is to be performed. This determines how the work area is positioned if the specified key value is not found. If lSoftSeek is omitted, the current global _SET_SOFTSEEK setting is used. lSoftSeek DBSEEK() returns true (‘.T.’) if the specified key value was found; otherwise, it returns false (‘.F.’). DBSELECTAREA() DBSELECTAREA(nArea | cAlias ) ⇒ NIL is a numeric value between zero and 250, inclusive, that specifies the work area being selected. is a character value that specifies the alias of a currently occupied work area being selected. nArea cAlias DBSELECTAREA() causes the specified work area to become the current work area. All subsequent database operations will apply to this work area unless another work area is explicitly specified for an operation. DBSETDRIVER() [ ] DBSETDRIVER( cDriver ) ⇒ cCurrentDriver cDriver is an optional character value that specifies the name of the database driver that should be used to activate and manage new work areas when no driver is explicitly specified. DBSETDRIVER() returns the name of the current default driver. DBSETFILTER() DBSETFILTER(bCondition , bCondition cCondition [cCondition]) ⇒ NIL is a code block that expresses the filter condition in executable form. is a character value that expresses the filter condition in textual form. If cCondition is omitted, the DBSETFILTER() function will return an empty string for the work area. DBSETFILTER() sets a logical filter condition for the current work area. When a filter is set, records which do not meet the filter condition are not logically visible. That is, database operations which act on logical records will not consider these records. The filter expression supplied to DBSETFILTER() evaluates to true (‘.T.’) if the current record meets the filter condition; otherwise, it should evaluate to false (‘.F.’). DBSETINDEX() DBSETINDEX(cOrderBagName ) ⇒ NIL cOrderBagName is a character value that specifies the filename of the index file (index bag) to be opened. DBSETINDEX() is a database function that adds the contents of an Order Bag into the Order List of the current work area. Any Orders already associated with the work area continue to be active. If the newly opened Order Bag is the only Order associated with nanoBase 1997 user manual 4196 the work area, it becomes the controlling Order; otherwise, the controlling Order remains unchanged. If the Order Bag contains more than one Order, and there are no other Orders associated with the work area, the first Order in the new Order Bag becomes the controlling Order. DBSETORDER() DBSETORDER(nOrderNum ) ⇒ NIL is a numeric value that specifies which of the active indexes is to be the controlling index. nOrderNum DBSETORDER() controls which of the active alias’ active indexes is the controlling index. DBSETRELATION() DBSETRELATION(nArea | cAlias , bExpr , [cExpr]) ⇒ NIL is a numeric value that specifies the work area number of the child work area. is a character value that specifies the alias of the child work area. is a code block that expresses the relational expression in executable form. is an optional character value that expresses the relational expression in textual form. If cExpr is omitted, the DBRELATION() function returns an empty string for the relation. nArea cAlias bExpr cExpr DBSETRELATION() relates the work area specified by nArea or cAlias (the child work area), to the current work area (the parent work area). Any existing relations remain active. DBSKIP() [ ] DBSKIP( nRecords ) ⇒ NIL is the number of logical records to move, relative to the current record. A positive value means to skip forward, and a negative value means to skip backward. If nRecords is omitted, a value of 1 is assumed. nRecords DBSKIP() moves either forward or backward relative to the current record. Attempting to skip forward beyond the last record positions the work area to LASTREC() + 1 and EOF() returns true (‘.T.’). Attempting to skip backward beyond the first record positions the work area to the first record and BOF() returns true (‘.T.’). DBSTRUCT() DBSTRUCT() ⇒ aStruct DBSTRUCT() returns the structure of the current database file in an array whose length is equal to the number of fields in the database file. Each element of the array is a subarray containing information for one field. The subarrays have the following format: aStruct[n][1] aStruct[n][2] aStruct[n][3] aStruct[n][4] == == == == cName cType nLength nDecimals If there is no database file in USE in the current work area, DBSTRUCT() returns an empty array ({}). nanoBase 1997 user manual 4197 DBUNLOCK() DBUNLOCK() ⇒ NIL DBUNLOCK() releases any record or file locks obtained by the current process for the current work area. DBUNLOCK() is only meaningful on a shared database in a network environment. DBUNLOCKALL() DBUNLOCKALL() ⇒ NIL DBUNLOCKALL() releases any record or file locks obtained by the current process for any work area. DBUNLOCKALL() is only meaningful on a shared database in a network environment. DBUSEAREA() [ ] [ ] [ ] ] DBUSEAREA( lNewArea , cDriver , cName , lShared , lReadonly ) ⇒ NIL [ lNewArea cDriver cName xcAlias lShared lReadonly [xcAlias], is an optional logical value. A value of true (‘.T.’) selects the lowest numbered unoccupied work area as the current work area before the use operation. If lNewArea is false (‘.F.’) or omitted, the current work area is used; if the work area is occupied, it is closed first. is an optional character value. If present, it specifies the name of the database driver which will service the work area. If cDriver is omitted, the current default driver is used. specifies the name of the database (.dbf) file to be opened. is an optional character value. If present, it specifies the alias to be associated with the work area. The alias must constitute a valid identifier. A valid xcAlias may be any legal identifier (i.e., it must begin with an alphabetic character and may contain numeric or alphabetic characters and the underscore). If xcAlias is omitted, a default alias is constructed from cName . is an optional logical value. If present, it specifies whether the database (.dbf) file should be accessible to other processes on a network. A value of true (‘.T.’) specifies that other processes should be allowed access; a value of false (‘.F.’) specifies that the current process is to have exclusive access. If lShared is omitted, the current global _SET_EXCLUSIVE setting determines whether shared access is allowed. is an optional logical value that specifies whether updates to the work area are prohibited. A value of true (‘.T.’) prohibits updates; a value of false (‘.F.’) permits updates. A value of true (‘.T.’) also permits read-only access to the specified database (.dbf) file. If lReadonly is omitted, the default value is false (‘.F.’). DBUSEAREA() opens the specified database (.DBF). DBDELETE() DELETED() ⇒ lDeleted DELETED() returns true (‘.T.’) if the current record is marked for deletion; otherwise, it returns false (‘.F.’). If there is no database file in USE in the current work area, DELETED() returns false (‘.F.’). DESCEND() DESCEND(exp ) ⇒ ValueInverted nanoBase 1997 user manual 4198 is any valid expression of character, date, logical, or numeric type. exp DESCEND() returns an inverted expression of the same data type as the exp , except for dates which return a numeric value. A DESCEND() of CHR(0) always returns CHR(0). DEVOUT() Device output DEVOUT(exp , [cColorString]) exp cColorString ⇒ NIL is the value to display. is an optional argument that defines the display color of exp . DEVOUT() is a full-screen display function that writes the value of a single expression to the current device at the current cursor or printhead position. DEVOUTPICT() Device output picture DEVOUTPICT(exp , cPicture , [cColorString]) exp cPicture cColorString ⇒ NIL is the value to display. defines the formatting control for the display of exp . is an optional argument that defines the display color of exp . DEVOUTPICT() is a full-screen display function that writes the value of a single expression to the current device at the current cursor or printhead position. DEVPOS() Device position DEVPOS(nRow , nCol ) ⇒ NIL nRow , nCol are the new row and column positions of the cursor or printhead. DEVPOS() is an environment function that moves the screen or printhead depending on the current DEVICE. DIRECTORY() DIRECTORY(cDirSpec , cDirSpec cAttributes [cAttributes]) ⇒ aDirectory identifies the drive, directory and file specification for the directory search. Wildcards are allowed in the file specification. If cDirSpec is omitted, the default value is *.*. specifies inclusion of files with special attributes in the returned information. cAttributes is a string containing one or more of the following characters: H Include hidden files S Include system files D Include directories V Search for the DOS volume label only Normal files are always included in the search, unless you specify V. nanoBase 1997 user manual 4199 DIRECTORY() returns an array of subarrays, with each subarray containing information about each file matching cDirSpec . The subarray has the following structure: aDirectory[n][1] aDirectory[n][2] aDirectory[n][3] aDirectory[n][4] aDirectory[n][5] == == == == == cName cSize dDate cTime cAttributes If no files are found matching cDirSpec or if cDirSpec is an illegal path or file specification, DIRECTORY() returns an empty ({}) array. DISKSPACE() [ ] DISKSPACE( nDrive ) ⇒ nBytes is the number of the drive to query, where one is drive A, two is B, three is C, etc. The default is the current DOS drive if nDrive is omitted or specified as zero. nDrive DISKSPACE() returns the number of bytes of empty space on the specified disk drive as an integer numeric value. DISPBOX() Display box DISPBOX(nTop , nLeft , nBottom , nRight , cnBoxString , cColorString ) ⇒ NIL [ ] [ nTop , nLeft , nBottom , nRight cnBoxString cColorString ] define the coordinates of the box. is a numeric or character expression that defines the border characters of the box. If specified as a numeric expression, a value of 1 displays a single-line box and a value of 2 displays a double-line box. All other numeric values display a singleline box. If cnBoxString is a character expression, it specifies the characters to be used in drawing the box. This is a string of eight border characters and a fill character. defines the display color of the box that is drawn. DISPBOX() is a screen function that draws a box at the specified display coordinates in the specified color. DISPOUT() Display out DISPOUT(exp , [cColorString]) exp cColorString cColorString ⇒ NIL is the value to display. is an optional argument that defines the display color of exp . is a character expression containing the standard color setting. DISPOUT() is a simple output function that writes the value of a single expression to the display at the current cursor position. This function ignores the SET DEVICE setting; output always goes to the screen. DOW() Day of week DOW(dDate) ⇒ nDay nanoBase 1997 user manual 4200 is a date value to convert. dDate DOW() returns the day of the week as a number between zero and seven. The first day of the week is one (Sunday) and the last day is seven (Saturday). If dDate is empty, DOW() returns zero. DTOC() Date to character DTOC(dDate) ⇒ cDate is the date value to convert. dDate DTOC() returns a character string representation of a date value. The return value is formatted in the current date format. A null date returns a string of spaces equal in length to the current date format. DTOS() Date to sort DTOS(dDate) ⇒ cDate is the date value to convert. dDate DTOS() returns a character string eight characters long in the form, yyyymmdd. When dDate is a null date (CTOD("")), DTOS() returns a string of eight spaces. EMPTY() EMPTY(exp ) ⇒ lEmpty exp is an expression of any data type. EMPTY() returns true (‘.T.’) if the expression results in an empty value; otherwise, it returns false (‘.F.’): Array Character/Memo Numeric Date Logical NIL {} Spaces, tabs, CR/LF, or "" 0 CTOD("") ‘.F.’ NIL EOF() End of file EOF() ⇒ lBoundary EOF() returns true (‘.T.’) when an attempt is made to move the record pointer beyond the last logical record in a database file; otherwise, it returns false (‘.F.’). If there is no database file open in the current work area, EOF() returns false (‘.F.’). If the current database file contains no records, EOF() returns true (‘.T.’). EVAL() Code block evaluation EVAL(bBlock , [BlockArg_list ]) ⇒ LastBlockValue nanoBase 1997 user manual 4201 is the code block to evaluate. is a list of arguments to send to the code block before it is evaluated. bBlock BlockArg_list To execute or evaluate a code block, call EVAL() with the block value and any parameters. The parameters are supplied to the block when it is executed. Code blocks may be a series of expressions separated by commas. When a code block is evaluated, the returned value is the value of the last expression in the block. EXP() Exponent EXP(nExponent ) ⇒ nAntilogarithm is the natural logarithm for which a numeric value is to be calculated. nExponent EXP() returns a numeric value that is equivalent to the value e raised to the specified power. FCLOSE() File close FCLOSE(nHandle ) ⇒ lError is the file handle obtained previously from FOPEN() or FCREATE(). nHandle FCLOSE() is a low-level file function that closes binary files and forces the associated DOS buffers to be written to disk. If the operation fails, FCLOSE() returns false (‘.F.’). FERROR() can then be used to determine the reason for the failure. FCOUNT() Field count FCOUNT() ⇒ nFields FCOUNT() returns the number of fields in the database file in the active alias as an integer numeric value. If there is no database file open, FCOUNT() returns zero. FCREATE() Field create FCREATE(cFile , cFile nAttribute [nAttribute ]) ⇒ nHandle is the name of the file to create. If the file already exists, its length is truncated to zero without warning. is the binary file attribute, the default value is zero. nAttribute = 0 Normal (default) nAttribute = 1 Read-only nAttribute = 2 Hidden nAttribute = 4 System FCREATE() returns the DOS file handle number of the new binary file in the range of zero to 65,535. If an error occurs, FCREATE() returns -1 and FERROR() is set to indicate an error code. nanoBase 1997 user manual 4202 FERASE() File erase FERASE(cFile ) ⇒ nSuccess cFile is the name (with or without path) of the file to be deleted from disk. FERASE() is a file function that deletes a specified file from disk. FERASE() returns -1 if the operation fails and zero if it succeeds. FERROR() File error FERROR() ⇒ nErrorCode FERROR() returns the DOS error from the last file operation as an integer numeric value. If there is no error, FERROR() returns zero. nErrorCode value 0 2 3 4 5 6 8 15 19 21 23 29 30 32 33 Meaning Successful File not found Path not found Too many files open Access denied Invalid handle Insufficient memory Invalid drive specified Attempted to write to a write-protected disk Drive not ready Data CRC error Write fault Read fault Sharing violation Lock Violation FERROR() is a low-level file function that indicates a DOS error after a file function is used. FIELDBLOCK() FIELDBLOCK(cFieldName ) ⇒ bFieldBlock cFieldName is the name of the field to which the set-get block will refer. FIELDBLOCK() returns a code block that, when evaluated, sets (assigns) or gets (retrieves) the value of the given field. If cFieldName does not exist in the current work area, FIELDBLOCK() returns NIL. FIELDGET() FIELDGET(nField ) ⇒ ValueField nField is the ordinal position of the field in the record structure for the current work area. FIELDGET() returns the value of the specified field. If nField does not correspond to the position of any field in the current database file, FIELDGET() returns NIL. nanoBase 1997 user manual 4203 FIELDNAME() FIELDNAME(nPosition ) ⇒ cFieldName nPosition is the position of a field in the database file structure. FIELDNAME() returns the name of the specified field as a character string. If nPosition does not correspond to an existing field in the current database file or if no database file is open in the current work area, FIELDNAME() returns a null string (""). FIELDPOS() Field position FIELDPOS(cFieldName ) ⇒ nFieldPos cFieldName is the name of a field in the current or specified work area. FIELDPOS() returns the position of the specified field within the list of fields associated with the current or specified work area. If the current work area has no field with the specified name, FIELDPOS() returns zero. FIELDPUT() FIELDPUT(nField , expAssign ) ⇒ ValueAssigned nField expAssign is the ordinal position of the field in the current database file. is the value to assign to the given field. The data type of this expression must match the data type of the designated field variable. FIELDPUT() is a database function that assigns expAssign to the field at ordinal position nField in the current work area. This function allows you to set the value of a field using its position within the database file structure rather than its field name. FIELDWBLOCK() Field work area block FIELDWBLOCK(cFieldName , nWorkArea ) ⇒ bFieldWBlock cFieldName nWorkArea is the name of the field specified as a character string. is the work area number where the field resides specified as a numeric value. FIELDWBLOCK() returns a code block that, when evaluated, sets (assigns) or gets (retrieves) the value of cFieldName in the work area designated by nWorkArea . If cFieldName does not exist in the specified work area, FIELDWBLOCK() returns NIL. FILE() FILE(cFilespec ) ⇒ lExists cFilespec is in the current default directory and path. It is a standard file specification that can include the wildcard characters * and ? as well as a drive and path reference. FILE() returns true (‘.T.’) if there is a match for any file matching the cFilespec pattern; otherwise, it returns false (‘.F.’). nanoBase 1997 user manual 4204 FLOCK() File lock FLOCK() ⇒ lSuccess FLOCK() tries to lock the active alias and returns true (‘.T.’) if it succeeds; otherwise, it returns false (‘.F.’). FOPEN() File open FOPEN(cFile , [nMode]) cFile nMode ⇒ nHandle is the name of the file to open including the path if there is one. is the requested DOS open mode indicating how the opened file is to be accessed. The open mode is composed of the sum of two elements: the Open mode and the Sharing mode. Open mode: 0 Open for reading (default) 1 Open for writing 2 Open for reading or writing Sharing mode: 0 Compatibility mode (default) 16 Exclusive use 32 Prevent others from writing 48 Prevent others from reading 64 Allow others to read or write FOPEN() returns the file handle of the opened file in the range of zero to 65,535. If an error occurs, FOPEN() returns -1. FOUND() FOUND() ⇒ lSuccess FOUND() returns true (‘.T.’) if the last search command was successful; otherwise, it returns false (‘.F.’). FREAD() File read FREAD(nHandle , @cBufferVar , nBytes ) ⇒ nBytes nHandle cBufferVar nBytes is the file handle obtained from FOPEN(), FCREATE(), or predefined by DOS. is the name of an existing and initialized character variable used to store data read from the specified file. The length of this variable must be greater than or equal to nBytes . cBufferVar must be passed by reference and, therefore, must be prefaced by the pass-by-reference operator (@). is the number of bytes to read into the buffer. FREAD() tries to read nBytes of the binary file nHandle inside cBufferVar . It returns the number of bytes successfully read as an integer numeric value. A return value less than nBytes or zero indicates end of file or some other read error. FREADSTR() File read string nanoBase 1997 user manual 4205 FREADSTR(nHandle , nBytes ) ⇒ cString nHandle nBytes is the file handle obtained from FOPEN(), FCREATE(), or predefined by DOS. is the number of bytes to read, beginning at the current DOS file pointer position. FREADSTR() returns a character string up to 65,535 (64K) bytes. A null return value ("") indicates an error or end of file. FREADSTR() is a low-level file function that reads characters from an open binary file beginning with the current DOS file pointer position. Characters are read up to nBytes or until a null character (CHR(0)) is encountered. All characters are read including control characters except for CHR(0). The file pointer is then moved forward nBytes . If nBytes is greater than the number of bytes from the pointer position to the end of the file, the file pointer is positioned to the last byte in the file. FRENAME() File rename FRENAME(cOldFile , cNewFile ) ⇒ nSuccess cOldFile cNewFile is the name of the file to rename, including the file extension. A drive letter and/or path name may also be included as part of the filename. is the new name of the file, including the file extension. A drive letter and/or path name may also be included as part of the name. FRENAME() returns -1 if the operation fails and zero if it succeeds. FSEEK() File seek FSEEK(nHandle , nOffset , nHandle nOffset nOrigin nOrigin == 0 nOrigin == 1 nOrigin == 2 [nOrigin]) ⇒ nPosition is the file handle obtained from FOPEN(), FCREATE(), or predefined by DOS. is the number of bytes to move the file pointer from the position defined by nOrigin . It can be a positive or negative number. A positive number moves the pointer forward, and a negative number moves the pointer backward in the file. defines the starting location of the file pointer before FSEEK() is executed. The default value is zero, representing the beginning of file. If nOrigin is the end of file, nOffset must be zero or negative. Seek from beginning of file Seek from the current pointer position Seek from end of file FSEEK() returns the new position of the file pointer relative to the beginning of file (position 0) as an integer numeric value. This value is without regard to the original position of the file pointer. FSEEK() is a low-level file function that moves the file pointer forward or backward in an open binary file without actually reading the contents of the specified file. The beginning position and offset are specified as function arguments, and the new file position is returned. nanoBase 1997 user manual 4206 FWRITE() File write FWRITE(nHandle , cBuffer , [nBytes]) ⇒ nBytesWritten is the file handle obtained from FOPEN(), FCREATE(), or predefined by DOS. is the character string to write to the specified file. indicates the number of bytes to write beginning at the current file pointer position. If omitted, the entire content of cBuffer is written. nHandle cBuffer nBytes FWRITE() returns the number of bytes written as an integer numeric value. If the value returned is equal to nBytes , the operation was successful. If the return value is less than nBytes or zero, either the disk is full or another error has occurred. GETENV() Get environment GETENV(cEnvironmentVariable ) ⇒ cString is the name of the DOS environment variable. When specifying this argument, you can use any combination of upper and lowercase letters; GETENV() is not case- sensitive. cEnvironmentVariable GETENV() returns the contents of the specified DOS environment variable as a character string. If the variable cannot be found, GETENV() returns a null string (""). HARDCR() Hard carriage return HARDCR(cString ) ⇒ cConvertedString is the character string or memo field to convert. cString HARDCR() is a memo function that replaces all soft carriage returns (CHR(141)) with hard carriage returns (CHR(13)). It is used to display long character strings and memo fields containing soft carriage returns with console commands. HEADER() HEADER() ⇒ nBytes HEADER() returns the number of bytes in the header of the current database file as an integer numeric value. If no database file is in use, HEADER() returns a zero (0). I2BIN() Integer to binary I2BIN(nInteger ) ⇒ cBinaryInteger nInteger is an integer numeric value to convert. Decimal digits are truncated. I2BIN() returns a two-byte character string containing a 16-bit binary integer. IF() [I]IF(lCondition , expTrue , expFalse ) ⇒ Value nanoBase 1997 user manual 4207 is a logical expression to be evaluated. is the value, a condition-expression, of any data type, returned if lCondition is true (‘.T.’). is the value, of any date type, returned if lCondition is false (‘.F.’). This argument need not be the same data type as expTrue . lCondition expTrue expFalse IF() returns the evaluation of expTrue if lCondition evaluates to true (‘.T.’) and expFalse if it evaluates to false (‘.F.’). INDEXEXT() Index extention INDEXEXT() ⇒ cExtension INDEXEXT() returns the default index file extension by determining which database driver is currently linked. INDEXKEY() INDEXKEY(nOrder ) ⇒ cKeyExp is the ordinal position of the index in the list of index files opened by the last USE...INDEX or SET INDEX TO command for the current work area. A zero value specifies the controlling index, without regard to its actual position in the list. nOrder INDEXKEY() returns the key expression of the specified index as a character string. If there is no corresponding index or if no database file is open, INDEXKEY() returns a null string (""). INDEXORD() Index order INDEXORD() ⇒ nOrder INDEXORD() returns an integer numeric value. The value returned is equal to the position of the controlling index in the list of open indexes for the current work area. A value of zero indicates that there is no controlling index and records are being accessed in natural order. If no database file is open, INDEXORD() will also return a zero. INKEY() Input key [ INKEY( nSeconds nSeconds ]) ⇒ nInkeyCode specifies the number of seconds INKEY() waits for a keypress. You can specify the value in increments as small as one-tenth of a second. Specifying zero halts the program until a key is pressed. If nSeconds is omitted, INKEY() does not wait for a keypress. INKEY() returns an integer numeric value from -39 to 386, identifying the key extracted from the keyboard buffer. If the keyboard buffer is empty, INKEY() returns zero. INKEY() returns values for all ASCII characters, function, Alt+function, Ctrl+function, Alt+letter, and Ctrl+letter key combinations. 4208 nInkeyCode value 5 24 19 4 1 6 18 3 397 401 26 2 29 23 31 30 408 416 411 413 407 415 409 417 13 32 27 10 379 309 284 387 257 422 399 405 406 398 400 5 420 311 330 334 22 7 8 9 271 402 403 127 404 418 419 nanoBase 1997 user manual Key or key combination [ Up arrow ], [ Ctrl ]+E [ Down arrow ], [ Ctrl ]+X [ Left arrow ], [ Ctrl ]+S [ Right arrow ], [ Ctrl ]+D [ Home ], [ Ctrl ]+A [ End ], [ Ctrl ]+F [ PgUp ], [ Ctrl ]+R [ PgDn ], [ Ctrl ]+C [ Ctrl ]+[ Up arrow ] [ Ctrl ]+[ Down arrow ] [ Ctrl ]+[ Left arrow ], [ Ctrl ]+Z [ Ctrl ]+[ Right arrow ], [ Ctrl ]+B [ Ctrl ]+[ Home ] [ Ctrl ]+[ End ], [ Ctrl ]+W [ Ctrl ]+[ PgUp ], [ Ctrl ]+Hyphen [ Ctrl ]+[ PgDn ], [ Ctrl ]+^ [ Alt ]+[ Up arrow ] [ Alt ]+[ Down arrow ] [ Alt ]+[ Left arrow ] [ Alt ]+[ Right arrow ] [ Alt ]+[ Home ] [ Alt ]+[ End ] [ Alt ]+[ PgUp ] [ Alt ]+[ PgDn ] [ Enter ], [ Ctrl ]+M Space bar Esc [ Ctrl ]+[ Enter ] [ Ctrl ]+Print Screen [ Ctrl ]+? [ Alt ]+[ Enter ] [ Alt ]+Equals [ Alt ]+Esc Keypad [ Alt ]+[ Enter ] Keypad [ Ctrl ]+5 Keypad [ Ctrl ]+/ Keypad [ Ctrl ]+* Keypad [ Ctrl ]+Keypad [ Ctrl ]++ Keypad [ Alt ]+5 Keypad [ Alt ]+/ Keypad [ Alt ]+* Keypad [ Alt ]+Keypad [ Alt ]++ [ Ins ], [ Ctrl ]+V [ Del ], [ Ctrl ]+G [ Backspace ], [ Ctrl ]+H [ Tab ], [ Ctrl ]+I [ Shift ]+[ Tab ] [ Ctrl ]+[ Ins ] [ Ctrl ]+[ Del ] [ Ctrl ]+[ Backspace ] [ Ctrl ]+[ Tab ] [ Alt ]+[ Ins ] [ Alt ]+[ Del ] nanoBase 1997 user manual nInkeyCode value 270 421 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 286 304 302 288 274 289 290 291 279 292 293 294 306 305 280 281 272 275 287 276 278 303 273 301 277 300 376 4209 Key or key combination [ Alt ]+[ Backspace ] [ Alt ]+[ Tab ] [ Ctrl ]+A, [ Home ] [ Ctrl ]+B, [ Ctrl ]+[ Right arrow ] [ Ctrl ]+C, [ PgDn ], [ Ctrl ]+[ ScrollLock ] [ Ctrl ]+D, [ Right arrow ] [ Ctrl ]+E, [ Up arrow ] [ Ctrl ]+F, [ End ] [ Ctrl ]+G, [ Del ] [ Ctrl ]+H, [ Backspace ] [ Ctrl ]+I, [ Tab ] [ Ctrl ]+J [ Ctrl ]+K [ Ctrl ]+L [ Ctrl ]+M, Return [ Ctrl ]+N [ Ctrl ]+O [ Ctrl ]+P [ Ctrl ]+Q [ Ctrl ]+R, [ PgUp ] [ Ctrl ]+S, [ Left arrow ] [ Ctrl ]+T [ Ctrl ]+U [ Ctrl ]+V, [ Ins ] [ Ctrl ]+W, [ Ctrl ]+[ End ] [ Ctrl ]+X, [ Down arrow ] [ Ctrl ]+Y [ Ctrl ]+Z, [ Ctrl ]+[ Left arrow ] [ Alt ]+A [ Alt ]+B [ Alt ]+C [ Alt ]+D [ Alt ]+E [ Alt ]+F [ Alt ]+G [ Alt ]+H [ Alt ]+I [ Alt ]+J [ Alt ]+K [ Alt ]+L [ Alt ]+M [ Alt ]+N [ Alt ]+O [ Alt ]+P [ Alt ]+Q [ Alt ]+R [ Alt ]+S [ Alt ]+T [ Alt ]+U [ Alt ]+V [ Alt ]+W [ Alt ]+X [ Alt ]+Y [ Alt ]+Z [ Alt ]+1 nanoBase 1997 user manual 4210 nInkeyCode value Key or key combination 377 378 379 380 381 382 383 384 385 28 -1 -2 -3 -4 -5 -6 -7 -8 -9 -40 -41 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -44 -45 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -46 -47 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 [ Alt ]+2 [ Alt ]+3 [ Alt ]+4 [ Alt ]+5 [ Alt ]+6 [ Alt ]+7 [ Alt ]+8 [ Alt ]+9 [ Alt ]+0 F1, [ Ctrl ]+[ Backslash ] F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 [ Ctrl ]+F1 [ Ctrl ]+F2 [ Ctrl ]+F4 [ Ctrl ]+F3 [ Ctrl ]+F5 [ Ctrl ]+F6 [ Ctrl ]+F7 [ Ctrl ]+F8 [ Ctrl ]+F9 [ Ctrl ]+F10 [ Ctrl ]+F11 [ Ctrl ]+F12 [ Alt ]+F1 [ Alt ]+F2 [ Alt ]+F3 [ Alt ]+F4 [ Alt ]+F5 [ Alt ]+F6 [ Alt ]+F7 [ Alt ]+F8 [ Alt ]+F9 [ Alt ]+F10 [ Alt ]+F11 [ Alt ]+F12 [ Shift ]+F1 [ Shift ]+F2 [ Shift ]+F3 [ Shift ]+F4 [ Shift ]+F5 [ Shift ]+F6 [ Shift ]+F7 [ Shift ]+F8 [ Shift ]+F9 [ Shift ]+F10 nanoBase 1997 user manual 4211 nInkeyCode value Key or key combination -42 -43 [ Shift ]+F11 [ Shift ]+F12 INT() Integer INT(nExp ) ⇒ nInteger is a numeric expression to convert to an integer. nExp INT() is a numeric function that converts a numeric value to an integer by truncating all digits to the right of the decimal point. INT() is useful in operations where the decimal portion of a number is not needed. ISALPHA() ISALPHA(cString ) ⇒ lBoolean is the character string to examine. cString ISALPHA() returns true (‘.T.’) if the first character in cString is alphabetic; otherwise, it returns false (‘.F.’). ISCOLOR() ISCOLOR() | ISCOLOUR() ⇒ lBoolean ISCOLOR() returns true (‘.T.’) if there is a color graphics card installed; otherwise, it returns false (‘.F.’). ISDIGIT() ISDIGIT(cString ) ⇒ lBoolean cString is the character string to examine. ISDIGIT() returns true (‘.T.’) if the first character of the character string is a digit between zero and nine; otherwise, it returns false (‘.F.’). ISLOWER() ISLOWER(cString ) ⇒ lBoolean cString is the character string to examine. ISLOWER() returns true (‘.T.’) if the first character of the character string is a lowercase letter; otherwise, it returns false (‘.F.’). ISPRINTER() ISPRINTER() ⇒ lReady ISPRINTER() returns true (‘.T.’) if ‘LPT1:’ is ready; otherwise, it returns false (‘.F.’). ISUPPER() ISUPPER(cString ) ⇒ lBoolean cString is the character string to examine. nanoBase 1997 user manual 4212 ISUPPER() returns true (‘.T.’) if the first character is an uppercase letter; otherwise, it returns false (‘.F.’). L2BIN() Long to binary L2BIN(nExp ) ⇒ cBinaryInteger is the numeric value to convert. Decimal digits are truncated. nExp L2BIN() returns a four-byte character string formatted as a 32- bit binary integer. LASTKEY() LASTKEY() ⇒ nInkeyCode LASTKEY() is a keyboard function that reports the INKEY() value of the last key fetched from the keyboard buffer by the INKEY() function, or a wait state. LASTKEY() retains its current value until another key is fetched from the keyboard buffer. LASTREC() Last record LASTREC() ⇒ nRecords LASTREC() returns the number of physical records in the active alias as an integer numeric value. LEFT() LEFT(cString , nCount ) ⇒ cSubString is a character string from which to extract characters. is the number of characters to extract. cString nCount LEFT() returns the leftmost nCount characters of cString as a character string. If nCount is negative or zero, LEFT() returns a null string (""). If nCount is larger than the length of the character string, LEFT() returns the entire string. LEN() Length LEN(cString | aTarget ) ⇒ nCount cString aTarget is the character string to count. is the array to count. LEN() returns the length of a character string or the number of elements in an array as an integer numeric value. LOG() LOG(nExp ) ⇒ nNaturalLog nExp is a numeric value greater than zero to convert to its natural logarithm. LOG() returns the natural logarithm as a numeric value. If nExp is less than or equal to zero, LOG() returns a numeric overflow (displayed as a row of asterisks). nanoBase 1997 user manual 4213 LOWER() LOWER(cString ) ⇒ cLowerString is a character string to convert to lowercase. cString LOWER() returns a copy of cString with all alphabetic characters converted to lowercase. LTRIM() Left trim LTRIM(cString ) ⇒ cTrimString is the character string to copy without leading spaces. cString LTRIM() returns a copy of cString with the leading spaces removed. LUPDATE() Last update LUPDATE() ⇒ dModification LUPDATE() returns the date of last change to the open database file in the current work area. MAX() MAX(nExp1 , nExp2 ) ⇒ nLarger MAX(dExp1 , dExp2 ) ⇒ dLarger are the numeric values to compare. are the date values to compare. nExp1 , nExp2 dExp1 , dExp2 MAX() returns the larger of the two arguments. The value returned is the same type as the arguments. MAXCOL() Max column MAXCOL() ⇒ nColumn MAXCOL() returns the column number of the rightmost visible column for display purposes. MAXROW() MAXROW() ⇒ nRow MAXROW() returns the row number of the bottommost visible row for display purposes. MEMOEDIT() [ ] ] [ ] ] [ ] ] ] ] ] ] ] ] ] MEMOEDIT( cString , nTop , nLeft , nBottom , nRight , lEditMode , cUserFunction , nLineLength , nTabSize , nTextBufferRow , nTextBufferColumn , nWindowRow , nWindowColumn ) ⇒ cTextBuffer [ [ [ [ [ [ [ [ [ [ nanoBase 1997 user manual 4214 cString nTop , nLeft , nBottom , nRight lEditMode cUserFunction nLineLength nTabSize nTextBufferRow , nTextBufferColumn nWindowRow , nWindowColumn is the character string or memo field to copy to the MEMOEDIT() text buffer. are window coordinates. The default coordinates are 0, 0, MAXROW(), and MAXCOL(). determines whether the text buffer can be edited or merely displayed. If not specified, the default value is true (‘.T.’). is the name of a user-defined function that executes when the user presses a key not recognized by MEMOEDIT() and when no keys are pending in the keyboard buffer. determines the length of lines displayed in the MEMOEDIT() window. If a line is greater than nLineLength , it is word wrapped to the next line in the MEMOEDIT() window. The default line length is (nRight - nLeft ). determines the size of a tab character to insert when the user presses Tab. The default is four. define the display position of the cursor within the text buffer when MEMOEDIT() is invoked. nTextBufferRow begins with one and nTextBufferColumn begins with zero. Default is the beginning of MEMOEDIT() window. define the initial position of the cursor within the MEMOEDIT() window. Row and column positions begin with zero. If these arguments are not specified, the initial window position is row zero and the current cursor column position. MEMOEDIT() is a user interface and general purpose text editing function that edits memo fields and long character strings. Editing occurs within a specified window region placed anywhere on the screen. [ Uparrow ]/[ Ctrl ]+E [ Dnarrow ]/[ Ctrl ]+X [ Leftarrow ]/[ Ctrl ]+S [ Rightarrow ]/[ Ctrl ]+D [ Ctrl ]-[ Leftarrow ]/[ Ctrl ]+A [ Ctrl ]-[ Rightarrow ]/[ Ctrl ]+F [ Home ] [ End ] [ Ctrl ]+[ Home ] [ Ctrl ]+[ End ] [ PgUp ] [ PgDn ] [ Ctrl ]+[ PgUp ] [ Ctrl ]+[ PgDn ] [ Return ] [ Delete ] [ Backspace ] [ Tab ] Printable characters [ Ctrl ]+Y [ Ctrl ]+T [ Ctrl ]+B [ Ctrl ]+V/[ Ins ] [ Ctrl ]+W [ Esc ] Move up one line Move down one line Move left one character Move right one character Move left one word Move right one word Move to beginning of current line Move to end of current line Move to beginning of current window Move to end of current window Move to previous edit window Move to next edit window Move to beginning of memo Move to end of memo Move to beginning of next line Delete character at cursor Delete character to left of cursor Insert tab character or spaces Insert character Delete the current line Delete word right Reform paragraph Toggle insert mode Finish editing with save Abort edit and return original nanoBase 1997 user manual 4215 MEMOLINE() MEMOLINE(cString , nLineLength , nLineNumber , nTabSize , lWrap ) ⇒ cLine [ [ [ [ ] ] ] ] is the memo field or character string from which to extract a line of text. specifies the number of characters per line and can be between four and 254 . If not specified, the default line length is 79. is the line number to extract. If not specified, the default value is one. defines the tab size. If not specified, the default value is four. toggles word wrap on and off. Specifying true (‘.T.’) toggles word wrap on; false (‘.F.’) toggles it off. If not specified, the default value is true (‘.T.’). cString nLineLength nLineNumber nTabSize lWrap MEMOLINE() returns the line of text specified by nLineNumber in cString as a character string. If the line has fewer characters than the indicated length, the return value is padded with blanks. If the line number is greater than the total number of lines in cString , MEMOLINE() returns a null string (""). If lWrap is true (‘.T.’) and the indicated line length breaks the line in the middle of a word, that word is not included as part of the return value but shows up at the beginning of the next line extracted with MEMOLINE(). If lWrap is false (‘.F.’), MEMOLINE() returns only the number of characters specified by the line length. The next line extracted by MEMOLINE() begins with the character following the next hard carriage return, and all intervening characters are not processed. MEMOREAD() MEMOREAD(cFile ) ⇒ cString is the name of the file to read from disk. It must include an extension if there is one, and can optionally include a path. cFile MEMOREAD() returns the contents of a text file as a character string. MEMORY() MEMORY(nExp ) ⇒ nKbytes is a numeric value that determines the type of value MEMORY() returns. nExp MEMORY() returns an integer numeric value representing the amount of memory available. Estimated total space available for character values Largest contiguous block available for character values Area available for RUN commands MEMORY(0) MEMORY(1) MEMORY(2) MEMOTRAN() Memo translate MEMOTRAN(cString , cReplaceHardCR , cReplaceSoftCR ) ⇒ cNewString [ [ ] ] nanoBase 1997 user manual 4216 is the character string or memo field to search. is the character to replace a hard carriage return/linefeed pair with. If not specified, the default value is a semicolon (;). is the character to replace a soft carriage return/linefeed pair with. If not specified, the default value is a space. cString cReplaceHardCR cReplaceSoftCR MEMOTRAN() returns a copy of cString with the specified carriage return/linefeed pairs replaced. MEMOWRIT() Memo write MEMOWRIT(cFile , cString ) ⇒ lSuccess is the name of the target disk file including the file extension and optional path and drive designator. is the character string or memo field to write to cFile . cFile cString MEMOWRIT() is a memo function that writes a character string or memo field to a disk file. If a path is not specified, MEMOWRIT() writes cFile to the current DOS directory and not the current DEFAULT directory. If cFile already exists, it is overwritten. MEMOWRIT() returns true (‘.T.’) if the writing operation is successful; otherwise, it returns false (‘.F.’). MEMVARBLOCK() MEMVARBLOCK(cMemvarName ) ⇒ bMemvarBlock is the name of the variable referred to by the set-get block, specified as a character string. cMemvarName MEMVARBLOCK() returns a code block that when evaluated sets (assigns) or gets (retrieves) the value of the given memory variable. If cMemvarName does not exist, MEMVARBLOCK() returns NIL. MIN() MIN(nExp1 , nExp2 ) ⇒ nSmaller MIN(dExp1 , dExp2 ) ⇒ dSmaller are the numeric values to compare. are the date values to compare. nExp1 , nExp2 dExp1 , dExp2 MIN() returns the smaller of the two arguments. The value returned is the same data type as the arguments. MLCOUNT() Memo line count [ ] [ ] MLCOUNT(cString , nLineLength , nTabSize , lWrap ) ⇒ nLines [ cString nLineLength nTabSize ] is the character string or memo field to count. specifies the number of characters per line and can range from four to 254 . If not specified, the default line length is 79. defines the tab size. If not specified, the default value is four. nanoBase 1997 user manual 4217 toggles word wrap on and off. Specifying true (‘.T.’) toggles word wrap on; false (‘.F.’) toggles it off. If not specified, the default value is true (‘.T.’). lWrap MLCOUNT() returns the number of lines in cString depending on the nLineLength , the nTabSize , and whether word wrapping is on or off. MLCTOPOS() Memo line column to position MLCTOPOS(cText , nWidth , nLine , nCol , nTabSize , lWrap ) ⇒ nPosition [ ] [ ] is the text string to scan. is the line length formatting width. is the line number counting from 1. is the column number counting from 0. is the number of columns between tab stops. If not specified, the default is 4. is the word wrap flag. If not specified, the default is true (‘.T.’). cText nWidth nLine nCol nTabSize lWrap MLCTOPOS() returns the byte position within cText counting from 1. MLPOS() Memo line position MLPOS(cString , nLineLength , nLine , nTabSize , lWrap ) ⇒ nPosition [ ] [ ] is a character string or memo field. specifies the number of characters per line. specifies the line number. defines the tab size. The default is four. toggles word wrap on and off. Specifying true (‘.T.’) toggles word wrap on, and false (‘.F.’) toggles it off. The default is true (‘.T.’). cString nLineLength nLine nTabSize lWrap MLPOS() returns the character position of nLine in cString as an integer numeric value. If nLine is greater than the number of lines in cString , MLPOS() returns the length of cString . MONTH() MONTH(dDate) ⇒ nMonth is the date value to convert. dDate MONTH() returns an integer numeric value in the range of zero to 12. Specifying a null date (CTOD("")) returns zero. MPOSTOLC() Memo position to line column MPOSTOLC(cText , nWidth , nPos , nTabSize , lWrap ) ⇒ aLineColumn [ cText ] [ ] is a text string. nanoBase 1997 user manual 4218 is the length of the formatted line. is the byte position within text counting from one. is the number of columns between tab stops. If not specified, the default is four. is the word wrap flag. If not specified, the default is true (‘.T.’). nWidth nPos nTabSize lWrap MPOSTOLC() returns an array containing the line and the column values for the specified byte position, nPos . MPOSTOLC() is a memo function that determines the formatted line and column corresponding to a particular byte position within cText . Note that the line number returned is one-relative, the column number is zero-relative. This is compatible with MEMOEDIT(). nPos is one-relative, compatible with AT(), RAT(), and other string functions. NETERR() Net error [ ] NETERR( lNewError ) ⇒ lError lNewError if specified sets the value returned by NETERR() to the specified status. lNewError can be either true (‘.T.’) or false (‘.F.’). Setting NETERR() to a specified value allows the runtime error handler to control the way certain file errors are handled. NETERR() returns true (‘.T.’) if a USE or APPEND BLANK fails. The initial value of NETERR() is false (‘.F.’). If the current process is not running under a network operating system, NETERR() always returns false (‘.F.’). NETNAME() NETNAME() ⇒ cWorkstationName NETNAME() returns the workstation identification as a character string up to 15 characters in length. If the workstation identification was never set or the application is not operating under the IBM PC Network, it returns a null string (""). NEXTKEY() NEXTKEY() ⇒ nInkeyCode NEXTKEY() returns an integer numeric value ranging from -39 to 386. If the keyboard buffer is empty, NEXTKEY() returns zero. If SET TYPEAHEAD is zero, NEXTKEY() always returns zero. NEXTKEY() is like the INKEY() function, but differs in one fundamental respect. INKEY() removes the pending key from the keyboard buffer and updates LASTKEY() with the value of the key. NEXTKEY(), by contrast, reads, but does not remove the key from the keyboard buffer and does not update LASTKEY(). NOSNOW() NOSNOW(lToggle ) ⇒ NIL lToggle is a logical value that toggles the current state of snow suppression. A value of true (‘.T.’) enables the snow suppression on, while a value of false (‘.F.’) disables snow suppression. NOSNOW() is used to suppress snow on old CGA monitors. nanoBase 1997 user manual 4219 ORDBAGEXT() ORDBAGEXT() ⇒ cBagExt ORDBAGEXT() returns a character expression that is the default Order Bag extension of the current work area. cBagExt is determined by the RDD active in the current work area. ORDBAGNAME() ORDBAGNAME(nOrder | cOrderName ) ⇒ cOrderBagName is an integer that identifies the position in the Order List of the target Order whose Order Bag name is sought. is a character string that represents the name of the target Order whose Order Bag name is sought. nOrder cOrderName ORDBAGNAME() returns a character string, the Order Bag name of the specific Order. ORDCREATE() [ ] ORDCREATE(cOrderBagName , cOrderName , cExpKey , lUnique ) ⇒ NIL [ [bExpKey], ] is the name of a disk file containing one or more Orders. is the name of the Order to be created. is an expression that returns the key value to place in the Order for each record in the current work area. The maximum length of the index key expression is determined by the database driver. is a code block that evaluates to a key value that is placed in the Order for each record in the current work area. specifies whether a unique Order is to be created. Default is the current global _SET_UNIQUE setting. cOrderBagName cOrderName cExpKey bExpKey lUnique ORDCREATE() is an Order management function that creates an Order in the current work area. It works like DBCREATEINDEX() except that it lets you create Orders in RDDs that recognize multiple Order Bags. ORDDESTROY() ORDDESTROY(cOrderName [, cOrderBagName ]) ⇒ NIL is the name of the Order to be removed from the current or specified work area. is the name of a disk file containing one or more Orders. cOrderName cOrderBagName ORDDESTROY() is an Order management function that removes a specified Order from multiple-Order Bags. ORDDESTROY() is not supported for DBFNDX and DBFNTX. ORDFOR() ORDFOR(cOrderName cOrderName nOrder cOrderBagName | nOrder [, cOrderBagName ]) ⇒ cForExp is the name of the target Order, whose cForExp is sought. is an integer that identifies the position in the Order List of the target Order whose cForExp is sought. is the name of an Order Bag containing one or more Orders. ORDFOR() returns a character expression, cForExp, that represents the FOR condition of nanoBase 1997 user manual 4220 the specified Order. If the Order was not created using the FOR clause the return value will be an empty string (""). If the database driver does not support the FOR condition, it may either return an empty string ("") or raise an "unsupported function" error, depending on the driver. ORDKEY() ORDKEY(cOrderName | nOrder [, cOrderBagName ]) ⇒ cExpKey is the name of an Order, a logical ordering of a database. is an integer that identifies the position in the Order List of the target Order whose cExpKey is sought. is the name of a disk file containing one or more Orders. cOrderName nOrder cOrderBagName ORDKEY() is an Order management function that returns a character expression, cExpKey, that represents the key expression of the specified Order. ORDLISTADD() ORDLISTADD(cOrderBagName [, ] cOrderName ) ⇒ NIL is the name of a disk file containing one or more Orders. the name of the specific Order from the Order Bag to be added to the Order List of the current work area. If you do not specify cOrderName , all orders in the Order Bag are added to the Order List of the current work area. cOrderBagName cOrderName ORDLISTADD() is an Order management function that adds the contents of an Order Bag , or a single Order in an Order Bag, to the Order List. Any Orders already associated with the work area continue to be active. If the newly opened Order Bag contains the only Order associated with the work area, it becomes the controlling Order; otherwise, the controlling Order remains unchanged. ORDLISTCLEAR() ORDLISTCLEAR() ⇒ NIL ORDLISTCLEAR() is an Order management function that removes all Orders from the Order List for the current work area. ORDLISTREBUILD() ORDLISTREBUILD() ⇒ NIL ORDLISTREBUILD() is an Order management function that rebuilds all the orders in the current Order List. ORDNAME() [ ORDNAME(nOrder ,cOrderBagName ]) ⇒ cOrderName is an integer that identifies the position in the Order List of the target Order whose database name is sought. is the name of a disk file containing one or more Orders. nOrder cOrderBagName ORDNAME() returns the name of the specified Order in the current Order List or the specified Order Bag if opened in the Current Order list. ORDNUMBER() ORDNUMBER(cOrderName [, cOrderBagName ]) ⇒ nOrderNo nanoBase 1997 user manual 4221 the name of the specific Order whose position in the Order List is sought. is the name of a disk file containing one or more Orders. cOrderName cOrderBagName ORDNUMBER() returns nOrderNo, an integer that represents the position of the specified Order in the Order List. ORDSETFOCUS() [ ] [,cOrderBagName ]) ORDSETFOCUS( cOrderName | nOrder ⇒ cPrevOrderNameInFocus cOrderName nOrder cOrderBagName is the name of the selected Order, a logical ordering of a database. is a number representing the position in the Order List of the selected Order. is the name of a disk file containing one or more Orders. ORDSETFOCUS() is an Order management function that returns the Order Name of the previous controlling Order and optionally sets the focus to an new Order. OS() OS() ⇒ cOsName OS() returns the operating system name as a character string. OUTERR() Output error OUTERR(exp_list ) ⇒ NIL is a list of values to display and can consist of any combination of data types including memo. exp_list OUTERR() is identical to OUTSTD() except that it writes to the standard error device rather than the standard output device. Output sent to the standard error device bypasses the console and output devices as well as any DOS redirection. It is typically used to log error messages in a manner that will not interfere with the standard screen or printer output. OUTSTD() Output standard OUTSTD(exp_list ) ⇒ NIL is a list of values to display and can consist of any combination of data types including memo. exp_list OUTSTD() is a simple output function similar to QOUT(), except that it writes to the STDOUT device (instead of to the console output stream). PAD?() [cFillChar]) nLength , [cFillChar ]) nLength , [cFillChar ]) PADL(exp , nLength , ⇒ cPaddedString PADC(exp , ⇒ cPaddedString PADR(exp , ⇒ cPaddedString nanoBase 1997 user manual 4222 is a character, numeric, or date value to pad with a fill character. is the length of the character string to return. is the character to pad exp with. If not specified, the default is a space character. exp nLength cFillChar PADC(), PADL(), and PADR() are character functions that pad character, date, and numeric values with a fill character to create a new character string of a specified length. PADC() centers exp within nLength adding fill characters to the left and right sides; PADL() adds fill characters on the left side; and PADR() adds fill characters on the right side. PCOL() Printed column PCOL() ⇒ nColumn PCOL() returns an integer numeric value representing the last printed column position, plus one. The beginning column position is zero. PROW() Printed row PROW() ⇒ nRow PROW() returns an integer numeric value that represents the number of the current line sent to the printer. The beginning row position is zero. QOUT() [ ] QQOUT([exp_list]) QOUT( exp_list ) ⇒ NIL ⇒ NIL is a comma-separated list of expressions (of any data type other than array or block) to display to the console. If no argument is specified and QOUT() is specified, a carriage return/linefeed pair is displayed. If QQOUT() is specified without arguments, nothing displays. exp_list QOUT() and QQOUT() are console functions. They display the results of one or more expressions to the console. QOUT() outputs carriage return and linefeed characters before displaying the results of exp_list . QQOUT() displays the results of exp_list at the current ROW() and COL() position. When QOUT() and QQOUT() display to the console, ROW() and COL() are updated. RAT() Right at RAT(cSearch , cTarget ) ⇒ nPosition is the character string to locate. is the character string to search. cSearch cTarget RAT() returns the position of cSearch within cTarget as an integer numeric value, starting the search from the right. If cSearch is not found, RAT() returns zero. RDDLIST() [ RDDLIST( nRDDType ]) ⇒ aRDDList nanoBase 1997 user manual 4223 is an integer that represents the type of the RDD you wish to list. nRDDType = 1 Full RDD implementation nRDDType = 2 Import/Export only driver. nRDDType RDDLIST() returns a one-dimensional array of the RDD names registered with the application as nRDDType . RDDNAME() RDDNAME() ⇒ cRDDName RDDNAME() returns a character string, cRDDName, the registered name of the active RDD in the current or specified work area. RDDSETDEFAULT() [ RDDSETDEFAULT( cNewDefaultRDD ]) ⇒ cPreviousDefaultRDD is a character string, the name of the RDD that is to be made the new default RDD in the application. cNewDefaultRDD RDDSETDEFAULT() is an RDD function that sets or returns the name of the previous default RDD driver and, optionally, sets the current driver to the new RDD driver specified by cNewDefaultRDD . READINSERT() [ ] READINSERT( lToggle ) ⇒ lCurrentMode lToggle toggles the insert mode on or off. True (‘.T.’) turns insert on, while false (‘.F.’) turns insert off. The default is false (‘.F.’) or the last user-selected mode in READ or MEMOEDIT(). READINSERT() returns the current insert mode state as a logical value. READMODAL() READMODAL(aGetList ) ⇒ NIL aGetList is an array containing a list of Get objects to edit. READMODAL() is like the READ command, but takes a GetList array as an argument and does not reinitialize the GetList array when it terminates. The GET system is implemented using a public array called GetList. Each time an @...GET command executes, it creates a Get object and adds to the currently visible GetList array. The standard READ command is preprocessed into a call to READMODAL() using the GetList array as its argument. READVAR() READVAR() ⇒ cVarName READVAR() returns the name of the variable associated with the current Get object or the variable being assigned by the current MENU TO command as an uppercase character string. RECNO() Record number RECNO() ⇒ nRecord nanoBase 1997 user manual 4224 RECNO() returns the current record number as an integer numeric value. If the work area contains a database file with zero records, RECNO() returns one, BOF() and EOF() both return true (‘.T.’), and LASTREC() returns zero. If the record pointer is moved past the last record, RECNO() returns LASTREC() + 1 and EOF() returns true (‘.T.’). If an attempt is made to move before the first record, RECNO() returns the record number of the first logical record in the database file and BOF() returns true (‘.T.’). If no database file is open, RECNO() will return a zero. RECSIZE() Record size RECSIZE() ⇒ nBytes RECSIZE() returns, as a numeric value, the record length, in bytes, of the database file open in the current work area. RECSIZE() returns zero if no database file is open. REPLICATE() REPLICATE(cString , nCount ) ⇒ cRepeatedString is the character string to repeat. is the number of times to repeat cString . cString nCount REPLICATE() returns a character string. Specifying a zero as the nCount argument returns a null string (""). RESTSCREEN() Restore screen [ ] [ ] ] [ ] RESTSCREEN( nTop , nLeft , nBottom , nRight , cScreen ) ⇒ NIL [ nTop , nLeft , nBottom , nRight cScreen define the coordinates of the screen information contained in cScreen . If the cScreen was saved without coordinates to preserve the entire screen, no screen coordinates are necessary with RESTSCREEN(). is a character string containing the saved screen region. RESTSCREEN() is a screen function that redisplays a screen region saved with SAVESCREEN(). The target screen location may be the same as or different than the original location when the screen region was saved. RIGHT() RIGHT(cString , nCount ) ⇒ cSubString cString nCount is the character string from which to extract characters. is the number of characters to extract. RIGHT() returns the rightmost nCount characters of cString . If nCount is zero, RIGHT() returns a null string (""). If nCount is negative or larger than the length of the character string, RIGHT() returns cString . RLOCK() Record lock RLOCK() ⇒ lSuccess RLOCK() is a network function that locks the current record, preventing other users from nanoBase 1997 user manual 4225 updating the record until the lock is released. RLOCK() provides a shared lock, allowing other users read-only access to the locked record while allowing only the current user to modify it. A record lock remains until another record is locked, an UNLOCK is executed, the current database file is closed, or an FLOCK() is obtained on the current database file. ROUND() ROUND(nNumber , nDecimals ) ⇒ nRounded is the numeric value to round. defines the number of decimal places to retain. Specifying a negative nDecimals value rounds whole number digits. nNumber nDecimals ROUND() is a numeric function that rounds nNumber to the number of places specified by nDecimals . Specifying a zero or negative value for nDecimals allows rounding of whole numbers. A negative nDecimals indicates the number of digits to the left of the decimal point to round. Digits between five to nine, inclusive, are rounded up. Digits below five are rounded down. ROW() ROW() ⇒ nRow ROW() returns the cursor row position as an integer numeric value. The range of the return value is zero to MAXROW(). RTRIM() Right trim [R]TRIM(cString ) ⇒ cTrimString is the character string to copy without trailing spaces. cString RTRIM() returns a copy of cString with the trailing spaces removed. If cString is a null string ("") or all spaces, RTRIM() returns a null string (""). SAVESCREEN() [ ] [ ] ] [ ] SAVESCREEN( nTop , nLeft , nBottom , nRight ) ⇒ cScreen [ nTop , nLeft , nBottom , nRight define the coordinates of the screen region to save. Default is the entire screen. SAVESCREEN() returns the specified screen region as a character string. SCROLL() [ ] [nLeft ], ] [nRight ], [nVert] [nHoriz]) SCROLL( nTop , nBottom , [ ⇒ NIL nTop , nLeft , nBottom , nRight define the scroll region coordinates. nVert defines the number of rows to scroll, vertically. A positive value scrolls up the specified number of rows. A negative value scrolls down the specified number of rows. A value of zero disables vertical scrolling. If nVert is not specified, zero is assumed. nanoBase 1997 user manual 4226 defines the number of rows to scroll horizontally. A positive value scrolls left the specified number of columns. A negative value scrolls right the specified number of columns. A value of zero disables horizontal scrolling. If nHoriz is not specified, zero is assumed. If you supply neither nVert or nHoriz parameters to SCROLL(), the area specified by the first four parameters will be blanked. nHoriz SCROLL() is a screen function that scrolls a screen region up or down a specified number of rows. When a screen scrolls up, the first line of the region is erased, all other lines are moved up, and a blank line is displayed in the current standard color on the bottom line of the specified region. If the region scrolls down, the operation is reversed. If the screen region is scrolled more than one line, this process is repeated. SECONDS() SECONDS() ⇒ nSeconds SECONDS() returns the system time as a numeric value in the form seconds.hundredths. The numeric value returned is the number of seconds elapsed since midnight, and is based on a twenty-four hour clock in a range from zero to 86399. SELECT() [ ] SELECT( cAlias ) ⇒ nWorkArea is the target work area alias name. cAlias SELECT() returns the work area of the specified alias as a integer numeric value. SET() [ SET(nSpecifier , expNewSetting ⇒ CurrentSetting ], [lOpenMode]) is a numeric value that identifies the setting to be inspected or changed. is an optional argument that specifies a new value for the nSpecifier . The type of expNewSetting depends on nSpecifier . is a logical value that indicates whether or not files are opened for some settings. A value of false (‘.F.’) means the file should be truncated. A value of true (‘.T.’) means the file should be opened in append mode. In either case, if the file does not exist, it is created. If this argument is not specified, the default is append mode. nSpecifier expNewSetting lOpenMode SET() returns the current value of the specified setting. Inside nB, the function SET() is not so easy to use as inside the Clipper environment. This because nB cannot support manifest constants and a numeric specifier nSpecifier is not easy to manage. Instead of SET() you can use SETVERB(). SETBLINK() [ ] SETBLINK( lToggle ) ⇒ lCurrentSetting nanoBase 1997 user manual 4227 changes the meaning of the asterisk (*) character when it is encountered in a SETCOLOR() string. Specifying true (‘.T.’) sets character blinking on and false (‘.F.’) sets background intensity. The default is true (‘.T.’). lToggle SETBLINK() returns the current setting as a logical value. SETCANCEL() [ ] SETCANCEL( lToggle ) ⇒ lCurrentSetting changes the availability of Alt-C and Ctrl-Break as termination keys. Specifying true (‘.T.’) allows either of these keys to terminate an application and false (‘.F.’) disables both keys. The default is true (‘.T.’). lToggle SETCANCEL() returns the current setting as a logical value. SETCOLOR() [ ] SETCOLOR( cColorString ) ⇒ cColorString is a character string containing a list of color attribute settings for subsequent screen painting. cColorString SETCURSOR() [ SETCURSOR( nCursorShape ]) ⇒ nCurrentSetting is a number indicating the shape of the cursor. nCursorShape == 0 None nCursorShape == 1 Underline nCursorShape == 2 Lower half block nCursorShape == 3 Full block nCursorShape == 4 Upper half block nCursorShape SETCURSOR() returns the current cursor shape as a numeric value. SETKEY() SETKEY(nInkeyCode , [bAction]) ⇒ bCurrentAction nInkeyCode bAction is the INKEY() value of the key to be associated or queried. specifies a code block that is automatically executed whenever the specified key is pressed during a wait state. SETKEY() returns the action block currently associated with the specified key, or NIL if the specified key is not currently associated with a block. SETMODE() SETMODE(nRows , nCols ) ⇒ lSuccess nRows nCols is the number of rows in the desired display mode. is the number of columns in the desired display mode. SETMODE() is an environment function that attempts to change the mode of the display hardware to match the number of rows and columns specified. The change in screen size is nanoBase 1997 user manual 4228 reflected in the values returned by MAXROW() and MAXCOL(). SETPOS() Set position SETPOS(nRow , nCol ) ⇒ NIL define the new screen position of the cursor. These values may range from 0, 0 to MAXROW(), MAXCOL(). nRow , nCol SETPOS() is an environment function that moves the cursor to a new position on the screen. After the cursor is positioned, ROW() and COL() are updated accordingly. SETPRC() Set printer row column SETPRC(nRow , nCol ) ⇒ NIL is the new PROW() value. is the new PCOL() value. nRow nCol SETPRC() is a printer function that sends control codes to the printer without changing the tracking of the printhead position. SOUNDEX() SOUNDEX(cString ) ⇒ cSoundexString is the character string to convert. cString SOUNDEX() returns a four-digit character string in the form A999. SPACE() SPACE(nCount ) ⇒ cSpaces is the number of spaces to return. nCount SPACE() returns a character string. If nCount is zero, SPACE() returns a null string (""). SQRT() SQRT(nNumber ) ⇒ nRoot nNumber is a positive number to take the square root of. SQRT() returns a numeric value calculated to double precision. The number of decimal places displayed is determined solely by SET DECIMALS regardless of SET FIXED. A negative nNumber returns zero. STR() String STR(nNumber , nNumber nLength nDecimals [nLength ], [nDecimals]) ⇒ cNumber is the numeric expression to convert to a character string. is the length of the character string to return, including decimal digits, decimal point, and sign. is the number of decimal places to return. nanoBase 1997 user manual 4229 STR() returns nNumber formatted as a character string. STRTRAN() STRTRAN(cString , cSearch , cReplace , nStart , [ ] [ ] [nCount ]) ⇒ cNewString is the character string or memo field to search. is the sequence of characters to locate. is the sequence of characters with which to replace cSearch . If this argument is not specified, the specified instances of the search argument are replaced with a null string (""). is the first occurrence that will be replaced. If this argument is omitted, the default is one. is the number of occurrences to replace. If this argument is not specified, the default is all. cString cSearch cReplace nStart nCount STRTRAN() returns a new character string with the specified instances of cSearch replaced with cReplace . STUFF() STUFF(cString , nStart , nDelete , cInsert ) ⇒ cNewString is the target character string into which characters are inserted and deleted. is the starting position in the target string where the insertion/deletion occurs. is the number of characters to delete. is the string to insert. cString nStart nDelete cInsert STUFF() returns a copy of cString with the specified characters deleted and with cInsert inserted. SUBSTR() Sub string SUBSTR(cString , nStart , cString nStart nCount [nCount ]) ⇒ cSubstring is the character string from which to extract a substring. is the starting position in cString . If nStart is positive, it is relative to the leftmost character in cString . If nStart is negative, it is relative to the rightmost character in the cString . is the number of characters to extract. If omitted, the substring begins at nStart and continues to the end of the string. If nCount is greater than the number of characters from nStart to the end of cString , the extra is ignored. SUBSTR() is a character function that extracts a substring from another character string or memo field. TIME() TIME() ⇒ cTimeString TIME() returns the system time as a character string in the form hh:mm:ss. hh is hours in 24-hour format, mm is minutes, and ss is seconds. TIME() is a time function that displays the system time on the screen or prints it on a report. nanoBase 1997 user manual 4230 TONE() TONE(nFrequency , nDuration ) ⇒ NIL nFrequency nDuration is a positive numeric value indicating the frequency of the tone to sound. is a positive numeric value indicating the duration of the tone measured in increments of 1/18 of a second. For example, an nDuration value of 18 represents one second. For both arguments, noninteger values are truncated (not rounded) to their integer portion. TRANSFORM() TRANSFORM(exp , cSayPicture ) ⇒ cFormatString exp cSayPicture is the value to format. This expression can be any valid data type except array, code block, and NIL. is a string of picture and template characters that describes the format of the returned haracter string. TRANSFORM() converts exp to a formatted character string as defined by cSayPicture . TYPE() TYPE(cExp ) ⇒ cType cExp is a character expression whose type is to be determined. cExp can be a field, with or without the alias, a private or public variable, or an expression of any type. TYPE() returns one of the following characters: Array Block Character Date Logical Memo Numeric Object NIL, local, or static Error syntactical Error indeterminate A B C D L M N O U UE UI TYPE() is a system function that returns the type of the specified expression. TYPE() is like VALTYPE() but uses the macro operator (&) to determine the type of the argument. VALTYPE(), by contrast, evaluates an expression and determines the data type of the return value. UPDATED() UPDATED() ⇒ lChange UPDATED() returns true (‘.T.’) if data in a GET is added or changed; otherwise, it returns false (‘.F.’). UPPER() UPPER(cString ) ⇒ cUpperString nanoBase 1997 user manual cString 4231 is the character string to convert. UPPER() returns a copy of cString with all alphabetical characters converted to uppercase. All other characters remain the same as in the original string. USED() USED() ⇒ lDbfOpen USED() returns true (‘.T.’) if there is a database file in USE in the current work area; otherwise, it returns false (‘.F.’). VAL() Value VAL(cNumber ) ⇒ nNumber cNumber is the character expression to convert. VAL() is a character conversion function that converts a character string containing numeric digits to a numeric value. When VAL() is executed, it evaluates cNumber until a second decimal point, the first non-numeric character, or the end of the expression is encountered. VALTYPE() Value type VALTYPE(exp ) ⇒ cType exp is an expression of any type. VALTYPE() returns a single character representing the data type returned by exp . VALTYPE() returns one of the following characters: Array Block Character Date Logical Memo Numeric Object NIL A B C D L M N O U VALTYPE() is a system function that takes a single argument, evaluates it, and returns a one character string describing the data type of the return value. YEAR() YEAR(dDate) ⇒ nYear dDate is the date value to convert. YEAR() returns the year of the specified date value including the century digits as a fourdigit numeric value. The value returned is not affected by the current DATE or CENTURY format. Specifying a null date (CTOD("")) returns zero. nanoBase 1997 user manual 4232 371.15 nB functions Some functions made into nB are available for macro use. Not all available functions are here documented. ACCEPT() ACCEPT( Field , [cMessage], [cHeader] ) ⇒ updatedField |NIL It is a prompt function that shows cMessage asking to type something into Field . It returns the updated data or NIL if [ Esc ] was pressed. The string cHeader is showed centered at the top window. ACHOICE() ACHOICE(nTop , nLeft , nBottom , nRight , acMenuItems , alSelectableItems , nInitialItem , [ ] [ ] [lButtons | aButtons ]) ⇒ nPosition nTop , nLeft , nBottom , nRight acMenuItems are the window coordinates. is an array of character strings to display as the menu items. is a parallel array of logical values (one element for each item in acMenuItems ) that specify the selectable menu items. Elements can be logical values or character strings. If the element is a character string, it is evaluated as a macro expression which should evaluate to a logical data type. A value of false (‘.F.’) means that the corresponding menu item is not available, and a value of true (‘.T.’) means that it is available. By default, all menu items are available for selection. is the position in the acMenuItems array of the item that will be highlighted when the menu is initially displayed. if True means that default buttons will appear. is an array of buttons. the nth button row position; the nth button column position; the nth button text; the nth button code block. alSelectableItems nInitialItem lButtons aButtons aButtons [n ][1] == N aButtons [n ][2] == N aButtons [n ][3] == C aButtons [n ][4] == B ACHOICE() returns the numeric position in the acMenuItems array of the menu item selected. If no choice is made, ACHOICE() returns zero. ACHOICEWINDOW() [ ] ACHOICEWINDOW( acMenuItems , cDescription , nTop , nLeft , nBottom , nRight , alSelectableItems , nInitialItem ) ⇒ nPosition [ [ ] ] acMenuItems cDescription nTop , nLeft , nBottom , nRight is an array of character strings to display as the menu items. is a header to be shown at the top of window. are the window coordinates. nanoBase 1997 user manual 4233 is a parallel array of logical values (one element for each item in acMenuItems ) that specify the selectable menu items. Elements can be logical values or character strings. If the element is a character string, it is evaluated as a macro expression which should evaluate to a logical data type. A value of false (‘.F.’) means that the corresponding menu item is not available, and a value of true (‘.T.’) means that it is available. By default, all menu items are available for selection. is the position in the acMenuItems array of the item that will be highlighted when the menu is initially displayed. alSelectableItems nInitialItem ACHOICEWINDOW() calls ACHOICE() with a window border around the ACHOICE() screen area. ALERTBOX() ALERTBOX( cMessage , [aOptions] ) ⇒ nChoice is the message text displayed, centered, in the alert box. If the message contains one or more semicolons, the text after the semicolons is centered on succeeding lines in the dialog box. defines a list of up to 4 possible responses to the dialog box. cMessage aOptions ALERTBOX() returns a numeric value indicating which option was chosen. If the [ Esc ] key is pressed, the value returned is zero. The ALERTBOX() function creates a simple modal dialog. The user can respond by moving a highlight bar and pressing the Return or SpaceBar keys, or by pressing the key corresponding to the first letter of the option. If aOptions is not supplied, a single "Ok" option is presented. ALERTBOX() is similar to ALERT() but it accept mouse input. ATB() ATB( [nTop], [nLeft ], [nBottom ], [nRight ], aArray , [nSubscript ], [acColSayPic], [acColTopSep], [acColBodySep], [acColBotSep], [acColHead], [acColFoot], [abColValid], [abColMsg], [cColor], [abColColors], [lModify], [lButtons | aButtons ] ) ⇒ NIL nTop , nLeft , nBottom , nRight aArray nSubscript acColSayPic acColTopSep acColBodySep acColBotSep acColHead acColFoot abColValid abColMsg defines the screen area where browse have to take place. bidimensional array to be browsed. starting array position. is the picture array. is the top separation array: default is chr(194)+chr(196). is the body separation array: default is chr(179). is the bottom separation array: default is chr(193)+chr(196). is the header array for every column. is the footer array for every column. is the validation array that specify when a field is properly filled. The condition must be specified in code block format. is the message array that permits to show information at the bottom of browse area. The array must be composed with code blocks which result with a character string. nanoBase 1997 user manual 4234 is the color string: it may be longer than the usual 5 elements. is the color code block array. The code block receive as parameter the value contained inside the field and must return an array containing two numbers: they correspond to the two color couple from cColor. indicates whether the browse can modify data. if True, default buttons are displayed. array of buttons. the nth button row position; the nth button column position; the nth button text; the nth button code block. cColor abColColors lModify lButtons aButtons aButtons [n ][1] N aButtons [n ][2] N aButtons [n ][3] C aButtons [n ][4] B This function starts the browse of a bidimensional array. Only arrays containing monodimensional array containing the same kind of editable data are allowed. The function can handle a maximum of 61 columns. BCOMPILE() BCOMPILE( cString ) ⇒ bBlock Compiles the string cString and returns the code block bBlock BUTTON() BUTTON( @aButtons , nRow , nCol , cText , bAction ) ⇒ NIL [ [ ] [ ] ] [ ] [cColor], the array of buttons to be increased with a new button array. is the row and column starting position for the button string. is the text that make up the button. is the color string. is the code block associated to the button. aButtons nRow and nCol cText cColor bAction This function adds to aButtons a new button array. Please note that the button array added is compatible only with the READ() function and not the other function using array of buttons: the others do not have a color string. COLORARRAY() COLORARRAY( cColor ) ⇒ aColors a color string to be translated into a color array. cColors This function transform a color string into a color array. The array has as many elements as the colors contained inside cColor string. COORDINATE() [ ] ] COORDINATE( @nTop , @nLeft , @nBottom , @nRight , cHorizontal , cVertical ) ⇒ NIL [ ] [ nTop , nLeft , nBottom and nRight are the starting position of a window that is to be differently aligned.
Documenti analoghi
Topolinux 9 - Vecchiomago
leggere da qualche parte la parola “Powerpack”, tant’è che ad un certo punto ho cominciato a preoccuparmi, pensando di aver
installato una semplice Free. Molti conoscono
com’è fatta Mandriva, e poc...