Mediafire: script Bash per upload di files da console

Di seguito pubblico uno script utile per chi vuole caricare su mediafire uno o più file da console senza dover utilizzare un browser web. Questo script è utile in caso di job notturni o programmi che non richiedono l’intervento dell’utente.

#!/bin/bash

#
# Copyright (c) 2011 vittorio benintende
# (vittorio@lucullo.it - http://www.morzello.com).
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# 3. Neither the name of the Site nor the names of its contributors
#    may be used to endorse or promote products derived from this software
#    without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE SITE AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE SITE OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
#
# --- PARAMS ---
#
# login and password must be url-encoded!
#

MF_LOGIN='mediafire%40morzello.com'
MF_PWD='mypass'

# --- PARAMS ---
#
# DO NOT MODIFY LINES BELOW
#

MF_FILES="$@"

if [ "z${MF_FILES}" = "z" ]; then
	echo
	echo "No file(s) specified."
	echo
	echo "USAGE: $0 : file1 file2 ..."
	echo

	exit 1
fi

OUTPUT_FILE=$(mktemp)
COOKIE_FILE=$(mktemp)

rdom () { local IFS=\> ; read -d \< E C ;}

wget -q --save-cookies ${COOKIE_FILE} --keep-session-cookies -O /dev/null "https://www.mediafire.com/"

wget -q --load-cookies ${COOKIE_FILE} --save-cookies ${COOKIE_FILE} --keep-session-cookies --referer="https://www.mediafire.com/" \
     --post-data "login_email=${MF_LOGIN}&login_pass=${MF_PWD}&login_remember=on&submit_login.x=97&submit_login.y=18" \
     -O /dev/null "https://www.mediafire.com/dynamic/login.php"

wget -q --load-cookies ${COOKIE_FILE} --save-cookies ${COOKIE_FILE} --keep-session-cookies --referer="https://www.mediafire.com/myfiles.php" \
     -O ${OUTPUT_FILE} "https://www.mediafire.com//basicapi/uploaderconfiguration.php?45144"

UPLOAD_SESSION=$(while rdom; do
    if [[ ${E} = "upload_session" ]]; then
        echo ${C}
        exit
    fi
done < ${OUTPUT_FILE})

# echo "Upload Session = ${UPLOAD_SESSION}"

UKEY=$(while rdom; do
    if [[ ${E} = "ukey" ]]; then
        echo ${C}
        exit
    fi
done < ${OUTPUT_FILE})  

# echo "UKey = ${UKEY}"

TRACKKEY=$(while rdom; do
    if [[ ${E} = "trackkey" ]]; then
        echo ${C}
        exit
    fi
done < ${OUTPUT_FILE})

# echo "Track Key = ${TRACKKEY}"

FOLDERKEY=$(while rdom; do
    if [[ ${E} = "folderkey" ]]; then
        echo ${C}
        exit
    fi
done < ${OUTPUT_FILE})  

# echo "Folder Key = ${FOLDERKEY}"

MFULCONFIG=$(while rdom; do
    if [[ ${E} = "MFULConfig" ]]; then
        echo ${C}
        exit
    fi
done < ${OUTPUT_FILE})  

# echo "MFUL Config = ${MFULCONFIG}"

MF_USER=$(while rdom; do
    if [[ ${E} = "user" ]]; then
        echo ${C}
        exit
    fi
done < ${OUTPUT_FILE})  

# echo "User = ${MF_USER}"

for FILENAME in ${MF_FILES}; do

	SHORT_FILENAME=$(basename "${FILENAME}")
	FILESIZE=$(stat -c%s "${FILENAME}")

	wget -q --load-cookies ${COOKIE_FILE} --save-cookies ${COOKIE_FILE} --keep-session-cookies --referer="https://www.mediafire.com/myfile.php" \
	     --header="Content-Type: application/octet-stream" \
	     --header="X-Filename: ${SHORT_FILENAME}" \
	     --header="X-Filesize: ${FILESIZE}" \
	     --post-file="${FILENAME}" \
	     -O ${OUTPUT_FILE} "https://www.mediafire.com/douploadtoapi/?type=basic&ukey=${UKEY}&user=${MF_USER}&uploadkey=${FOLDERKEY}&filenum=0&uploader=0&MFULConfig=${MFULCONFIG}"

	RESKEY=$(while rdom; do
	    if [[ ${E} = "key" ]]; then
	        echo ${C}
	        exit
	    fi
	done < ${OUTPUT_FILE})

	# echo "${FILENAME} > ${RESKEY}" 

	# get result

	RUN=1

	while [ ${RUN} -eq 1 ]; do
		wget -q --load-cookies ${COOKIE_FILE} --save-cookies ${COOKIE_FILE} --keep-session-cookies --referer="https://www.mediafire.com/myfile.php" \
		     -O ${OUTPUT_FILE} "https://www.mediafire.com/basicapi/pollupload.php?key=${RESKEY}&MFULConfig=${MFULCONFIG}"

		QUICKKEY=$(while rdom; do
		    if [[ ${E} = "quickkey" ]]; then
		        echo ${C}
		        exit
		    fi
		done < ${OUTPUT_FILE})

		FILEERROR=$(while rdom; do
		    if [[ ${E} = "fileerror" ]]; then
		        echo ${C}
		        exit
		    fi
		done < ${OUTPUT_FILE})

		# echo "${QUICKKEY} ; ${FILEERROR}" 

		RUN=0
		if [ "z${FILEERROR}" = "z" ] && [ "z${QUICKKEY}" = "z" ]; then
			RUN=1

		fi

		#
		#..

		#...
		#... File too large. 1 0 
		#... File too small. 2 0 
		#... Archive is bad. 3 0 
		#... Archive is bad or password protected. 4 0 
		#... Virus found. 5 0  
		#... File already uploaded. 13 0  
		#... Archive has an unknown problem. 9 0 
		#... Invalid image. 10 0 
		#... File lost. 6 1 
		#... Error scanning file. 7 1 
		#... Disk error. 8,11 1 
		#... Database error. 12 1 
		#..
		#	

		case "${FILEERROR}" in
			1) FILEERROR=" 1 : File too large."
			;;
			2) FILEERROR=" 2 : File too small."
			;;
			3) FILEERROR=" 3 : Archive is bad."
			;;
			4) FILEERROR=" 4 : Archive is bad or password protected."
			;;
			5) FILEERROR=" 5 : Virus found."
			;;
			13) FILEERROR="13 : File already uploaded."
			;;
			9) FILEERROR=" 9 : Archive has an unknown problem."
			;;
			10) FILEERROR="10 : Invalid image."
			;;
			6) FILEERROR=" 6 : File lost."
			;;
			7) FILEERROR=" 7 : Error scanning file."
			;;
		 8) FILEERROR=" 8 : Disk error."
			;;
			11) FILEERROR="11 : Disk error."
			;;
			12) FILEERROR="12 : Database error."
			;;
			*) FILEERROR="0 : Success."
			;;
		esac

	done 

	echo "${FILEERROR} | ${FILENAME} > ${QUICKKEY}"
done

rm ${OUTPUT_FILE}
rm ${COOKIE_FILE}

Prima di iniziare
Prima di utilizzare lo script è necessario impostare i parametri di login e password in uso su medaifire. Bisogna ricordare che questi paramentri sono url-encoded; questo perchè vengono esposti direttamente nell’url di login. Ad esempio:

mediafile@morzello.com -> mediafire%40morzello.com

Usare lo script
Per utilizzare lo script basta indicare il file o i file da inviare al server:

./mfup.sh myfile.txt all.* /root/repo/*

Ricordate però che non verranno prese in considerazione le sottocartelle.

Alla prossima

OpenVPN – NTLM e quel proxy infame

Questo articolo è rivolto a coloro che hanno problemi a far dialogare OpenVPN con proxy che richiedono autenticazione NTLM.

Al momento della stesura di questo articolo, la versione di ovpn è la 2.1.3, versioni successive a questa potrebbero non soffrire dello stesso male.

Utilizzando l’autenticazione di tipo NTLM ho notato che la comunicazione si interrompe subito dopo l’invio del messaggio di tipo 1 da parte del client. In pratica il proxy non si degna di dare una risposta negativa o positiva che sia.

Per maggiori informazioni sulla comunicazione tipo NTLM potete visitare il sito:

http://davenport.sourceforge.net/ntlm.html

Dando uno sguardo al codice sorgente ho notato che la stringa inviata dal client al proxy è la seguente:

TlRMTVNTUAABAAAAAgIAAA==

ed è una costante definita nel file ntlm.c

Dopo aver portato a termine qualche prova con uno sniffer di rete mi sono accorto che altri prorammi come ad esempio firefox, funzionano perfettamente con il proxy così mi sono chiesto quali fossero le differenze. In effetti il messaggio di fase 1 inviato dal browser è sostanzialmente diverso:

TlRMTVNTUAABAAAAB4IIogAAAAAAAAAAAAAAAAAAAAAFASgKAAAADw==

e sembra essere più gradito.

A questo punto ho modificato il codice sorgente nell’unica riga necessaria:

const char *
ntlm_phase_1 (const struct http_proxy_info *p, struct gc_arena *gc)
{
  struct buffer out = alloc_buf_gc (96, gc);
  /* try a minimal NTLM handshake
   *
   * http://davenport.sourceforge.net/ntlm.html
   *
   * This message contains only the NTLMSSP signature,
   * the NTLM message type,
   * and the minimal set of flags (Negotiate NTLM and Negotiate OEM).
   *
   */
  // buf_printf (&out, "%s", "TlRMTVNTUAABAAAAAgIAAA==");
   buf_printf (&out, "%s", "TlRMTVNTUAABAAAAB4IIogAAAAAAAAAAAAAAAAAAAAAFASgKAAAADw==");
  return (BSTR (&out));
}

e dopo aver ricompilato l’eseguibile, tutto funziona correttamente.

Alla prossima.

MySQL – abilitare l’accesso a root dalla rete

Può capitare a volte di voler accedere all’istanza presente su una macchina dalla quale non è possibile utilizzare strumenti visuali quali:

  • MySQL Administrator
  • MySQL Workbench

è ad esempio il caso di CentOS, dove questi strumenti sono difficili da installare. A mio avviso un metodo veloce per arginare il problema è quello di utilizzare il comando mysql in locale per abilitare l’accesso all’utente root dalla rete. Una volta apportate tutte le modifiche del caso, sarà sufficiente ristabilire le restrizioni volute.

Per accedere alla console di mysql basta usare il comando:

mysql -uroot -p

dopo aver digitato la password ci ritroveremo nell’ambiente del db e potremo abilitare l’accesso dall’esterno con due comandi:

grant all privileges on mysql.* to root@"NOMEPC" identified by 'MIA_PASSWORD';
flush privileges;

Dove NOMEPC è l’indirzzo della macchina dalla quale vogliamo accedere (indicando ‘%’ abilitiamo tutta la rete) e MIA_PASSWORD è la password che vogliamo usare per la connessione.

Ora è possibile fare le modifiche del caso da un altro pc.

Alla prossima.

Gestire le cartelle della tombola in Excel

Certo che per inserire questo articolo nella sezione dell’Office Automation ci vuole coraggio ma in fin dei conti si tratta sempre di Excel.
In queste vacanze ho giocato a tombola con la mia famiglia; ovviamente non ho vinto nulla ma mi sono divertito ugualmente. Un geek malato di informatica come me non poteva escludere il computer in una occasione come questa.
Ovviamente al posto di usare le lenticchie, le bucce d’arancia o altri segnaposto per tenere traccia dei numeri estratti ho decisao di usare un foglio di calcolo.
Per dirla tutta ho usato OpenOffice ma la sostanza non cambia dal momento che ho fatto uso delle formule standard.

In pratica sono bastati un paio di cerca.vert per tenere traccia dei numeri estratti e delle eventuali vincite.


Per chi fosse interessato, alla fine di questo articolo è possibile scaricare sia la versione xls che ods.

Il file si compone di tre fogli:

  • Estrazioni – E’ una sorta di deshboard in cui visualizzare lo stato delle cartelle ed i numeri che di volta in volta vengono estratti. Man mano che si procede con l’estrazione è possibile vedere cosa succede su ogni cartella: dall’ambo alla tombola;
  • Cartelle – Contiene i dati relativi a tutte le cartelle possedute. In particolare contiene i numeri così come sono riportate sulla cartella originale. E’ qui che l’utente deve inserire i numeri appartenenti alle cartelle in tre righe da cinque collonne ciascuna;
  • Vincita – Contiene un pannello che consente di calcolare facilmente le quote da assegnare alla tombola, alla cinquina e così via fino all’ambo. Il calcolo tiene conto del costo di ogni singola cartella e del numero di cartelle assegnate (comprensive delle sei cartelle che costituiscono il tabellone);

Si tratta di certo di un prodotto migliorabile ma sinceramente io non sono andato oltre dal momento che si tratta di un lavoro svolto in mezz’ora durante le vacanze di natale.

per scaricare il file cliccate qui.

alla prossima.

Una versione avanzata di Run As

Gli utenti di Linux sanno bene che non si dovrebbe mai utilizzare l’utenza amministrativa (root) per uso quotidiano. L’uso dovrebbe essere limitato alla manutenzione ordinaria (aggiornamenti) e straordinaria (installazione di programmi e nuovi componenti hardwere) del computer.

Questa regola è ancor più valida per gli utenti Windows dove, a parer mio, l’abuso dell’utenza amministrativa è più marcata.

In Linux, per accedere all’utenza di root dopo aver fatto il logon è possibile usare il comando "su" mentre in Windows si può utilizzare il comando "runas". Nonostante i due comandi siano essenzialmente simili, il loro utilizzo nella pratica non è il medesimo.

Mi sono reso conto negli anni che in Windows non è sempre agevole sostituirsi all’Amministratore per eseguire singoli comandi.

Per eseguire un comando come altro utente è sufficiente cliccare sull’icona con il tasto destro. Se tutto è eseguito correttamente nell’elenco comparirà l’opzione "Esegui come…" oppure "Run as…". Nelle versioni precedenti del sistema operativo potrebbe essere necessario premere contemporaneamente il tasto [SHITF].

Quando però è necessario eseguire diversi comandi le cose si complicano.

Su Windows 2000 è possibile eseguire una shell visuale "Esplora Risorse" come altro utente mentre in Windows XP no. Nella migliore delle ipotesi, l’utente resta invariato e nelle peggiori si rischia di dover reinstallare il sistema operativo.

A questo punto ho pensato ad un piccolo vbscript che possa risolvere il problema agevolmente.

Supponiamo di voler installare una stampante oppure editare un file di testo presente in c:\windows\system32.

Cliccando con il tasto destro sull’icona non compare l’opzione "Esegui come…" perché non si tratta di un programma.

L’idea è quella di creare una voce nel menu contestuale con la quale eseguire gli applicativi in base all’estensione dei file.

Per prima cosa è necessario creare un file vbs in windows. Nel nostro esempio il file sarà creato con il nome c:\windows\morzello_runas.vbs

option explicit

dim WshShell
dim strLogin
dim strParams
dim objArgs
dim I
Const TITLE="Morzello RunAs 1.1"
Set objArgs = WScript.Arguments

For I = 0 to objArgs.Count – 1
strparams= strParams & " " & objArgs(I)
Next

‘eliminiamo il primo carattere perchè non fa parte del parametro.
If Len(strParams)>1 Then strparams=Right(strparams,Len(strparams)-1)

strLogin=InputBox ("Inserire il ‘Login’ dell’utente con cui si vuole eseguire il comando:",TITLE, "Administrator")

If strLogin="" Then wscript.quit (1)

Set WshShell = WScript.CreateObject( "WScript.Shell" )
wscript.echo "%comspec% /C runas /env /utente:" & strLogin & " ""start " & strParams & """"
WshShell.Run "%comspec% /C runas /env /utente:" & strLogin & " ""start " & strParams & """" ,1,false

Lo script si limita a chiedere login e password dell’utente desiderato, quindi carica il file cone le credenziali desiderate.

Per rendere utile questo script è possibile inserire una opzione aggiuntiva alla shell simile a quella già presente. Questa opzione la chiameremo per comodità "Morzello Run As".

Di seguito riporto il contenuto del file "morzello_runas.reg" da aggiungere al file di registro:

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\*\shell]

[HKEY_CLASSES_ROOT\*\shell\Morzello Run As]

[HKEY_CLASSES_ROOT\*\shell\Morzello Run As\command]
@="cscript c:\\windows\\morzello_runas.vbs %1"

Per aggiungere il comando al registro di sistema è sufficiente fare doppio click sul file con credenziali amministrative.

A questo punto è possibile ad esempio cliccare con il tasto destro su un file txt ed eseguirlo come altro utente. Utile ad esempio per editare il file di host.

I sorgenti di questo articolo sono disponibili qui

Alla prossima.

Regular Expressions. Un aiuto per neofiti

Le espressioni regolari (regular expressions) per chi non è esperto, possono essere veramente frustranti da gestire.

Devo ammettere che anche io ho qualche problema a volte, specie se si tratta di implementare RE con linguaggi che non uso molto frequentemente.

Esiste però un modo per “testare” le RE online. Di seguito propongo alcuni siti da me utilizzati, suddivisi per linguaggio di programmazione:

Java (ed altri linguaggi come ad esempio Javascript e Apex):
http://www.fileformat.info/tool/regex.htm (mediante java.util.regex)

Python:
http://re-try.appspot.com/

Bash (sed e grep):
http://www.linuxjournal.com/content/bash-regular-expressions (codice sorgente)

PHP e Perl:
http://regex.larsolavtorvik.com/

Spero che questo elenco sia utile anche se non di certo esaustivo.

Se avete altre segnalazioni da fare siete i benvenuti.

Alla prossima.

Qual’è il mio indirizzo IP su Internet?

Se vuoi sapere qual’è il tuo indirizzo IP sulla rete in questo momento, te lo dico io:





Di seguito il codice asp utilizzato per raccogliere questa informazione:

<html>
<body>
<%
Dim myIP

myIP = Request.ServerVariables(“HTTP_X_FORWARDED_FOR”)
If myIP=”" Then myIP = Request.ServerVariables(“REMOTE_ADDR”)
response.write “Il tuo è: ” & myIP & “.”
%>
</body>
</html>

Scopa per ZX Spectrum

Qualche giorno fa, facendo un po’ di ordine tra le mie cose vecchie e polverose è saltata fuori una cassetta audio. Incuriosito l’ho inserita in un mangianastri (anche quello vecchio e polveroso) per capire cosa ci fosse inciso sopra.
Con immensa sorpresa ho scoperto che non si trattava di musica. Infatti appena ho premuto play ho sentito l’inconfondibile “piiiiiiiiiiiiiiiiiiiii(schic) piiiiiiiiiiiiiiiiiiiiiiiiiiiiii(scccccccccccccc)” tipico del mio caro, vecchio ZX Spectrum 48k.
A questo punto ho scaricato un emulatore per PC. Non vedevo l’ora di capire quali dati contenesse il nastro.
Dopo aver passato diversi minuti a capire come fare una conversione dell’audio su file sono finalmente riuscito a caricare i dati nell’emulatore.
Non potete capire l’emozione. Si trattava del gioco della scopa; un programma scritto in BASIC tantissimi anni fa del quale mi ero totalmente dimenticato.
Dopo aver giocato un po’ ho dato un breve sguardo al sorgente. Certamente scrivere un gioco che doveva girare in 32k di memoria RAM non era facile ma io all’epoca programmavo veramente con i piedi.
Mi è bastato dare un’occhiata veloce al modo in cui vengono mischiate le carte.
Comunque mi rendo conto che non era una cosa banale. Il programma è dotato di schermata iniziale. Per farla sono ricorso ad un escamotage. Ricordo ora di aver fatto un disegno usando diversi fogli di un quadernone a quadretti e di aver raggruppato i quadretti in blocchi di 8×8. Successivamente ho definito i mitici UDG (User Defined Graphic) e con diversi MERGE$ sono riuscito a disegnare uno splendido “asso di bastoni” napoletano degno del più agguerrito camionista meridionale.

Per chi volesse farsi quattro risate, potete scaricare il nastro da qui.

Excel – Totali in grassetto con il minimo sforzo

La scorsa settimana sono stato coinvolto in un progetto molto impegnativo legato al costo del prodotto di Fiat Group Automobiles (FGA), nell’ambito del quale era necessario simulare la chiusura di fine mese di alcuni stabilimenti.

Come sempre accade in questi casi, Excel viene impiegato sopratutto per lo scambio di informazioni e per la generazione di report.

Poiché gli stabilimenti del gruppo Fiat sono veramente tanti e per ognuno di essi è stato necessario produrre un numero consistente di documenti, mi sono reso conto subito che una cospicua parte del tempo era destinata alla formattazione dei fogli lasciando poco tempo alle attività di controllo.

In particolare un mio collega mi ha chiesto se fosse possibile mettere in grassetto le righe dei totali in modo facile e veloce.
Ho pensato subito alla formattazione condizionale ma c’era un limite da superare; le condizioni di formattazione per una cella vengono calcolate sul valore della cella stessa.

La formattazione condizionale
Per mezzo della formattazione condizionale è possibile personalizzare lo stile di una cella in base al valore che contiene. Ad esempio è possibile visualizzare un numero in rosso quando è negativo ovvero in verde se positivo.
Nella figura riportata in basso è possibile vedere un esempio:

Nell’esempio riportato in precedenza, il carattere della cella diventa verde se il valore contenuto è maggiore di 0 altrimenti resta del valore impostato nella formattazione originaria.
E’ possibile specificare più condizioni contemporaneamente è precisamente un massimo di tre.
Formattare le celle in questo modo è utile quando i dati sono calcolati, perchè non c’è bisogno di modificare a mano il formato. Purtroppo però, alla base della condizione è possibile specificare solo il valore ovvero la formula contenuta nella cella.

Il Conto Economico
Nel mio caso, il report del conto economico era simile a quello indicato nella figura in basso. Ovviamente i valori inseriti sono fittizi ma il layout si avvicina molto all’originale.

L’idea è quella di copiare in tutte le celle del foglio, una formattazione condizionale che imposti il formato grassetto in base alla seguente regola:

La cella è da riportare in grassetto se sulla stessa riga, in corrispondenza della colonna D, il valore inizia per ‘TOT’

Testare condizioni su celle diverse
Come dicevo prima però, la condizione può essere testata solo sul contenuto della cella stessa, di conseguenza è necessario implementare una formula che restituisca il valore della cella testata in caso di esito negativo. Se ad esempio la cella da testare è A1, allora la formula potrebbe essere:

=A1+SE(SINISTRA($D1;3)=”TOT”;0;1)



Se i primi tre caratteri della cella D1 contengono TOT, allora la formula restituirà A1 altrimenti restituirà A1+1. In questo modo posso utilizzare la formattazione condizionale impostando il parametro “uguale a“.
E’ possibile trascinare la formattazione della cella A1 in qualunque cella (notare il dollaro che precede la lettera D).
Esiste però un problema con le celle che non contengono un valore numerico poichè in questo caso non è possibile fare una somma.

In effetti per i valori testuali dovrei modificare la formula nel modo seguente:

=A1 & SE(SINISTRA($D1;3)=”TOT”;”";”1″)



Al posto di una somma verrebbe fatta una concatenazione ottenendo lo stesso risultato.
Non è però conveniente utilizzare due formattazioni condizionali differenti perchè bisognerebbe sempre controllare il valore contenuto nelle celle. Per questo motivo, la formula definitiva controlla il tipo di dati contenuti nella cella ed applica la somma o la concatenazione di conseguenza:

=SE(TIPO(A1)=2;A1 & SE(SINISTRA($D1;3)=”TOT”;”";”1″);A1 + SE(SINISTRA($D1;3)=”TOT”;0;1))




Ora è sufficiente selezionare l’intero foglio e copiare la fomattazione della cella A1.

Automatizzare il tutto
Per automatizzare il tutto creiamo una piccola macro in Visual Basic:

Sub TotaliGrassetto()
    Application.ScreenUpdating = False
    Cells.Select
    Selection.FormatConditions.Delete
    Selection.FormatConditions.Add Type:=xlCellValue, Operator:=xlEqual, _
        Formula1:=”= SE (TIPO(A1) = 2; A1 & SE(SINISTRA($D1;3) = “”TOT”"; “”"”; “”1″”);A1 + SE(SINISTRA($D1;3) = “”TOT”"; 0 ; 1 ))”
    With Selection.FormatConditions(1).Font
        .Bold = True
        .Italic = False
    End With
    Range(“A1″).Select
    Application.ScreenUpdating = True
End Sub




Creazione di una add-in MS-Excel
Per rendere le operazioni più veloci è possibile includere la procedure appena descritta in file di tipo xla pronta per essere importata mediante i componenti aggiuntivi di Excel.
Utilizzare una add-in è comodo altrimenti bisognerebbe incollare tutto nello stesso file.

Conclusioni
Al fondo di questo articolo ho inserito il link ai sorgenti.
Nel sorgente trovate anche una procedura che crea un menù per richiamare la macro (avevamo detto che bisogna essere veloci e così ho inserito anche una shortcut da tastiera [CTRL-ALT-T]).

Sorgenti
totali_grassetto.zip

Array ed Indici in VB

La settimana scorsa, durante la pausa pranzo, parlavo con alcuni colleghi del più e del meno.
Parlare con me però significa quasi sempre parlare di informatica.
E’ venuto fuori il solito discorso sulla programmazione ed in particolare si è parlato di Visual Basic.

Il VB, come anche altri linguaggi interpretati, è uno strumento che consente di ottenere soluzioni valide scrivendo poche righe di codice. L’ideale per implementazioni veloci ad hoc.
Durante la breve chiacchierata mi è stata rivolta questa domanda:

Gli indici di un array in vb partono da zero a meno di non specificare il contrario. Ma non sarebbe più semplice se partissero da uno?

E’ seguito un breve silenzio ed un sorriso da parte mia. Odio le persone che rispondono alle domande con altre domande ma questa volta è capitato a me che replico:

Conosci l’assembly?

Come risposta è veramente antipatica e poco professionale.
Il fatto è che se durante la pausa pranzo, incomincio a parlare di registri, locazioni di memoria e contatori, la gente mi guarda male e finisce per dire: “ma perchè ho fatto questa domanda?”
Lo ammetto, la sintesi non è il mio forte.

Comunque sia, chi ha studiato l’assembly conosce bene il motivo per cui gli array, a meno di non specificare diversamente, partono con indici pari a zero.
Alla fine della pausa pranzo, avevo già in mente un piccolo test per fare una dimostrazione.

Di seguito riporto due procedure scritte in Visual Basic che lasciano il tempo che trovano. Si tratta di poche righe che non hanno alcuna pretesa ma che secondo me possono rendere l’idea.

Option Explicit

Sub test1()
Dim j(0 To 9) As Long
Dim x As Integer
Dim Conta As Long
Dim Time1 As Date
Dim Time2 As Date

Time1 = Now
Time2 = DateAdd(“s”, 60, Time1)
Conta = 0
While Now < Time2
  For x = LBound(j) To UBound(j)
    j(x) = Conta
    Conta = Conta + 1
  Next
Wend
MsgBox Conta
End Sub

Sub test2()
Dim j(1 To 10) As Long
Dim x As Integer
Dim Conta As Long
Dim Time1 As Date
Dim Time2 As Date

Time1 = Now
Time2 = DateAdd(“s”, 60, Time1)
Conta = 0
While Now < Time2
  For x = LBound(j) To UBound(j)
    j(x) = Conta
    Conta = Conta + 1
  Next
Wend
MsgBox Conta
End Sub

test1 e test2 sono 2 procedure identiche, si differenziano solamente per la dichiarazione dell’array j che per test1 va da 0 a 9 mentre per test2 va da 1 a 10. Sempre 10 elementi per entrambi gli array.
Vinene chiesto al programma di eseguire un ciclo per la durata di un minuto. In detto ciclo una variabile viene costantemente sommata ad 1 come fosse un contatore. Alla fine della esecuzione, viene mostrato all’utente il numero di volte che è stato eseguito il ciclo. Poichè ad ogni iterazione viene aggiornato un elemento diverso dell’array, il numero di volte che il ciclo viene eseguito dipende anche dall’array stesso.

Proviamo adesso ad eseguire test1 e successivamente test2 prendendo nota del valore di Conta alla fine di ogni esecuzione.

Nel mio caso, il valore per test1 è stato 329.842.430 mentre per test2 è stato 326.544.260.
E’ inutile dire che il valore dipende dalla velocità del processore e del sistema operativo ma quello che a noi interessa non è il valore assoluto ma la semplice differenza.

Durante la prova dunque, test1 è stato più veloce di test2 perchè ha eseguito 3.298.170 cicli in più.

Questo può sembrare banale ad un programmatore di visual basic, sopratutto ora che i processori sono molto veloci e la memoria non è più un problema.
In ambienti in cui la velocità è molto importante però, l’attenzione del programmatore è un aspetto da non sottovalutare. C’è da dire comunque che chi vuole ottenere la massima velocità dal processore non si affida certo a vb.

Mi capita però di vedere in giro codice bizzarro e ridondante come ad esempio:

if blVar = true then …

Che senso ha testare se VERO è uguale a VERO oppure VERO è uguale a FALSO? E’ più semplice testare la condizione con:

if blVar then …

L’esecuzione risulterà sicuramente più veloce.

Abbiamo eseguito il test ma non abbiamo ancora dimostrato perchè gli array con indice iniziale pari a zero sono più veloci degli altri.
Per fare le cose come si deve, avrei dovuto prendere un disassemblatore e portarmi nel punto in cui avviene l’esecuzione delle due procedure per analizzarne il contenuto.

Ma questo non è un documento tecnico e non vuole essere una lezione di assembly.

Chi volesse approfondire l’argomento può leggere il quinto capitolo presente in: The Art Of Assembly un libro che per me è stato fondamentale e che può essere scaricato dal link riportato alla fine di questo articolo.
Ed è proprio dal quinto capitolo di questo meraviglioso libro che prendiamo spunto per dare la dostra spiegazione.

Come è possibile vedere dalla immagine riportata in alto, la memoria viene vista dal computer come una grande pila di mattoncini. Ogni mattoncino è una locazione di memoria delle dimenzioni necessarie per conservare la variabile (nel nostro esempio l’array è composto da 10 elementi di tipo Long. In questo caso ogni mattoncino rappresenta 4 byte).

Di consenguenza, per accedere ad un elemento dell’array è necessario applicare la seguente formula:

Element_Address = Base_Address + ((Index – Initial_Index) * Element_Size)

Base_Address è l’indirizzo di memoria da cui parte l’array, Index è l’indice dell’elemento che ci interessa, Initial_Index è l’indice del primo elemento dell’array ed Element_Size è la dimensione occupata in memoria da ciascun elemento.
Se l’indice iniziale dell’array è zero, allora la formula può essere semplificata nel modo seguente:

Element_Address = Base_Address + (Index * Element_Size)

Per questo motivo è preferibile utilizzare array con indici iniziali pari a zero.

Per saperne di più:

The Art Of Assembly (download formato pdf)
Visual Basic Array Tutorial