Encoder-Decoder Codi Binari – Codi Gray

Reprenc el bloc després d’una aturada d’un any!

He dedicat bona part del temps que dedicava al bloc a estudiar. Des de fa un any i mig estic matriculat als estudis de Màster d’Enginyeria Informàtica de la UOC. He tornat a la Universitat gairebé trenta anys després de posar-hi el peu per primer cop!

Fa poc més d’un parell d’anys les circumstàncies professionals em van empènyer a canviar de feina. Una de les conseqüències d’aquell canvi va ser que em va semblar que era un bon moment per capitalitzar l’experiència professional acumulada després de… gairebé vint-i-cinc anys al sector TIC, dels quals més de setze específicament a la consultoria.

He de dir que em va molt bé, per ara, i que duri. També cal dir que m’ho estic prenent amb calma: només faig dues assignatures per semestre.

Tanmateix, la quantitat de temps que cal dedicar als treballs i pràctiques, només amb dues assignatures, és prou important. Tant que en tot un any no m’he vist amb cor de dedicar temps al bloc d’apunts de tecnologia.

idealment, a meva intenció és que bloc i estudis siguin activitats sinèrgiques. No és el cas d’avui, però alguns dels treball que ja he fet per a la UOC donen per a un post interessant, i potser els adaptaré i els acabaré publicant aquí. En aquest sentit, a diferència dels polítics del PP, el meu màster me l’estic currant…  i puc aportar proves.

Fetes aquestes explicacions, anem al gra. Avui presento un exercici senzill per reprendre el bloc : es tracta d’un decoder/encoder de codi Binari a codi Gray. El codi Gray és una d’aquelles coses que vaig aprendre a l’Enginyeria Tècnica de Telecos, quan estudiava electrònica digital allà pels finals dels vuitanta. Hi havia alguna cosa gairebé màgica en les simplificacions de circuits combinacionals fent servir mapes de Karnaugh. La màgia és que als mapes  de Karnaugh els minterms es distribueixen per la taula seguint el codi de gray, aleshores, elements adjacents difereixen només en un bit, i això vol dir que es pot simplificar la variable binària corresponent al bit que canvia en aquells termes adjacents.

Però té més usos. Notablement, es fa servir per minimitzar els errors en les modulacions digitals QAM i Gray Coded M-PSK.

De Viquipèdia (https://ca.wikipedia.org/wiki/Codi_Gray) : « El codi binari reflectit o codi Gray, nomenat així en honor de l’investigador Frank Gray, és un sistema de numeració binari en què dos valors successius difereixen només en un dels seus dígits.

El codi Gray va ser dissenyat originalment per prevenir senyals espuris dels switches electromecànics. Actualment és utilitzat per a facilitar la correcció d’errors en els sistemes de comunicacions, com ara alguns sistemes de televisió per cable i la televisió digital terrestre. »

Vet aquí un vídeo que explica perquè el codi gray és tan útil en els codificadors de posició rotatoris (elimina els valors espuris que podria introduir la codificació binària directa) :

De fet, la idea original d’aquest post era implementar un control rotatori basat en el codi Gray amb Arduino . Queda per a més endavant.

Anem per feina, el que vull fer és un codificador / decodificador de binari a gray, és dir, vull un mètode al que li passo un número enter de n bits, i vull que em torni un array amb els bits del codi gray corresponent al número rebut.

també vull el mètode que faci el camí de tornada, és dir, vull el mètode al que li passi un array de bits amb el codi gray d’un número de n bits, i vull que em torni un array amb els bits de la codificació binària directa d’aquell número.

L’algoritme per a calcular el codi gray / codi binari és, en realitat, molt senzill. Està ben explicat a la wiki. La gràcia de tot això és que abans de consultar-ho a la wiki he dedicat un temps a veure si me’n recordava com es feia aquesta codificació i descodificació. Després d’algunes proves (de les que en trobareu rastre al meu GitHub), finalment me n’he sortit. Vet aquí el resultat :

Vet aquí el codi Python

Coder

def calculate_gray(number, n):
    bits = []
    gray = []
    
    remainder = number
    for nn in range(0, n):
        [quotient, remainder] = divmod(remainder, 2 ** (n - 1 - nn))
        bits.append(quotient)
        if nn == 0:
            gray.append(bits[0])
        else:
            gray.append(bits[nn - 1] ^ bits[nn])      
            
            
    return [bits, gray]

i decoder

def calculate_binary(gray, n):  # gray is an array of bits
    bits = []

    for pos in range(0, n):
        if pos == 0:
            bits.append(gray[0])
        else:
            bits.append(bits[pos - 1] ^ gray[pos])      
            
    return bits

i una taula de prova

if __name__ == '__main__':
    print "print a Gray table for n bits"
    n = raw_input('Number of bits? ')
    n = int(n)
    print "number of bits : %d" % n   
    
    bits = []
    gray = []
    grayTable = []

    for i in range(0, 2 ** n):
        [bits, gray] = calculate_gray(i, n)
        grayTable.append(gray)
        strBits = ''.join(map(lambda (x) : chr(ord('0') + x), bits))
        strGray = ''.join(map(lambda (x) : chr(ord('0') + x), gray))
        
        print "%5d --> %s --> %s" % (i, strBits, strGray)
    
    print "-------------------------------------------"
    
    bits = []

    for i in range(0, 2 ** n):
        bits = calculate_binary(grayTable[i], n)
        strBits = ''.join(map(lambda (x) : chr(ord('0') + x), bits))
        strGray = ''.join(map(lambda (x) : chr(ord('0') + x), grayTable[i]))
        
        print "%5d --> %s --> %s" % (i, strGray, strBits)

    print "Done!"

Com a cosa molt bonica, indicaria l’us de lambdes i join per a passar l’array de bits a cadena de caràcters; i també la funció divmod, que utilitzada en cascada em permet obtenir els bits corresponents a l’enter que vull codificar.

El repositori Github és :

https://github.com/abaranguer/gray-code-py-version.git

Bé, tot plegat queda una mica curt, oi? el que he fet és escriure una segona versió del codi, però aquest cop amb llenguatge C. Així, de pas, l’he refrescat una mica.

Python és un llenguatge de molt alt nivell d’abstracció i m’ha permès implementar l’algoritme en un obrir i tancar d’ulls. La versió C, en canvi, ha estat més interessant. Vet aquí el codi :

#include 
#include 
#include 

#define BUF_SIZE 3

/* typedef */
typedef struct typeRetDivMod {
	int quotient;
	int remainder;
} RetDivMod;

typedef struct typeRetCalculateGray {
	int *bits;
	int *gray;
} RetCalculateGray;

/* prototypes */
int main(void);
RetCalculateGray calculate_gray(int number, int n);
int *calculate_binary(int *gray, int n);
RetDivMod divmod(int number, int divisor);
char *bitsToString(int *bits, int numBits);

/* functions */
int main() {
	char buf[BUF_SIZE];
	int n;
	int i;
	RetCalculateGray retValue;
	int *bitsFromGray;
	char *bits;
	char *bits2;
	char *gray;

	printf("Print a Gray table for n bits\n\n");
	printf("Number of bits? ");
	fgets(buf, BUF_SIZE, stdin);
	n = atoi(buf);
	printf("number of bits : %d\n", n);

	int range = pow(2, n);

	for (i = 0; i  %s --> %s --> %s\n", i, bits, gray, bits2);
		free(bits);
		free(gray);
		free(bits2);
		free(retValue.bits);
		free(retValue.gray);
		free(bitsFromGray);
	}

	printf("Done!\n");
	return 0;
}

RetCalculateGray calculate_gray(int number, int n) {
	int remainder;
	int nn;
	RetDivMod retDivMod;
	RetCalculateGray retValue;

	retValue.bits = (int *) malloc(n * sizeof(int));
	retValue.gray = (int *) malloc(n * sizeof(int));

	remainder = number;

	for (nn = 0; nn < n; nn++) {
		retDivMod = divmod(remainder, pow(2, n - 1 - nn));
		retValue.bits[nn] = retDivMod.quotient;
		remainder = retDivMod.remainder;
		if (nn == 0) {
			retValue.gray[nn] = retValue.bits[0];
		} else {
			retValue.gray[nn] = retValue.bits[nn - 1] ^ retValue.bits[nn];
		}
	}

	return (retValue);
}

int *calculate_binary(int *gray, int n) {
	int nn;
	int *bits;

	bits = (int *) malloc(n * sizeof(int));

	for (nn = 0; nn < n; nn++) {
		if (nn == 0) {
			bits[nn] = gray[0];
		} else {
			bits[nn] = bits[nn - 1] ^ gray[nn];
		}
	}

	return (bits);
}

RetDivMod divmod(int number, int divisor) {
	RetDivMod retDivMod;

	retDivMod.quotient = number / divisor;
	retDivMod.remainder = number % divisor;

	return retDivMod;
}

char *bitsToString(int *bits, int numBits) {
	int i;
	char *charBits;

	charBits = (char *) malloc(numBits * sizeof(char) + 1);
	for (i = 0; i < numBits; i++) {
		charBits[i] = '0' + bits[i];
	}
	charBits[numBits] = '\0';

	return charBits;
}

El repositori Github de la versió C :

https://github.com/abaranguer/gray-code-c-version.git

Per acabar, doncs, a la versió C hi han més coses a comentar. En destaco :

  • L’us de typedef i struct per a definir estructures que faig servir per moure arguments entre  main  i les funcions de coding i encoding.
  • Us de malloc i free per reservar / alliberar dinàmicament l’espai de memòria per als arrays dels bits.
  • La versió C de la funció divmod de Python.
  • L’ús de fgets per a prendre entrada per consola de forma segura aprofitant que fgets fa automàticament el control de desbordament del buffer d’entrada.

Un puzzle 3×3 (versió amb Python i versió amb C)

Després d’una llarga aturada, reprenc el blog amb un un puzzle fet amb python (i amb C)

Segur que recordeu els puzzles en que una imatge es trosseja en NxN – 1 quadrets dins un marc de NxN (és dir, amb un espai lliure). Aleshores, movent quadrets a l’espai que queda lliure es pot aconseguir reordenar la imatge.

En el post d’avui he implementat el cas d’un puzzle de 3×3, tot molt simplificat i en mode text. Com sempre, estic desenvolupant sobre una màquina Linux. En concret, sobre el meu vell i petit eCAFÉ EC-800-H20G/S, amb Lubuntu 10.10, una maquineta minúscula amb un SO antic que, per circumstàncies personals, m’ha fet molta companyia aquests darrer mes.

  puzzle resolt    puzzle desordenat
  +---+            +---+
  |123|            | 38|
  |456|            |245|
  |78 |            |761|
  +---+            +---+

El programa ha de
1. generar un puzzle desordenat
2. mostrar el puzzle
3. demanar quin número es vol moure
4. veure si es pot moure
5.  si no es pot moure, dir-ho i tornar al punt 2
6.  si es pot moure, fer el moviment.
7.    comprovar si ha guanyat
8.      si ha guanyat acabar eljoc
9.      si no ha guanyat, tornar al punt 2.

Amb Python, l’algorisme anterior és d’implementació gairebé directa, fixeu-vos en el bloc main.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import random
import sys

def genera_puzzle(puzzle):
  for i in range(0,9):
    repeat = True
    while repeat:
      j = random.randint(0,8)
      if puzzle[j] == 10:
        puzzle[j] = i
        repeat = False
  
  return puzzle

def mostra_puzzle(puzzle):
  print "+---+"
  print "|%d%d%d|" % (puzzle[0], puzzle[1], puzzle[2])
  print "|%d%d%d|" % (puzzle[3], puzzle[4], puzzle[5])
  print "|%d%d%d|" % (puzzle[6], puzzle[7], puzzle[8])  
  print "+---+"
  print "\n"

def demanar_numero():
  valid = False
  while not valid:
    in_value = raw_input("(1-9) or q? ")
    if len(in_value) == 1:
      if in_value in "12345678qQ":
        valid = True

  if in_value == 'q' or in_value == 'Q':
    print "\nGame interrupted\n"
    sys.exit(0)

  return int(in_value)


def obtenir_posicio(num, puzzle):
  pos = []

  for row in range(0, 3):
    for column in range(0, 3):
      if num == puzzle[row * 3 + column]:
        pos = [row, column]
        break

  return pos     


def verifica_numero(num, puzzle):
  valid = False
  pos = []
  
  pos = obtenir_posicio(num, puzzle)
  valid = verifica_posicio(pos, puzzle)

  return valid

def verifica_posicio(pos, puzzle):
  valid = False
  row = pos[0]
  column = pos[1]
  
  if (row - 1) >= 0:
    if puzzle[(row - 1) * 3 + column] == 0:
      valid = True

  if (row + 1) = 0:
    if puzzle[(row * 3) + (column - 1)] == 0:
      valid = True

  if (column + 1) = 0:
    if puzzle[(row - 1) * 3 + column] == 0:
      puzzle[(row - 1) * 3 + column] = num
      puzzle[(row * 3) + column] = 0

  if (row + 1) = 0:
    if puzzle[(row * 3) + (column - 1)] == 0:
      puzzle[(row * 3) + (column - 1)] = num
      puzzle[(row * 3) + column] = 0

  if (column + 1) <= 2:
    if puzzle[(row * 3) + (column + 1)] == 0:
      puzzle[(row * 3) + (column + 1)] = num
      puzzle[(row * 3) + column] = 0    
  
  return puzzle

def es_puzzle_completat(puzzle):
  s = ''.join(map(lambda (x): str(x), puzzle))
  if s == '123456780':
    return True
  else:
    return False


if __name__ == "__main__":
  puzzle = [10 for x in range(0, 9)]
  final = False
  puzzle = genera_puzzle(puzzle)
  while not final:
    final = False
    mostra_puzzle(puzzle)
    num = demanar_numero()
    if verifica_numero(num, puzzle):
      puzzle = moviment(num, puzzle)
      if es_puzzle_completat(puzzle):
        print "\nWell done!!\n"
        mostra_puzzle(puzzle)
        final = True

Topt i ser un programa molt curt, té algun detall bonic: per exemple, l’us d’una funció lambda a la verificació de puzzle completat. Amb una sola línia es transforma es transforma un array numèric en una cadena de text. En total, 155 línies de codi.

I això mateix, com queda amb C?Amb C són 230 línies

#ifndef PUZZLE
#define PUZZLE
#include 
#include 
#include 
#include 
#include 

#define TRUE 1
#define FALSE 0

int puzzle[] = {10,10,10,10,10,10,10,10,10};

typedef struct POS {
  int row;
  int column;
} T_POS;

int randint(int range) {
  return random() % (range + 1);  
}

void genera_puzzle(int puzzle[]) {
  int i;
  int j;
  int repeat;
  for(i = 0; i < 9; i++) {
    repeat = 1;
    while (repeat) {
      j = randint(8);
      if (puzzle[j] == 10) {
        puzzle[j] = i;
        repeat = 0;
      }
    }
  }
}

void mostra_puzzle(int puzzle[]) {
  printf("+---+\n");
  printf("|%d%d%d|\n", puzzle[0], puzzle[1], puzzle[2]);
  printf("|%d%d%d|\n", puzzle[3], puzzle[4], puzzle[5]);
  printf("|%d%d%d|\n", puzzle[6], puzzle[7], puzzle[8]);
  printf("+---+\n");
  printf("\n");
}

int demanar_numero() {
  int valid = FALSE;
  char *line;
  char c;

  while(!valid) {
    line = readline("(1-9) or q (quit)? ");
    if (strlen(line) == 1) {
      if ((strcmp(line, "q") == 0) || (strcmp(line, "Q") == 0)) {
	  valid = TRUE;
          free(line);
          printf("\nGame interrupted\n");
          exit(0);
      }
  	
      c = line[0];
      free(line);

      if ((c == '0') || (c == '1') || (c == '2') ||
          (c == '3') || (c == '4') || (c == '5') ||
          (c == '6') || (c == '7') || (c == '8')) {
        valid = TRUE;      
      }       
    }
  }
  
  return (c - '0');
}


T_POS obtenir_posicio(int num, int puzzle[]) { 
  T_POS pos;
  int row;
  int column;

  for(row = 0; row < 3; row++) {
    for(column = 0; column = 0) {
    if (puzzle[(row - 1) * 3 + column] == 0) {
      valid = TRUE;  
    }
  }

  if ((row + 1) = 0) {
    if (puzzle[(row * 3) + (column - 1)] == 0) {
      valid = TRUE;
    }
  }

  if ((column + 1) = 0) {
    if (puzzle[(row - 1) * 3 + column] == 0) {
      puzzle[(row - 1) * 3 + column] = num;
      puzzle[(row * 3) + column] = 0;
    }
  }

  if ((row + 1) = 0) {
    if (puzzle[(row * 3) + (column - 1)] == 0) {
      puzzle[(row * 3) + (column - 1)] = num;
      puzzle[(row * 3) + column] = 0;
    }
  }

  if ((column + 1) <= 2) {
    if (puzzle[(row * 3) + (column + 1)] == 0) {
      puzzle[(row * 3) + (column + 1)] = num;
      puzzle[(row * 3) + column] = 0;
    }
  }
}

int es_puzzle_completat(int puzzle[]) {
  char solved[]="123456780";
  char actual[10];
  int i;
 
  for(i = 0; i < 9; i++) {
    actual[i] = puzzle[i] + '0';
  }
  actual[9] = 0;

  if (strcmp(solved, actual) == 0) {
    return TRUE;
  } else {
    return FALSE;
  }
}

int main(int argc, char *argv[]) {
  int final = 0;
  int num;
  genera_puzzle(puzzle);
  while(!final) {
    final = 0;
    mostra_puzzle(puzzle);
    num = demanar_numero();
    if (verifica_numero(num, puzzle)) {
      moviment(num, puzzle);
      if (es_puzzle_completat(puzzle)) {
        printf("\nWell done!!\n");
        mostra_puzzle(puzzle);
        final = 1;
      }
    }
  }
}

#endif

I el makefile corresponent

all: puzzle clean

puzzle: puzzle.o
	gcc -g -lreadline -o puzzle puzzle.o

puzzle.o: puzzle.c
	gcc -g -c puzzle.c

clean:
	-rm *.o *~

Amb C també és força directe, però cal fer la gestió d’espai de memòria que amb Python ve donada.

Remarcar l’us de la llibreria readline per a fer l’entrada per teclat. Aquesta és la forma més recomanable de fer entrades per teclat a una aplicació de consola a Linux. Les aplicacions curses tenen també un sistema propi i segur per fer entrades per teclat que eviten el perill de desbordament del buffer d’entrada si es fa servir gets(), o scanf(), o qualsevol sistema que demani un buffer d’entrada de mida especificada.

Finalment, aquest puzzle bàsic es pot ampliar senzillament amb:
– incrementant a dimensions NxN amb N > 3
– afegint interfície gràfica: amb Tkinter, o Pygame, a la versió amb Python; Fent servir Glade a la versió C.

Finalment, el codi de les dues versions (C i Python) es pot descarregar del meu GitHub

CURL. La navalla suïssa per a les transferències a Internet

En un post anterior vam presentar el ”truc” de com descarregar un lloc web fent servir el programa wget.

Doncs bé, curl (http://curl.haxx.se/) és un programa escrit per Daniel Stenberg similar a wget, i és del que parlarem en aquest post. Hi han algunes diferències entre curl i wget. Un bon resum el tenim en aquesta taula comparativa entre curl, wget i altres programes similars.

Aquest pdf és una presentació que ens pot fer donar una idea ràpida de curl.

Potser el més destacable és que curl es pot trobar també com a llibreria (libcurl), de forma que es pot enllaçar amb els nostres programes.

I no només C. curl es troba integrat a PHP. També hi ha un port per a Python. De fet, hi han ports per a un munt d’altres llenguatges.

Pel que fa estrictament al programa, curl pot tractar amb més protocols que wget , és capaç de fer uploads, autenticar-se i treballar amb cookies. Curl es pot integrar a scripts de shell de forma més “natural” que wget al ser una aplicació que funciona a l’estil Unix. Per exemple, fent ús de pipes que wget no admet.

Per contra, el súper-poder de wget és que és capaç de descarregar webs recursívament i aquest és, en veritat us dic, un gran súper-poder.

En general es considera que wget és una eina més útil per a descarregar llocs web, mentre que curl permet tractar amb llocs web a més baix nivell, que hagin de tenir en compte cookies, autenticació i enviaments de client a servidor.

La pàgina del manual de Curl és la referència imprescindible de l’aplicació.

Com ens baixaríem el manual amb curl? Fent:

curl -o manpage.html http://curl.haxx.se/docs/manpage.html 
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current 
                                 Dload  Upload   Total   Spent    Left  Speed 
100  121k  100  121k    0     0   255k      0 --:--:-- --:--:-- --:--:--  308k

(amb l’opció -o per output, indica el fitxer de sortida)

Hi han més de 100 opcions diferents per a curl. Vet aquí les opcions d’emergència de Curl:

curl http://curl.haxx.se/ -o guarda-ho_aquí
curl ftp://curl.haxx.se/ -o guarda-ho_aquí
curl https://curl.haxx.se/ -o guarda-ho_aquí
curl sftp://curl.haxx.se/ -u elmeulogin:elmeupassword -o guarda-ho_aquí
curl scp://curl.haxx.se/ -o guarda-ho_aquí
curl tftp://curl.haxx.se/ -o guarda-ho_aquí
curl -T fitxer_upload ftp://curl.haxx.se/guarda-ho_aquí/
curl -T fitxer_upload https://curl.haxx.se/guarda-ho_aquí/

Aquesta altre pàgina http://www.cs.sunysb.edu/documentation/curl/ ens dona una excel·lent referència de la majoria de les opcions de curl, i amb exemples.

Com que la millor forma d’aprendre és practicant, ara faré alguns experiments en localhost. Primer de tot engego la meva versió local del XAMPP, de forma que tinc un servidor web amb PHP i un servidor d’FTP per a fer les proves.

Creo una carpeta curl dins htdocs i allà hi posaré els diferents html i php que aniré fent.

Emplenar un formulari web tipus POST amb CURL

Vet aquí el formulari

<html>
<head>
<title>Formulari de prova</title>
</head>
<body>
<form action="action.php" method="post">
Nom: <input type="text" name="sNom" /><br />
Valor: <input type="text" name="sValor" /><br />
Codi: <input type="text" name="sCodi" /><br />
<input type="submit" value="envia" /> <input type="reset" value="cancel·la" />
</form>
</body>
</html>

i la seva pàgina de resposta (action.php)

<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1" />
<title>Pàgina de resposta del formulari</title>
</head>
<body>
Valors enviats al formulari<br /><br />
sNom: <?php echo($_POST['sNom']) ?> <br />
sValor: <?php echo($_POST['sValor']) ?> <br />
sCodi: <?php echo($_POST['sCodi']) ?> <br />
</body>
</html>

Doncs bé, podem invocar per línia de comandes fent

albert@athena:~/lampp/htdocs/curl$ curl http://localhost/curl/action.php -FsNom=ALBERT -FsValor=1234546 -FsCodi=id > file.txt 

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current 
                                 Dload  Upload   Total   Spent    Left  Speed 
100   705  100   362  100   343  13730  13009 --:--:-- --:--:-- --:--:-- 20111

i el resultat és el fitxer de text file.txt que conté

<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1" />
<title>Pàgina de resposta del formulari</title>
</head>
<body>
Valors enviats al formulari<br /><br />
sNom: ALBERT <br />
sValor: 1234546 <br />
sCodi: id <br />
</body>
</html>

És dir, estic invocant el formulari des de línia de comandes, en un script, i el resultat és un fitxer de text que puc analitzar en el mateix script.

Pujar un fitxer a un servidor web (multipart)

El formulari de prova podria ser aquest. Comentar l’enctype al form i l’us del input type file

<html>
<head>
<title>Formulari de prova</title>
</head>
<body>
<form action="action-upload.php" method="post" enctype="multipart/form-data">
Nom: <input type="text" name="sNom" /><br />
Valor: <input type="text" name="sValor" /><br />
Codi: <input type="text" name="sCodi" /><br />
Fitxer: <input type="file" value="Upload" name="sUpload" /><br />
<input type="submit" value="envia" /> <input type="reset" value="cancel·la" />
</form>
</body>
</html>

La pàgina de resposta (action-upload.php), aquesta. Comentar aquí com es fa servir l’objecte $_FILES per a obtenir el fitxer pujat,, informació com mida i tipus; i l’us de la funció move_uploaded_file per moure el fitxer del directori temporal al directori de destinació que, en aquest cas, és una carpeta upload dins de la carpeta curl. Cal recordar de donar permís d’escriptura a l’usuari amb que s’executa el php al servidor web sobre el directori de destinació.

<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1" />
<title>Pàgina de resposta del formulari</title>
</head>
<body>
Valors enviats al formulari<br /><br />
sNom: <?php echo($_POST['sNom']) ?> <br />
sValor: <?php echo($_POST['sValor']) ?> <br />
sCodi: <?php echo($_POST['sCodi']) ?> <br />

<?php if ($_FILES["sUpload"]["error"] > 0) { ?>
S'ha produit un error: <?php echo( $_FILES["sUpload"]["error"]) ?> <br />
<?php } else { ?>
Fitxer d'upload: <?php echo ($_FILES["sUpload"]["name"]) ?><br />
Tipus de fitxer: <?php echo ($_FILES["sUpload"]["type"]) ?><br />
Mida del fitxer: <?php echo (($_FILES["sUpload"]["size"] / 1024)) ?> Kb<br />
<?php move_uploaded_file($_FILES["sUpload"]["tmp_name"], "upload/" . $_FILES["sUpload"]["name"]); ?>
Desat a: <?php echo("upload/" . $_FILES["sUpload"]["name"]) ?>
<?php } ?>
</body>
</html>

L’anterior pàgina de resposta es pot invocar des de línia de comandes amb una instrucció com la següent. La clau és prefixar el fitxer a pujar amb ‘@’

albert@athena:~/Baixades/bin$ curl http://localhost/curl/action-upload.php -FsUpload=@sl4a_r5.apk -FsNom=ALBERT -FsValor=1234546 -FsCodi=id > file.txt 
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current 
                                 Dload  Upload   Total   Spent    Left  Speed 
100  858k  100   515  100  857k   9208  14.9M --:--:-- --:--:-- --:--:-- 15.5M

Vet aquí el fitxer descarregat:

albert@athena:~/Baixades/bin$ cat file.txt
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1" />
<title>Pàgina de resposta del formulari</title>
</head>
<body>
Valors enviats al formulari<br /><br />
sNom: ALBERT <br />
sValor: 1234546 <br />
sCodi: id <br />

Fitxer d'upload: sl4a_r5.apk<br />
Tipus de fitxer: application/octet-stream<br />
Mida del fitxer: 857.083984375 Kb<br />
Desat a: upload/sl4a_r5.apk</body>
</html>
albert@athena:~/Baixades/bin$

Pujar i obtenir fitxers de servidor FTP

Aprofito que XAMPP m’ofereix un servidor d’FTP local per provar curl amb aquest protocol

hi faig un cop d’ull amb el client d’ftp:

albert@athena:~/Baixades/bin$ ftp localhost 
Connected to localhost. 
220 ProFTPD 1.3.3e Server (ProFTPD) [::ffff:127.0.0.1] 
Name (localhost:albert): albert 
331 Password required for albert 
Password: 
230 User albert logged in 
Remote system type is UNIX. 
Using binary mode to transfer files. 
ftp> ls 
200 PORT command successful 
150 Opening ASCII mode data connection for file list 
drwxr-xr-x   7 albert   albert       4096 Mar  7 22:33 Symfony 
drwxrwxr-x   4 albert   albert       4096 Apr  8 22:32 captcha 
drwxrwxr-x   3 albert   albert       4096 Apr 19 21:14 curl 
-rwxrwxrwx   1 root     root        30894 May 11  2007 favicon.ico 
-rwxrwxrwx   1 root     root          256 Feb  5  2009 index.php 
drwxrwxrwx   2 nobody   root         4096 Jul 15  2009 webalizer 
drwxrwxrwx   7 root     root         4096 Mar  1  2011 xampp 
226 Transfer complete 
ftp> ^C 
ftp> quit 
221 Goodbye.

Amb curl faig un put del fitxer de prova a la carpeta curl/upload. La clau d’aquesta instrucció és la barra del final a ftp://albert:xxxxxx@localhost/curl/upload/ . D’aquesta manera, curl entén que li estem indicant una carpeta de destinació. Sense la barra es creu que és un directori i obtenim un error.

albert@athena:~/Baixades/bin$ curl -TPythonForAndroid_r5.apk ftp://albert:xxxxxx@localhost/curl/upload/ 
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current 
                                 Dload  Upload   Total   Spent    Left  Speed 
100  124k    0     0  100  124k      0  1353k --:--:-- --:--:-- --:--:-- 1553k

Una sintaxi alternativa és amb el paràmetre -u, pe a indicar usuari i password
curl -TPythonForAndroid_r5.apk -u albert:xxxxxx ftp://@localhost/curl/upload/

Obtinc el llistat de fitxers pujats (de nou, cal indicar la barra del final)

albert@athena:~/Baixades/bin$ curl -ualbert:xxxxxx ftp://localhost/curl/upload/ 
-rw-r--r--   1 albert   albert     127271 Apr 19 22:01 PythonForAndroid_r5.apk 
-rw-r--r--   1 nobody   nogroup    877654 Apr 19 21:32 sl4a_r5.apk

Per cert, noteu com en l’anterior llistat el fitxer sl4a_r5.apk que ha estat pujat via http, el propietari i el grup són nobody i nogroup, mentre que el que ha pujat per ftp l’usuari i el grup són els utilitzats en l’ftp.

També, puc obtenir un fitxer del servidor. Em descarregaré la font AHGBold.ttf que tinc a la carpeta de securimage.

Primer de tot, examino la carpeta

albert@athena:~/Baixades/bin$ curl -ualbert:xxxxxx ftp://localhost/captcha/securimage/ 
-rw-rw-r--   1 albert   albert     144556 Jan 16 20:16 AHGBold.ttf 
-rw-rw-r--   1 albert   albert       1386 Jan 16 20:16 LICENSE.txt 
-rw-rw-r--   1 albert   albert        398 Jan 16 20:16 README.FONT.txt 
-rw-rw-r--   1 albert   albert       3981 Jan 16 20:16 README.txt 
drwxrwxr-x   2 albert   albert       4096 Jan 16 20:16 audio 
drwxrwxr-x   2 albert   albert       4096 Jan 16 20:16 backgrounds 
-rw-rw-r--   1 albert   albert       1113 Jan 16 20:16 captcha.html 
drwxrwxr-x   2 albert   albert       4096 Jan 16 20:16 database 
-rw-rw-r--   1 albert   albert       8330 Jan 16 20:16 example_form.ajax.php 
-rw-rw-r--   1 albert   albert       7994 Jan 16 20:16 example_form.php 
drwxrwxr-x   2 albert   albert       4096 Jan 16 20:16 images 
-rw-rw-r--   1 albert   albert        491 Apr  8 18:29 prova-form.php~ 
-rw-rw-r--   1 albert   albert      49526 Jan 16 20:18 securimage.php 
-rw-rw-r--   1 albert   albert       1941 Jan 16 20:16 securimage_play.php 
-rw-rw-r--   1 albert   albert       7686 Jan 16 20:16 securimage_play.swf 
-rw-rw-r--   1 albert   albert       3929 Jan 16 20:16 securimage_show.php 
-rw-rw-r--   1 albert   albert       2929 Jan 16 20:16 securimage_show_example.php 
-rw-rw-r--   1 albert   albert       2813 Jan 16 20:16 securimage_show_example2.php 
drwxrwxr-x   2 albert   albert       4096 Jan 16 20:16 words

I ara n’obtinc el fitxer:

albert@athena:~/Baixades/bin$ curl -ualbert:xxxxxx ftp://localhost/captcha/securimage/AHGBold.ttf > AHGBold.ttf 
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current 
                                 Dload  Upload   Total   Spent    Left  Speed 
100  141k  100  141k    0     0  1219k      0 --:--:-- --:--:-- --:--:-- 1470k

Notem com l’ha descarregat (Average Dload de 1219k) .

libcurl

Però, el més interessant és que curl es pot enllaçar (libcurl) als nostres programes fets amb C, o Python, o amb algun dels 40 ports a diferents llenguatges que se n’han fet. Fer servir curl per a resoldre les transferències per HTTP, FTP o d’altres protocols no és esnobisme.

Entre la llista de les aplicacions que el fan servir n’hi han de tant populars com OpenOffice.org, Transmission, Xine o GnuPG. En aquest enllàç trobarem una llista d’aplicacions que fan servir Curl.

A la web de Curl podrem trobar exemples d’us de la llibreria.

Exemples amb C
http://curl.haxx.se/libcurl/c/allexamples.zip

Exemples de pycurl (curl amb python) (arxiu .tar)
http://pycurl.cvs.sourceforge.net/viewvc/pycurl/pycurl/examples/?view=tar

Test de Pycurl (arxiu .tar)
http://pycurl.cvs.sourceforge.net/viewvc/pycurl/pycurl/tests/?view=tar

C
Com de fàcil és fer servir curl amb C? La resposta és “molt”. Vet aquí aquest exemple extret de la web de curl. Per a compilar-lo ens caldrà que estigui instal·lat el paquet libcurl4-dev (he instal·lat el libcurl4-openssl-dev. Els protocols segurs poden ser proporcionats per altres llibreries de base, a més d’OpenSSL, com GnuTLS o NSS.

#include 
#include <curl/curl.h>

int main(void)
{
  CURL *curl;
  CURLcode res;

  curl = curl_easy_init();
  if(curl) {
    curl_easy_setopt(curl, 
		     CURLOPT_URL,     
		     "http://www.cs.sunysb.edu/documentation/curl/");

    res = curl_easy_perform(curl);

    /* always cleanup */ 
    curl_easy_cleanup(curl);
  }
  return 0;
}

L’exemple il·lustra com treballar amb libcurl: iniciar la llibreria (curl_easy_init), establir opcions, en aquest cas per a obtenir el contingut de la URL i abocar-lo a la sortida estàndar. Executar l’acció. (curl_easy_setopt estableix les opcions i curl_easy_perform, les executa). Finalment, tancar (curl_easy_cleanup).

Les crides del tipus culr_easy_* permeten accedir a les possibilitats de curl de forma “easy”, fàcil, com el mateix nom indica. Es tracta de la versió síncrona, i ràpida d’usar per a fer transferències. Existeix la versió “multi”, que permet transferències múltiples i asíncrones.

Compilar el programa anterior, executar-lo i examinar-ne el resultat:

albert@athena:~/workspace/wk-c/prova-libcurl$ gcc simple.c -lcurl -o simple-curl
albert@athena:~/workspace/wk-c/prova-libcurl$ simple-curl > pagina.html
albert@athena:~/workspace/wk-c/prova-libcurl$ ls -al
total 64
drwxrwxr-x 2 albert albert 4096 2012-04-22 16:38 .
drwxrwxr-x 12 albert albert 4096 2012-04-22 16:22 ..
-rw-rw-r-- 1 albert albert 38126 2012-04-22 16:38 pagina.html
-rw-rw-r-- 1 albert albert 1353 2012-04-22 16:23 simple.c
-rwxrwxr-x 1 albert albert 8478 2012-04-22 16:38 simple-curl
albert@athena:~/workspace/wk-c/prova-libcurl$ head pagina.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head> <title>cURL - Manual</title>
<link rel="STYLESHEET" type="text/css" href="curl.css">
</head>
<body bgcolor="#ffffff" text="#000000" LINK="#0000ff" VLINK="#808080" ALINK="red">
<table width="100%"><tr>
<td>
<a href="http://curl.haxx.se/"><img border="0" src="ds-curlicon.png" width="99" height="37" alt="cURL"></a>
</td>
albert@athena:~/workspace/wk-c/prova-libcurl$

Python

I amb PyCurl?

L’exemple anterior amb Python, també extret dels exemples:

http://pycurl.cvs.sourceforge.net/viewvc/pycurl/pycurl/examples/basicfirst.py?view=markup

#! /usr/bin/env python
# -*- coding: iso-8859-1 -*-
# vi:ts=4:et
# $Id: basicfirst.py,v 1.5 2005/02/11 11:09:11 mfx Exp $

import sys
import pycurl

class Test:
    def __init__(self):
        self.contents = ''

    def body_callback(self, buf):
        self.contents = self.contents + buf

print >>sys.stderr, 'Testing', pycurl.version

t = Test()
c = pycurl.Curl()
c.setopt(c.URL, 'http://curl.haxx.se/dev/')
c.setopt(c.WRITEFUNCTION, t.body_callback)
c.perform()
c.close()

print t.contents

Copio aquest codi a simple.py, li en dono persmisos d’execució i provo.

albert@athena:~/workspace/wk-python/prova-curl$ simple.py > pagina.html
Testing libcurl/7.21.6 GnuTLS/2.10.5 zlib/1.2.3.4 libidn/1.22 librtmp/2.3
albert@athena:~/workspace/wk-python/prova-curl$ head pagina.html 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<HTML>
<HEAD> <TITLE>cURL - Development</TITLE>
<meta content="text/html; charset=ISO-8859-1" http-equiv="Content-Type">
<link rel="STYLESHEET" type="text/css" href="/curl.css">
<link rel="shortcut icon" href="http://curl.haxx.se/favicon.ico">
</HEAD>
<BODY bgcolor="#ffffff" text="#000000" LINK="#0000ff" VLINK="#808080" ALINK="red">
<!-- first-line-in-body -->
<table width="100%"><tr valign="top"><td> <a href="http://curl.haxx.se/"><img border="0" src="http://curl.haxx.se/ds-curlicon.png"

En resum, una eina per a transferir informació “cap a” i “des de” servidors http i ftp que es pot encastar als nostres scripts d’administració de sistemes o les aplicacions que desenvolupem.

Taules hash amb C a Linux

Les taules hash , hash maps són una estructura de dades que, mitjançant una funció hash (o funció resum) identifica uns valors, anomenats claus, amb uns valors associats.

L’avantatge de les taules hash sobre altres estructures de dades és la gran rapidesa d’accés a la informació i el cost constant d’aquest accés. Per això,  les taules hash tenen un munt d’aplicacions.

En tot cas, les taules hash són una solució molt senzilla i eficient per a l’emmagatzematge i l’accés a la informació que es pugui modelar directament amb taules de  parelles clau valor.

Amb C sobre Linux (en el meu cas Ubuntu 11.04) apareixen almenys tres opcions ràpides per a implementar taules hash:
Amb funcions de la llibreria estàndar GlibC
Amb funcions de la llibreria GTK, Glib

Amb “bases de dades” del tipus DBM, a Linux Gdbm; o amb l’evolució amb capacitat concurrent de Gdbm desenvolupada per a SAMBA: TDB

Anem a repassar les opcions:

Amb Glibc (La llibreria estàndard de C)

Un exemple de taules hash amb les funcions de la llibreria estàndard Glibc (hcreate, hsearch i hdestroy)

/* aquest exemple està adaptat de la documentació del man */
#include <stdio.h>
#include <search.h>
#include <string.h>

struct info {        /* aquesta estructura és la dels valors emmagatzemats a la taula */
    int age, room;  
};

#define NUM_EMPL    5000    /* # número d’elements. */

int main(void)
{
    char string_space[NUM_EMPL*20];       /* espai d’emmagatzematge de la taula de valors de les claus. */
    struct info info_space[NUM_EMPL];     /* espai per emmagatzemar la informació de claus. */
    char *str_ptr = string_space;         /* següent posició a l’espai de valors. */
    struct info *info_ptr = info_space;   /* següent posició a l’espai de claus. */
    ENTRY item;
    ENTRY *found_item;                    /* nom a buscar. */
    char name_to_find[30];
    int i = 0;

    /* Create table; no error checking is performed. */
    printf(“Crea la taula\n”);
    (void) hcreate(NUM_EMPL);

    printf(“Introdueix clau age room a la taula. Per acabar d’entrar valors prem CTRL-D\n”);
    while (scanf(“%s %d %d”, str_ptr, &info_ptr->age,
           &info_ptr->room) != EOF && i++ < NUM_EMPL) {


        /* Put information in structure, and structure in item. */
        printf(“Afegeix %s %d %d a la taula\n”, str_ptr, info_ptr->age, info_ptr->room);
        item.key = str_ptr;
        item.data = info_ptr;
        str_ptr += strlen(str_ptr) + 1;
        info_ptr++;


        /* Put item into table. */
        printf(“Afegeix – hsearch(item, ENTER)\n”);
        (void) hsearch(item, ENTER);
    }



    /* Access table. */
    printf(“Busca valors a la taula. Per acabar CTRL-C\n”);
    item.key = name_to_find;
    while (scanf(“%s”, item.key) != EOF) {
        printf(“Buscant %s amb hsearch(item, FIND)\n”, item.key);
        if ((found_item = hsearch(item, FIND)) != NULL) {
           
            /* If item is in the table. */
            (void)printf(“trobat %s, age = %d, room = %d\n”,
                found_item->key,
                ((struct info *)found_item->data)->age,
                ((struct info *)found_item->data)->room);
        } else
            (void)printf(“no trobat %s\n”, name_to_find);
    }
    return 0;
}

Una excució podria ser, per exemple:


albert@atenea:~/wk-c/prova-hash-glibc$ ./prova_hash_glibc
Crea la taula
Introdueix clau age room a la taula. Per acabar d’entrar valors prem CTRL-D
clau1 1 2
Afegeix clau1 1 2 a la taula
Afegeix – hsearch(item, ENTER)
clau2 3 4
Afegeix clau2 3 4 a la taula
Afegeix – hsearch(item, ENTER)
clau3 5 6
Afegeix clau3 5 6 a la taula
Afegeix – hsearch(item, ENTER)
Busca valors a la taula. Per acabar CTRL-C
prova
Buscant prova amb hsearch(item, FIND)
no trobat prova
clau1
Buscant clau1 amb hsearch(item, FIND)
trobat clau1, age = 1, room = 2
clau3
Buscant clau3 amb hsearch(item, FIND)
trobat clau3, age = 5, room = 6
^C
albert@atenea:~/wk-c/prova-hash-glibc$

Amb GLib (la llibreria de GTK)

La referència per a taules hash amb Glib es troba a http://developer.gnome.org/glib/2.28/glib-Hash-Tables.html

En resum:

Per a crear una taula hash de Glib (una GHashTable), fem servir: g_hash_table_new().

Per afegir una clau i el seu valor associat a la GHashTable, fem servir: g_hash_table_insert().

Per a cercar el valor associat a una clar fem servir  g_hash_table_lookup()  i g_hash_table_lookup_extended().

Per a eliminar una aprella clau-valor, g_hash_table_remove().

Per a  invocar una funció per a cada parella clau-valor,  g_hash_table_foreach().

Per a esborrar del tot una GHashTable,  g_hash_table_destroy().

Vet aquí l’exemple amb GLibC implementat amb les funcions de GLib. L’exemple ha estat desenvolupat fent servir Anjuta. He partit d’un projecte genèric mínim amb C. M’ha calgut afegir la llibreria glib2.0-0 i l’include a glib.h al fitxer Makefile.am (automake)

## Process this file with automake to produce Makefile.in

## Created by Anjuta

AM_CPPFLAGS = \
    -DPACKAGE_DATA_DIR=\””$(datadir)”\”

AM_CFLAGS =\
     -Wall\
     -g\
     -I/usr/include/glib-2.0\
     -I/usr/lib/glib-2.0/include
     
bin_PROGRAMS = hashtables

hashtables_SOURCES = \
    main.c

hashtables_LDFLAGS = -lglib-2.0

hashtables_LDADD =

El codi és el següent. Pràcticament només he tingut que canviar les funcions:



#include <stdio.h>
#include <string.h>
#include <glib.h>


struct value {        /* aquesta estructura és la dels valors emmagatzemats a la taula */
    gint age, room;  /* gint és el tipus glib que encapsula a int*/ 
};

#define NUM_KEYS    5000    /* número d’elements. */
#define KEY_SIZE    20      /* tamanys de la clau*/

int main(void)
{
    GHashTable *hashTable;

    gchar keys[NUM_KEYS * KEY_SIZE];    /* espai d’emmagatzematge de la taula de valors de les claus. */
    struct value values[NUM_KEYS];      /* espai per emmagatzemar la informació de claus. */
    gchar *keyPtr = keys;               /* següent posició a l’espai de valors. */
    struct value *valuePtr = values;    /* següent posició a l’espai de claus. */
    gchar keyToFind[KEY_SIZE];          /* clau a buscar, màxim de KEY_SIZE caràcters */
    int i=0;                   

    /* Crea taula hash.
      g_str_hash és una funció hash que es proporciona amb Glib
      per a claus del tipus (gchar *)
      g_str_equal és una funció que es proporciona amb Glib per verificar
      l’igualtat de dues claus del tipus (gchar *)
    */
    printf(“Crea la taula\n”);
    hashTable = g_hash_table_new(g_str_hash, g_str_equal);

    printf(“Introdueix clau age room a la taula. Per acabar d’entrar valors prem CTRL-D\n”);
    while (scanf(“%s %d %d”,
                 keyPtr,
                 &valuePtr->age,
                 &valuePtr->room) != EOF && i++ < NUM_KEYS) {
                 
        /* traça */
        printf(“Afegeix %s %d %d a la taula\n”, keyPtr, valuePtr->age, valuePtr->room);

        /* els posa a la taula */
        printf(“Afegeix – g_hash_table_insert\n”);
        /* afegeix clau-valor a taula hash.
          g_hash_table_insert rep tres paràmetres
          punter a la taula hash
          punter a la clau
          punter al valor
        */        
        g_hash_table_insert(hashTable, keyPtr, valuePtr);

        /* prepara els punters per a nova clau valor*/
        keyPtr += strlen(keyPtr) + 1;
        valuePtr++;
    }


    /* Ara busca per la taula. */
    printf(“Busca valors a la taula. Per acabar CTRL-C\n”);
    keyPtr = keyToFind;
    while (scanf(“%s”, keyPtr) != EOF) {
        printf(“Buscant %s amb g_hash_table_lookup\n”, keyPtr);
        if ((valuePtr = g_hash_table_lookup(hashTable, keyPtr)) != NULL) {
           
            /* si ha trobat diferent de nul, el mostra. */
            /* nota: això pot ser un problema, perquè no es pot distingir
               el “no trobat” del “trobat el valor nul”.
               En aquests casos, cal fer servir g_hash_table_lookup_extended
            */
            printf(“found %s, age = %d, room = %d\n”,
                keyPtr,
                valuePtr->age,
                valuePtr->room);
        } else
            printf(“not found %s\n”, keyPtr);
    }
    return 0;
}

L’execució del programa anterior segueix les mateixes indicacions que al primer exemple:

albert@atenea:~/wk-c/prova-hash-glib$  hashtables
Crea la taula
Introdueix clau age room a la taula. Per acabar d’entrar valors prem CTRL-D
albert 42 192
Afegeix albert 42 192 a la taula
Afegeix – g_hash_table_insert
montse 40 201
Afegeix montse 40 201 a la taula
Afegeix – g_hash_table_insert

^D
Busca valors a la taula. Per acabar CTRL-C
kok
Buscant kok amb g_hash_table_lookup
not found kok
albert
Buscant albert amb g_hash_table_lookup
found albert, age = 42, room = 192
montse
Buscant montse amb g_hash_table_lookup
found montse, age = 40, room = 201
^C

GDBM i TDB

En el desenvolupament de SAMBA es va veure que calia un sistema de base de dades senzill del tipus hash: una taula de claus i valors.

DBM (la BD de BSD) oferia la interfase adequada, però no permetia l’accés concurrent. Aleshores, es va reescriure DBM permetent aquest accés concurrent i el resultat va ser TDB. TDB es pot utilitzar com un alternativa concurrent de DBM.

A Ubuntu es poden instal·lar des del Centre de Programari tant la versió GNU de DBM, la GDBM, com la TDB.

TDB i GDBM ocupen molt poc espai i poden ser una bona sol·lució per a implementar taules hash amb C allà on calguin, en comptes de fer servir les funcions que ofereixen la llibreria estàndard de C, GLibc, o les de la llibreria de GTK, GLib.

Una preacució a tenir en compte és que existeixen diferents formats de fitxer dbm.

La instal·ació de TDB inclou, a més  dels llibreries i dels include,  els executables següents:

tdbbackup: és una eina que es pot fer servir per fer el backup des fitxers .tdb de Samba i verificar-ne la integritat. Si troba un fitxer .tdb de Samba fet malbé i un fitxer de backup previ, aleshores restaura el fitxer anterior.
tdbdump: és una eina que fa el volcat, o dump, d’un fitxer .tdb en un format legible. A més també pot fer el volcat d’una clau específica.           
tdbtool: permet manipular fitxers .tdb. Les opcions que ofereix aquest programa són:

albert@atenea:~$ tdbtool
tdb> help
database not open

tdbtool:
  create    dbname     : create a database
  open      dbname     : open an existing database
  transaction_start    : start a transaction
  transaction_commit   : commit a transaction
  transaction_cancel   : cancel a transaction
  erase                : erase the database
  dump                 : dump the database as strings
  keys                 : dump the database keys as strings
  hexkeys              : dump the database keys as hex values
  info                 : print summary info about the database
  insert    key  data  : insert a record
  move      key  file  : move a record to a destination tdb
  store     key  data  : store a record (replace)
  show      key        : show a record by key
  delete    key        : delete a record by key
  list                 : print the database hash table and freelist
  free                 : print the database freelist
  check                : check the integrity of an opened database
  speed                : perform speed tests on the database
  ! command            : execute system command
  1 | first            : print the first record
  n | next             : print the next record
  q | quit             : terminate
  \n                   : repeat ‘next’ command

per exemple, puc fer

albert@atenea:~$ tdbtool
tdb> create prova.tdb
tdb> insert clau1 valor1
tdb> insert clau2 valor2
tdb> insert clau3 valor3
tdb> dump

key 5 bytes
clau1
data 6 bytes
[000] 76 61 6C 6F 72 31                                 valor1

key 5 bytes
clau2
data 6 bytes
[000] 76 61 6C 6F 72 32                                 valor2

key 5 bytes
clau3
data 6 bytes
[000] 76 61 6C 6F 72 33                                 valor3
 tdb> quit
albert@atenea:~$ tdbdump prova.tdb
{
key(5) = “clau1”
data(6) = “valor1”
}
{
key(5) = “clau2”
data(6) = “valor2”
}
{
key(5) = “clau3”
data(6) = “valor3”
}

Amb TDB

Anem a repetir el mateix cas de prova de GLibC i GLib, però implementant-lo amb l’API C de TDB. Amb GDBM seria molt semblant.

El package de TDB em proporciona eines per a examinar fitxers de bases de dades TDB. Aquest pot ser un avantatge decisiu a l’hora de fer decantar-se per TDB per a implementar les taules hash d’una aplicació. No cal dir que l’API C de TDB permet volcar, i recuperar, taules hash a, i de, fitxers.

Com en els casos anteriors, he desenvolupat el programa amb Anjuta, amb un projecte C genèric mínim. M’ha calgut tocar el fitxer makefile.am (per a l’automake) per a afegir-li la referència a la
llibreria libtdb:

## Process this file with automake to produce Makefile.in


## Created by Anjuta


AM_CPPFLAGS = \
-DPACKAGE_DATA_DIR=\””$(datadir)”\” 


AM_CFLAGS =\
-Wall\
-g


bin_PROGRAMS = hashtables_tdb


hashtables_tdb_SOURCES = \
main.c


hashtables_tdb_LDFLAGS = -ltdb 


hashtables_tdb_LDADD = 

Vet aquí el codi (consulteu API TDB)

#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>     /* mode_t */
#include <tdb.h>


struct value {         /* aquesta estructura és la dels valors */
    int age, room;          /* emmagatzemats a la taula */
};


#define NUM_KEYS    5000    /* número d’elements. */
#define KEY_SIZE    20      /* tamanys de la clau*/


int main() {   
    TDB_CONTEXT *tdbHashTable;
    TDB_DATA tdbKey, tdbValue;
    char keysData[NUM_KEYS * KEY_SIZE]; /* espai d’emmagatzematge de la taula de claus. */
    struct value valuesData[NUM_KEYS]; /* espai d’emmagatzematge de la taula de valors. */
    char *keyPtr = keysData;     /* següent posició a l’espai de valors. */
    struct value *valuePtr = valuesData; /* següent posició a l’espai de claus. */   
    char keyToFind[KEY_SIZE]; /* clau a buscar, màxim de KEY_SIZE caràcters */
    int i=0; /* comptador de claus afegides*/


    /* Crea taula hash.*/
    printf(“Crea la taula\n”);
    tdbHashTable = tdb_open(“prova.tdb”, 
    0,
                            TDB_CLEAR_IF_FIRST, 
    O_RDWR | O_CREAT | O_TRUNC, 
    0666);


    printf(“Introdueix clau age room a la taula. Per acabar d’entrar valors prem CTRL-D\n”);
    while (scanf(“%s %d %d”, 
 keyPtr, 
 &valuePtr->age, 
 &valuePtr->room) != EOF && i++ < NUM_KEYS) {  
        /* traça */
        printf(“Afegeix %s %d %d a la taula\n”, 
               keyPtr, 
               valuePtr->age, 
               valuePtr->room);


        /* els posa a la taula */
        printf(“Afegeix – tdb_store\n”);
        
tdbKey.dptr = keyPtr;
tdbKey.dsize = strlen(keyPtr) + 1; /* caràcter 0 de final*/

tdbValue.dptr = valuePtr;
tdbValue.dsize = sizeof(struct value);

        tdb_store(tdbHashTable, tdbKey, tdbValue, TDB_INSERT);


/* prepara els punters per a nova clau valor*/
        keyPtr += strlen(keyPtr) + 1;
        valuePtr++;
    }




    /* Ara busca per la taula. */
    printf(“Busca valors a la taula. Per acabar CTRL-C\n”);
    keyPtr = keyToFind;
    while (scanf(“%s”, keyPtr) != EOF) {
        printf(“Buscant %s amb tdb_fetch\n”, keyPtr);
/* prepara la clau per a la cerca*/
tdbKey.dptr = keyPtr;
tdbKey.dsize = strlen(keyPtr) + 1;   
   
        tdbValue = tdb_fetch(tdbHashTable, tdbKey);
        if (tdbValue.dptr != NULL) {
            /* si ha trobat diferent de nul, el mostra. */
    /* obté el punter al valor a partir del TDB_DATA retornat*/
    valuePtr = tdbValue.dptr;


            printf(“found %s, age = %d, room = %d\n”,
                   keyPtr,
                   valuePtr->age,
                   valuePtr->room);
        } else
            printf(“not found %s\n”, keyPtr);
    }


    tdb_close(tdbHashTable);
return (0);
}

No hi han gaires diferències amb els casos anteriors. Si de cas remarcar l’ús de l’estructura TDB_DATA, per a passar, i recuperar, les claus i els valors a, i de, les funcions de l’API.

Amb la funció tdb_open es crea físicament un fitxer prova.tdb que podrem examinar (i modificar) amb tdbtool. Tanmateix, hauria pogut fer servir l’opció TDB_INTERNAL per a crear  la taula hash només en memòria.

Una execució del programa anterior:

albert@atenea:~/wk-c/prova-hash-tdb$ hashtables_tdb 
Crea la taula
Introdueix clau age room a la taula. Per acabar d’entrar valors prem CTRL-D
albert 42 125
Afegeix albert 42 125 a la taula
Afegeix – tdb_store
montse 40 125
Afegeix montse 40 125 a la taula
Afegeix – tdb_store
artiom 6 125
Afegeix artiom 6 125 a la taula
Afegeix – tdb_store
^D
Busca valors a la taula. Per acabar CTRL-C
prova
Buscant prova amb tdb_fetch
not found prova
albert
Buscant albert amb tdb_fetch
found albert, age = 42, room = 125
montse
Buscant montse amb tdb_fetch
found montse, age = 40, room = 125
artiom
Buscant artiom amb tdb_fetch
found artiom, age = 6, room = 125
^C
albert@atenea:~/wk-c/prova-hash-tdb$ 

I, a més, tenim un fitxer prova.tdb amb la taula generada. Aquest fitxer el podem examinar amb tdbtool:
albert@atenea:~/wk-c/prova-hash-tdb$ tdbtool prova.tdb
tdb> dump

key 7 bytes
artiom
data 8 bytes
[000] 06 00 00 00 7D 00 00 00                           ….}.. 

key 7 bytes
montse
data 8 bytes
[000] 28 00 00 00 7D 00 00 00                           (…}.. 

key 7 bytes
albert
data 8 bytes
[000] 2A 00 00 00 7D 00 00 00                           *…}.. 
tdb> quit
albert@atenea:~/wk-c/prova-hash-tdb$ 
Dump fa un volcat de la taula en format de cadena. Què obtenim? pèr a cada registre de la taula obtenim: la clau i 8 bytes de dades en hexadecimal. No cal dir que 7D és 125; 06 hex. és 6 decimal; 28 hex. és 42 decimal i 2A hex, 40 decimal.
Conclusió

Evidentment, aquests petits escripts només són una aproximació a la implementació de taules hash amb GlibC, Glib i TDB.
Segurament, per a la majoria d’aplicacions n’hi ha prou i són més que suficients les funcions per a hashtables de la llibreria estàndard de C (GLibC); tanmateix, en aplicacions  GTK, la GLib hi és present i aporta funcions més flexibles -per exemple, permetent definir la funció hash- i, a més, fan servir els tipus de dades de GTK, sent consistents amb la resta de l’aplicació.
Finalment, TDB pot ser una bona tria en el cas que ens interessi poder disposar de la taula hash en un fitxer i, a més, ens proporciona eines addicionals, la tdbtool, per a manipular aquests fitxers. 

Parseig d’XML amb C# i DOM (Mono)

Una de les guerres religioses més interessants que hi han hagut en els darrers temps en el món del programari lliure ha estat entre els partidaris i detractors de Mono.

Mono és una implementació lliure de .Net per a màquines GNU/Linux basada en l’estàndar ECMA. La batalla al voltant d’aques projecte és perquè part de les eines que s’han implementat podrien no respectar les llicències de Microsoft. Es tem que, en un moment donat, els de Redmond pordrien denunciar aquesta possible violació de llicències i provocar que tot allò que estigues desenvolupat a partir d’aquest programari no lliure de Mono quedés en una situació difícil.

Els defensors de Mono diuen que les parts conflictives són molt poques i estan ben localitzades i que programar sobre Mono no presenta perill.

No prendré partit. En tot cas, des del meu punt de vista, Mono ofereix una plataforma  alternativa als desenvolupadors de .Net. Una plataforma basada en programari i sistemes operatius lliures, amb l’extraordinari estalvi de cost de llicències que això suposa.

Per mi, doncs, Mono és una plataforma a tenir en compte pels desenvolupadors. I no només. Amb Mono sobre Linux i Apache es poden executar aplicacions  CMS com dotNetNuke, o  MojoPortal. EL trio Linux,Apache,Mono ofereix, doncs, una alternativa per al hosting amb ASP.

Mono ens porporciona eines tan interessants com els compiladors de C# i  VB.NET que poden compilar a Linux nadiu però també al bytecode del CLI i, per tant, poden produir executables aptes per Windows.

En aquest post presento com fer servir un fitxer XML com a fitxer de configuració.  NET no té, com Java, una classe per a fitxers de properties de Unix, ni fitxers .ini de Windows. És dir, fitxers amb files  nom=valor. La filosofia NET recomana fer servir XML per a les configuracions.

NET és eficient amb l’XML. Per als programadors de NET  provinents de Java l’anàlisi de fitxers XML té dos noms principals associats: DOM i SAX.

De forma molt resumida: DOM permet analitzar fitxers més aviat petits i es basa en carregar el fitxer en memòria i exposar-lo a les aplicacions com un arbre d’objectes que ens representen els elements i els atributs.

SAX, per la seva banda, és un parser que processa  un flux XML i que respon, amb funcions de callback, quan es produeixen determinats esdeveniments, per exemple l’aparició d’una etiqueta concreta o una ordre de processament. SAX és lleuger i  adequat per al processat de grans fluxos, o grans fitxers d’XML.

Doncs bé. L’API estàndar de SAX no està al framework de .NET. Existeix, això sí, una API pròpia que és, conceptualment, equivalent a SAX (diguem que el parseig basat en XmlTextReader). I també es pot trobar la implementació de SAX per a .NET, com una descàrrega externa.

En canvi, l’API estàndar DOM està implementada al framework de .NET. El que faré serà aprofitar l’API DOM de NET per a fer servir un fitxer  XML com a fitxer de configuració.

El fitxer XML és aquest:

<?xml version=”1.0″ encoding=”utf-8″ ?>
<!DOCTYPE claus> 
<claus>
<clau nom=”Nom1″ valor=”Valor1″ />
<clau nom=”Nom2″ valor=”Valor2″ />

</claus>

Primer de tot, amb el MonoDevelop (instal·lable des del Centre de programari de l’Ubuntu) crearé una nova Solució del tipus aplicació de consola amb  C#.
Afegeixo la referència System.Xml i creo dues classes C#

KeyValuePair és, com el seu nom indica, una estructura de dades clau-valor

using System;
using System.Collections.Generic;
using System.Text;

namespace provaproperties
{
    class KeyValuePair
    {
        string sKey;
        string sValue;

        public KeyValuePair(string sKey, string sValue)
        {
            this.sKey = sKey;
            this.sValue = sValue;
        }

        public string getKey()
        {
            return sKey;
        }

        public string getValue()
        {
            return sValue;
        }

    } // de la class
} // del namespace
La classe Config, carrega un fitxer XML de configuració, l’analitza amb XML i el fa servir per carregar parells nom valor en una classe de col·lecció ArrayList. A més, proporciona un mètode per obtenir un valor de la llista a partir d’una clau.

Un incís: el que acabo d’implementar aquí és un diccionari. NET proporciona la classe Dictionary a System.Collections.Generic. L’objectiu del post era presentar l’ús de DOM, no implementar un diccionari. Si el que ens cal és un diccionari, la millor opció, evidentment, és fer  servir la classe del framework.

La classe config:


using System;
using System.Collections;
using System.Collections.Generic;
using System.Xml;
using System.Text;

namespace provaproperties
{
    class Config
    {
        ArrayList arrKeyValuePairs = null;
       
        public static void Main (string[] args)
        {
            Console.WriteLine (“Anàlisi de fitxer de configuració”);
            Config cf = new Config(“./config.xml”);
            Console.WriteLine(“clau: Nom1; valor: {0}”, cf.getValue(“Nom1”));
            Console.WriteLine(“clau: Nom2; valor: {0}”, cf.getValue(“Nom2”));
            Console.WriteLine(“clau: Nom3; valor: {0}”, cf.getValue(“Nom3”));   
        }
       
       
        public Config(string sXmlConfigFile)
        {
            arrKeyValuePairs = new ArrayList();
           
            /* XML de configuracions
            * <?xml version=”1.0″ encoding=”utf-8″ ?>
            * <!DOCTYPE claus>
            * <claus>
            *   <clau nom=”Nom1″ valor=”Valor1″ />
            *   <clau nom=”Nom2″ valor=”Valor2″ />
            *   …
            * </claus>
            */

            string sClau = “”;
            string sValor = “”;
           
            // DOM parsing
            XmlDocument xdoc = new XmlDocument();
            xdoc.Load(sXmlConfigFile);
            XmlNodeList xnl = xdoc.GetElementsByTagName(“clau”);
            foreach (XmlNode node in xnl)
            {
                XmlAttributeCollection attrColl = node.Attributes;
                foreach( XmlAttribute attr in attrColl)
                {
                    string sName = attr.Name;
                    string sValue = attr.Value;

                    if (sName == “nom”)
                    {
                        sClau = sValue;
                    }

                    if (sName == “valor”)
                    {
                        sValor = sValue;
                    }
                }

                // afegeix les claus a l’Array
                arrKeyValuePairs.Add(new KeyValuePair(sClau, sValor));
            }
        }


        public string getValue(string sClau)
        {
            // si no el troba, torna “”.
           
            string sValue = “”;

            foreach (KeyValuePair item in arrKeyValuePairs)
            {
                if (item.getKey() == sClau)
                {
                    sValue = item.getValue();
                    break;
                }
            }
           
            return sValue;
        }
       
    } // de la class
}  // del namespace

I compilem. L’execució del programa proporciona l’efecte esperat:

albert@atenea:~/wk-mono/prova-properties/prova-properties/bin/Debug$ ./prova-properties.exe
Anàlisi de fitxer de configuració
clau: Nom1; valor: Valor1
clau: Nom2; valor: Valor2 
clau: Nom3; valor:

Encara un altre incís: si la intenció és fer servir l’XML com a fitxer de configuració mitjançant DOM, aleshores, el fet de carregar una ArrayList amb els parells nom valor resulta, si més no, redundant.

Una solució sense redundàncies seria crear un mètode que obtingués el valor corresponent a la clau cercada directament a partir del Document XML parsejat. Per exemple, una cosa com la següent. Un detall interessant, la doble iteració sobre els atributs. És necessària perquè l’ordre de la col·lecció d’atributs no està determinat i, si fes una sola passada, podria ser que el valor aparegués abans del nom, i no sabria identificar-lo. Vet aquí el codi:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Xml;
using System.Text;

namespace provaproperties
{
    class XmlConfig
    {
        XmlDocument xdoc = null;
       
        public static void Main (string[] args)
        {
            Console.WriteLine (“Anàlisi de fitxer de configuració – millorat”);
            Config cf = new Config(“./config.xml”);
            Console.WriteLine(“clau: Nom1; valor: {0}”, cf.getValue(“Nom1”));
            Console.WriteLine(“clau: Nom2; valor: {0}”, cf.getValue(“Nom2”));
            Console.WriteLine(“clau: Nom3; valor: {0}”, cf.getValue(“Nom3”));   
        }
       
       
        public XmlConfig(string sXmlConfigFile)
        {
           
            /* XML de configuracions
            * <?xml version=”1.0″ encoding=”utf-8″ ?>
            * <!DOCTYPE claus>
            * <claus>
            *   <clau nom=”Nom1″ valor=”Valor1″ />
            *   <clau nom=”Nom2″ valor=”Valor2″ />
            *   …
            * </claus>
            */
           
            // DOM parsing
            XmlDocument xdoc = new XmlDocument();
            xdoc.Load(sXmlConfigFile);
        }

        public string getValue(string sClau)
        {
            // si no el troba, torna “”.
            string sFoundValue = “”;

            // obté la llista de nodes
            XmlNodeList xnl = xdoc.GetElementsByTagName(“clau”);
           
            // itera pels nodes
            foreach (XmlNode node in xnl)
            {
                // obté els atributs del node
                XmlAttributeCollection attrColl = node.Attributes;
                Boolean boolTrobat = false;
               
                // itera pels atributs del node
                foreach( XmlAttribute attr in attrColl)
                {
                    // nom de l’atribut
                    string sName = attr.Name;
                    // valor de l’atribut
                    string sValue = attr.Value;

                    // si el nom de l’atribut és nom
                    if (sName == “nom”)
                    {
                        // aleshores, si el valor coincideix amb la clau buscada
                        if (sValue == sClau)
                        {
                            // és que l’ha trobat
                            boolTrobat = true;
                        }
                    }
                }
                   
                // si l’ha trobat, aleshores busca el valor
                if (boolTrobat) {
                    // itera pels atributs del node un altre cop
                    foreach( XmlAttribute attr in attrColl)
                    {
                        // nom de l’atribut
                        string sName = attr.Name;
                        // valor de l’atribut
                        string sValue = attr.Value;
   
                        // si el nom de l’atribut és valor
                        if (sName == “valor”)
                        {
                            // aleshores, ja el tenim
                            sFoundValue = sValue;
                            break;
                       
                        }
                    }
                }   
            } // del foreach
           
            return sFoundValue;
       
        } // del mètode       
    } // de la class
}  // del namespace

Com abans, compilem i executem. El resultat és l’esperat:

albert@atenea:~/wk-mono/prova-properties/prova-properties/bin/Debug$ ./prova-properties.exe
Anàlisi de fitxer de configuració – millorat
clau: Nom1; valor: Valor1
clau: Nom2; valor: Valor2
clau: Nom3; valor:

API C per a SQLite3

En aquest post presento un exemple de com fer servir l’API de C per a SQLite3 a Ubuntu 10.04.

L’API de C per a SQLite3 ens proporciona una interfície eficient per a programar aplicacions amb aquesta base de dades.

Per una banda crearé una base de dades nova. Fem servir l’SQLite Database Browser (es pot instal·lar des del Centre de Programari de l’Ubuntu).

Creo la ‘taula1’ amb tres columnes: id, del tipus integer; i valor i traduccio, les dues del tipus varchar.

Informo la taula amb algunes dades de prova:

Desenvolupo l’exemple fent servir la IDE Anjuta (si cal, es pot instal·lar des del centre de programari de l’Ubuntu). Creo un projecte C nou del tipus genèric (mínim).
Fins i tot sent un projecte “mínim” la quantitat de fitxers que genera Anjuta és important. Tanmateix, només ens caldrà modificar-ne dos: el main.c i el Makefile.am.

Li dono la ubicació del projecte i al fitxer main.c hi posem el següent codi


/*
 * main.c
 * Copyright (C) albert 2011 <stsoftlliure@gmail.com>
 *
 * c-sqlite is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * c-sqlite is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/&gt;.
 */

#include <stdio.h>
#include <sqlite3.h>
int main(void) {
    /* variables */
    sqlite3 *conn;
    sqlite3_stmt *stmtResultSet;
    int iError=0;
    int iComptador=0;
    const char *tail;
   
    /* obre  la connexió a la bd*/
    /* int sqlite3_open(
    /*     const char *filename,   /* Database filename (UTF-8) */
    /*     sqlite3 **ppDb          /* OUT: SQLite db handle */
    /* );
    /*****/
    iError = sqlite3_open(“/home/albert/databases/sqlite3/prova-sqlite2/prova2.db3”,
                          &conn);
    if (iError) {
        puts(“No pot obrir la base de dades”);
        exit(0);
    }
   
    /* crea una instrucció per a executar-la, en aquest cas un update */
    /* int sqlite3_exec(
    /*     sqlite3*,                                  /* An open database */
    /*     const char *sql,                           /* SQL to be evaluated */
    /*     int (*callback)(void*,int,char**,char**),  /* Callback function */
    /*     void *,                                    /* 1st argument to callback */
    /*     char **errmsg                              /* Error msg written here */
    /* );
    /*****/
    iError = sqlite3_exec(conn,
                          “update taula1 set traduccio=\’nova traducció\’ where id=5”,
                          0,0,0);
    if (iError) {
        puts(“Error en fer update”);
        exit(0);
    }
   
    /* fa una query i obté un “resultset” */
    /*
    /* int sqlite3_prepare_v2(
    /*     sqlite3 *db,            /* Database handle */
    /*    const char *zSql,       /* SQL statement, UTF-8 encoded */
    /*    int nByte,              /* Maximum length of zSql in bytes. */
    /*    sqlite3_stmt **ppStmt,  /* OUT: Statement handle */
    /*    const char **pzTail     /* OUT: Pointer to unused portion of zSql */
    /* );
    /*****/
    iError = sqlite3_prepare_v2(conn,
                                “select id, valor, traduccio from taula1 order by id”,
                                1000,
                                &stmtResultSet,
                                &tail);
    if (iError != SQLITE_OK) {
        puts(“No pot obtenir dades”);
        exit(0);
    }
   
    puts(“—————————————“);
   
    /* mostra els resultats obtinguts iterant pas a pas pel pel “ResultSet” */
    /*
        int sqlite3_step(sqlite3_stmt*);
        After a prepared statement has been prepared using either sqlite3_prepare_v2() 
        this function must be called one or more times to evaluate the statement.
        If the SQL statement being executed returns any data,
        then SQLITE_ROW is returned each time a new row of data is ready
        for processing by the caller.
        The values may be accessed using the column access functions.
        sqlite3_step() is called again to retrieve the next row of data.
       
        Result Values From A Query:
        int sqlite3_column_int(sqlite3_stmt*, int iCol);
        const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);
    */                   
    while(sqlite3_step(stmtResultSet) == SQLITE_ROW) {
        /* en la fila actual, obté els valors de les columnes */
        printf(“%d |”, sqlite3_column_int(stmtResultSet, 0)); /* id,  decimal */
        printf(“%s |”, sqlite3_column_text(stmtResultSet, 1)); /* valor, varchar */
        printf(“%s \n”, sqlite3_column_text(stmtResultSet, 2)); /* traducció, varchar */
        iComptador++;
    }
    printf(“Total de registres: %d\n”, iComptador);
   
    /* tanca el resultset */
    /*
    /* int sqlite3_finalize(sqlite3_stmt *pStmt);
    /* The sqlite3_finalize() function is called to delete a prepared statement.
    /*****/
    sqlite3_finalize(stmtResultSet);
   
    /* tanca la connexió */
    /*
    /* int sqlite3_close(sqlite3 *);
    /* The sqlite3_close() routine is the destructor for the sqlite3 object.
    /*****/
    sqlite3_close(conn);
   
    return (0);
}

Per a poder compilar el codi anterior, és necessari enllaçar-lo amb la llibreria de l’SQLite3. Per a fer això, modifico {nom-projecte}_LDFLAGS = -lsqlite3.
Com a alternativa, també hauria pogut generar un makefile particular.

Inclús en tindríem prou amb una instrucció del tipus:
gcc ./main.c -o prova-csqlite.exe -lsqlite3

Finalment, podem compilar i muntar amb l’Anjuta; i executem:

EXECUTING:
/home/albert/wk-c/prova-c-sqlite/c_sqlite
———————————————-
1 |valor1 |traducció 1
2 |valor2 |traducció 2
3 |valor3 |traducció 3
4 |valor4 |traducció 4
5 |valor5 |nova traducció
Total de registres: 5

———————————————-
Program exited successfully with errcode (0)
Press the Enter key to close this terminal …

Com sempre, diposar de la documentació és clau per a poder reeixir en els desenvolupaments. La documentació està disponible al lloc de SQLite a Internet. D’aquesta altre adreça en podem descarregar un zip amb la documentació.