MaCocoa 033

Capitolo 033 - Un pezzo di Carbon

Mi tolgo un sassolino dalla scarpa, ma sono costretto ad utilizzare Carbon. Nessuna paura, è tutto molto semplice.

Documentazione Apple (Carbon e Cocoa).

Primo inserimento: 25 giugno 2003

Dimensione e Risorse

Giocando con la barra dello scorso capitolo, qualcuno si sarà accorto che l'avanzamento lavori non è proprio perfetto. Il problema è molto più evidente in volumi che contengono molti file ed applicazioni di vecchio tipo. Ritorna, ormai è una sorta di incubo, il problema della resource fork.

Ricordo per i più distratti. I file all'interno del Macintosh nascono bifidi. Il loro contenuto è diviso in due parti: la data fork, dove sono contenuti i dati, e la resource fork, dove sono contenute le risorse. Questa divisione si riflette abbastanza bene nei file di dati (costituiti spesso in prevalenze dalla data fork) e nei file che contengono applicazioni (costituiti massimamente da risorse). Con Mac OS X invece, i file sono solitamente costituiti dalla sola data fork, dal momento che le risorse sono trattate in altro modo (i bundle dell'applicazione, ad esempio). Sto semplificando, mi rendo conto, ma è solo per tirarla breve. Quando con Cocoa si recuperano le informazioni relative ad un file, si ottiene la dimensione della sola data fork. Quindi, sono persi tutti i numeri relativi alla resorce fork. Ciò spiega le discrepanze tra le dimensioni riportate dal terminale, dalla finestra delle info del Finder, eccetera.

È giunto allora il momento di risolvere la questione una volta per tutte. Non c'è altra possibilità che ricorrere a Carbon. Ricordo (ancora una volta) per i più distratti. Sopra il sistema operativo dal cuore Unix, sono disponibili diversi ambienti all'interno dei quali è possibile costruire applicazioni per Macintosh. Uno di questi è Cocoa, argomento di questo sito. Un altro è appunto Carbon. Carbon è costituito in pratica da una serie di funzioni (una libreria, direbbe qualcuno) utilizzabili da ogni linguaggio di programmazione per scrivere applicazioni Macintosh. Le funzioni sono quelle ereditate (ma con la faccia ripulita per l'occasione) dalle precedenti versioni del sistema operativo (fino alla 9 compresa). Chi, come me molti anni fa, si dilettava di programmazione su Macintosh, riconosce molti vecchi amici e può riutilizzare (con debita attenzione) i vecchi volumi di Inside Macintosh.

Risorse con Carbon

La sta tirando per le lunghe, in quanto in realtà le cose sono piuttosto semplici. Cominciamo col recuperare un po' di informazioni da un file; mi interessa in pratica solo la dimensione della resource fork, visto che le altre informazioni utili sono recuperate con il metodo fileAttributesAtPath: della classe NSFileManager.

Il primo passo è specificare quale file mi interessa. Con NSFileManager un file è identificato dal suo path completo. Con Carbon, si usa tutt'altro: un File System Representation, o FSRef, una struttura dati che identifica in maniera completa ed equivalente un file. Piuttosto che perdere tempo a spiegare come sia fatta questa struttura, conviene utilizzare una funzione Carbon adatta a nascondere ogni dettaglio inutile. La funzione FSPathMakeRef richiede in ingresso una stringa che contiene il percorso completo del file secondo la rappresentazione del file system considerato, e restituisce due dati: la struttura FSRef completa delle informazioni necessarie, ed un valore che dice se si tratta di una directory o meno. La funzione ritorna un codice di errore, che vale la costante noErr se tutto è andato bene.

Con la struttura FSRef ottenuta è a questo punto facile recuperare le informazioni che mi servono: mi giovo della funzione FSGetCatalogInfo, che fa al caso mio.

La funzione sembra avere un brutto aspetto ad una prima occhiata, ma poi in realtà è un pezzo di pane. Gli si deve fornire una FSRef per specificare un file, ed un codice che individua quali informazioni recuperare. Da tutto ciò produce una notevole quantità di dati; in primo luogo, raccolte in una struttura FSCatalogInfo i dati richiesti, e poi, opzionalmente, il nome del file e tante altre cose che in questa applicazione non ci interessano. Mi sto dilungando troppo; ecco quindi il codice da aggiungere al metodo initWithPath: della classe LSFileInfo.

// se il file e' effettivamente un vero file
if ( [[self fileType] isEqualToString: NSFileTypeRegular ])
{
    OSStatus         status ;
    FSRef             myFSRef;
    FSCatalogInfo        catinfo ;
    unsigned long long    tmpSize ;
    float            d1, d2, d3 ;
    // mi faccio qualche scrupolo in piu'
    // potrebbe esserci una resource fork
    // costruisco una FSRef
    status = FSPathMakeRef ([aFile fileSystemRepresentation], &myFSRef, NULL);
    // e poi indago con Carbon le dimensioni della resource
    if (status == noErr)
        status = FSGetCatalogInfo (&myFSRef, kFSCatInfoRsrcSizes,
            & catinfo, NULL, NULL, NULL);
    tmpSize = [self fileSize] + catinfo.rsrcLogicalSize ;
    [ self setFileSize: tmpSize ];
}

Da notare che tutte queste operazioni sono svolte solo se il file in esame è un vero e proprio file, e non qualcos'altro (una directory, o altre diavolerie tipiche di Unix). Ecco quindi che chiamo le due funzioni citate prima, per ottenere solo il campo rsrcLogicalSize della struttura dati FSCatalogInfo prodotta. Piuttosto che mantenere separata questa dimensione (che andrebbe gestita, nella struttura dati, nelle preferenze, nella finestra delle info, eccetera), ho preferito aggiungerla alla dimensione della data fork ed ottenere un dato globale della dimensione del file.

Conto i file

A questo punto sembrerebbe tutto a posto; tuttavia, avendo cominciato con Carbon, tanto vale andare avanti, e fare qualcosa di più corretto con la barra di avanzamento lavori. Utilizzare la dimensione dei file come criterio sullo stato di catalogazione non è proprio la cosa più giusta da farsi: se ci sono tanti piccoli file ed un solo grande file, la barra non è equilibrata. La cosa migliore sarebbe lavorare col numero di file. Eppure, con Cocoa, il numero di file presenti su di un volume non è disponibile all'inizio delle operazioni. Invece, con Carbon, tale dato è disponibile: basta utilizzare la funzione giusta, FSGetVolumeInfo. Anche qui, sono necessarie due informazioni in ingresso: la specifica del volume e quella delle informazioni desiderate. La funzione produce molte informazioni, ma a me interessa un solo campo della struttura FSVolumeInfo, quello che appunto riporta il numero di file contenuti nel volume.

Il problema nasce nella specifica del volume, che deve essere data attraverso un codice identificativo (il volume referente number). Dopo una rapida (si fa per dire) indagine, scopro che tale codice è possibile ricavarlo come un sottoprodotto della funzione FSCatalogInfo, in uno dei campi delle strutture in precedenza trascurate. Ancora una volta, mi disinteresso dei dettagli e procedo spedito secondo i miei scopi. Modifico il metodo performAddFilesModal: di CatalogDoc.

while ( volPath )
{
    VolInfo     * fInfo ;
    // il volPath ho il nome completo del volume
    FSSpec myFSSpec ;
    FSRef myFSRef;
    FSVolumeInfo myInfo;
    FSCatalogInfo    catinfo ;
    OSStatus status = FSPathMakeRef ([volPath fileSystemRepresentation],
            &myFSRef, NULL);
    if (status == noErr)
        status = FSGetCatalogInfo (&myFSRef, kFSCatInfoNone, & catinfo, NULL,
                             & myFSSpec, NULL);
    // adesso in myFSSpec ho il mio FSSpec
     status = FSGetVolumeInfo ( myFSSpec.vRefNum, 0, NULL, kFSVolInfoFileCount, & myInfo,
        NULL, NULL );
    fsattrs = [[NSFileManager defaultManager] fileSystemAttributesAtPath: volPath];
    totalSpace = [[fsattrs objectForKey:NSFileSystemSize] unsignedLongLongValue] ;
    freeSpace = [[fsattrs objectForKey:NSFileSystemFreeSize] unsignedLongLongValue] ;
    usedspace = totalSpace - freeSpace ;
    [ WaitPanCtrl setMaxValue: myInfo.fileCount ];
    [ WaitPanCtrl setCurrentValue: 0 ];
    // eccetera
}

C'è una catena di tre funzioni Carbon il cui unico scopo è di produrre la variabile myInfo.fileCount; questa è utilizzata direttamente per dare il valore massimo della barra di scorrimento.

Non rimane altro che effettuare una modifica all'interno di initWithPath: della classe LSFileInfo:

    [ WaitPanCtrl setCurrentValue: ([ WaitPanCtrl getCurrentValue ] + 1)] ;

Così facendo è possibile gestire in maniera un po' più pulita la barra che indica lo stato avanzamento lavori.

Licenza Creative Commons
Eccetto dove diversamente specificato, i contenuti di questo sito sono rilasciati sotto Licenza Creative Commons.
Pagina a cura di Livio Sandel (macocoa2012@gmail.com).