| ![[UACA]](../../img/escudo02.gif)  | ![[/\]](../../img/top.gif) Acta Académica Universidad Autónoma de Centro América | 
| ![[<=]](../../img/back.gif)  ![[home]](../../img/home.gif)  | ![[<>]](../../img/index.gif)  | ![[\/]](../../img/bottom.gif)  ![[=>]](../../img/next.gif)  | 
genridx.h"
     | Adolfo Di Mare | 
![[<>]](../../img/index.gif) 
 ![[\/]](../../img/bottom.gif) 
 ![[/\]](../../img/top.gif) 
 
| " genridx.h" es una interfaz que 
unifica el acceso a archivos indizados, lo que permite escribir 
programas C que son parcialmente independientes del método 
de acceso utilizado.  Esta independencia física del 
método de acceso comporta un costo despreciable en espacio 
y tiempo de ejecución , y le brinda al programador la 
oportunidad de cambiar el método de acceso con facilidad.  
Se discuten además varias ideas sobre cómo usar 
archivos en programas C. Como ejemplo, se discute una 
implementación completa de la interfaz para el 
método de acceso Btrieve de la casa
Novell. | " genridx.h" is an unyfied interface 
to access indexed files in C, to support writting programs that 
are partially independent from the access method in use.  This 
physical independence has a negligible cost both in execution time 
and space, and gives the programmer the oportunity to change the 
access method with ease.  Some ideas on how to use record files in 
C are also discused.  As an example, a complete implementation to 
interface Novell's Btrieve is described. | 
![[<>]](../../img/index.gif) 
 ![[\/]](../../img/bottom.gif) 
 ![[/\]](../../img/top.gif) 
 
Para desarrollar aplicaciones administrativas en C es necesario complementar al lenguaje con al menos dos tipos de herramienta. La primera es un manejador de interfaz con el usuario, la que permite al programador definir fácilmente las pantallas de la aplicación. Ejemplo de éste tipo de biblioteca es el C Tools Plus para MS/DOS o el paquete Curses en UNIX.
El segundo tipo de herramienta a utilizar es un manejador de archivos, lo que permite al programador crear y dar mantenimiento a ficheros de registros [12]. El servicio que brinda un manejador de archivos o método de acceso es el permitir que los registros del archivo puedan ser recuperados con base en el contenido de alguno de sus campos, y no simplemente por su posición física dentro del archivo. Esta forma de acceso se conoce como acceso por medio de llaves, y es fundamental para el manejo de bases de datos. De otra manera no sería posible obtener los registros de una persona almacenados en un computador si se tiene el número de identificación de la persona.
Ejemplo de manejadores de archivos son el Btrieve de Novell [9] para ambientes IBM/pc y el CBtree en UNIX. Algunos vendedores de programas llaman a estos métodos de indización sistemas de bases de datos, aunque este término debe reservarse para sistemas de administración de archivos más sofisticados y completos que los manejadores de archivos a los que se refiere este documento.
En este artículo se discute una interfaz uniforme para este tipo de bibliotecas. A diferencia de otros enfoques ([4], [7], [10]), esta interfaz permite escribir programas que mantienen una independencia parcial del sistema de archivos utilizado. Esta independencia física del método de acceso implica un costo muy reducido en espacio y en tiempo de ejecución, y le brinda al programador la oportunidad de cambiar el método de acceso a archivos con gran facilidad.
Aunque la interfaz que se presenta como ejemplo ha sido implementada en Borland C++ v3.1 [1] en el contexto del manejador de archivos Btrieve de Novell, es relativamente sencillo adaptarla a cualquier otro manejador de archivos o a otra versión del lenguaje C. Aunque C++ es el lenguaje que más apoyo tiene ahora, por lo menos en las publicaciones técnicas, esta interfaz fue escrito en C porque en mucho ambientes, principalmente en Unix, C++ todavía no es una tecnología que cuente con el apoyo que C ya tiene. Las ideas que aquí se presentan pueden ser desarrolladas también usando las plantillas de C++ [3] pero todavía la mayoría de los compiladores no soportan esta nueva construcción sintáctica.
Esta interfaz es útil para métodos de acceso que permiten accesar registros de uno en uno (one record at a time access). En el caso de los sistemas administradores de bases de datos relacionales, que en general usan SQL, con esta interfaz se pueden recibir los tuples de uno en uno, pero la interfaz no tiene la capacidad de recibir varios registros simultáneamente. Como la mayoría de los sistemas relacionales poseen interfaces para el lenguaje C ([8], [11]), esta interfaz es también aplicable en estos casos.
     
Los programas que acompañan a este artículo 
están documentado en la lengua inglesa, que es la lengua de 
la computación.  Es más cómodo producir 
código en el lenguaje universal de la informática 
pues entonces es más fácil intercambiarlo con otros 
programadores. El código C en este artículo 
está disponible en esta localización Internet:
    
     
    
        http://www.uaca.ac.cr/actas/1994nov/genridx.zip
![[<>]](../../img/index.gif) 
 ![[\/]](../../img/bottom.gif) 
 ![[/\]](../../img/top.gif) 
 
     
Un archivo de declaraciones en general tiene un nombre corto, que 
en general es de hasta ocho caracteres, y su extensión es 
".h" (punto-hache, dot-h en inglés).  
Los archivos de código generalmente tienen extención 
".c" (o ".cpp" para C++).
     
El C ANSI incluye una biblioteca estándar de funciones, 
cuyos prototipos están descritos en los archivos de 
encabezados estándar.  Por ejemplo, para usar el 
procedimiento printf(...), la manera de hacerlo es la 
siguiente:
#include <stdio.h> /* declara printf(...) y otros */ /*....*/ printf(...); /* uso de la función printf() */
Esta forma de verificar interfaces entre los diversos módulos que conforman un sistema (archivos en el caso de C) es bastante rudimentaria, pero efectiva. Otros lenguajes (como Modula 2, ADA y Turbo Pascal) cuentan con esquemas mucho más elaborados para verificación de interfaces, pero requieren de ligadores de eslabonamiento (linkers en inglés) mucho más sofisticados. Para programar en C es necesario entender bien cómo funciona el sistema de verificación de interfaces; quien no lo conoce, en general, no puede hacer uso adecuado del lenguaje.
![[<>]](../../img/index.gif) 
 ![[\/]](../../img/bottom.gif) 
 ![[/\]](../../img/top.gif) 
 
| +NUMERO....+CHAR+08+.+ +NOMBRE....+CHAR+15+.+ +SEXO......+CHAR+01+0+ /* ENCABEZADO */ +SUELDO....+DEC.+10+2+ +NACIDO....+DATE+08+.+ 10-03-24MAYELA VARGAS030000.00F561010 20-00-32JUAN PELOTAS 000000.00M751023 /* REGISTROS DE */ 30-15-99RAMBO RAMIREZ000112.00M700802 /* DATOS */ | 
En un ambiente de bases de datos lo usual es que el sistema de acceso incluya una facilidad llamada diccionario de datos que permite definir todos los archivos y sus campos. Manejadores de archivos, como Dbase, incluyen un archivo de encabezado (header) dentro de cada archivo que contiene la descripción de los campos, como se muestra en la Figura 1. De esta manera todos los programas que usan un archivo Dbase tienen acceso a su descripción actualizada.
En general, los diccionarios de datos de los sistemas de bases de datos utilizan estructuras externas a cada archivo para almacenar esas definiciones. Por ejemplo, el Unify para UNIX almacena el diccionario de datos en archivos de un formato especial; en este caso, el diccionario de datos también está descritos en el mismo diccionario.
     
Para el programador Dbase no es problema usar un archivo desde 
varios programas de aplicación, pues el sistema puede leer 
la descripción del archivo al abrirlo. El programador 
db_Vista III en UNIX usa varios archivos de encabezado 
(.h) en los que se describen los campos de cada 
archivo.  Estos encabezados son generados por el utilitario de 
mantenimiento del diccionario de datos.
Esta disciplina en el uso de archivos, impuesta en unos casos por el manejador de archivos (como es el caso en Dbase), y en otros por el sistema de bases de datos (com es el caso en db_Vista III), es muy beneficiosa para el programador, pues logra que la definición de cada archivo esté disponible para todos los programas que los usan. Esta es, precisamente, una de las justificaciones originales para pagar el alto precio de utilizar sistemas de bases de datos: lograr que todos los programas que utilizan un mismo archivo tengan acceso a su definición actualizada. De lo contrario sucede que cuando cambia la definición del archivo entonces unos programas lo actualizarán correctamente y otros no, lo que introduce inconsistencias en los datos almacenados.
| 
/* @(#)    usaper.c     NO-Copyright 1991 Adolfo Di Mare       */
/* Ejemplo de como hacer MAL las cosas al usar archivos        */
#include <stdio.h> /* declara fopen() y fread() */
/* ... */
struct persona {
    char NUMERO[08];    /* declara el registro sin usar */
    char NOMBRE[15];    /* un archivo de cabezal (.h)   */
    char SEXO;
    char SUELDO[10];
    char NACIDO[08];
} var1, var2;
FILE *f;
main() {
    /* ... */
    f = fopen("personas.dat", "rt");   /* abre el archivo */
    fread(var1, sizeof(var1), 1, f);   /* lee el registro */
    /* ... */
}
/* End of file usaper.c */
 | 
     
Tal vez parezca innecesario exigir que se declare bien cada 
archivo utilizado en un programa (¡pues es obvio que hay que 
hacerlo!), pero lo cierto es que muchos programadores C no lo 
hacen. El autor ha leído muchos programas C en que los 
archivos se declaran no en un encabezado (.h), como 
es propio, sino en el código de implementación 
(.c).  Por ejemplo, para usar un archivo de personas 
como el de la
Figura 1, muchos programadores C 
escriben el código como se muestra en la
Figura 2.
     
Como el programa de la
Figura 2 funciona adecuadamente, 
después de escribirlo el programador se dedicará al 
siguiente trabajo.  El problema surge cuando se necesita usar el 
mismo archivo en varios programas, que es lo usual en cualquier 
sistema de información. En este caso, siempre que se 
requiera cambiar la estructura del registro de personas 
será necesario cambiar manualmente todos los programas que 
lo usan.  Deberán además documentarse todos los 
nombres que se usan en el archivo de personas, pues unos 
llamarán a la estructura de una forma 
("persona" como en el ejemplo), y otros de otra 
("PERSONA" o "Persona" son candidatos 
igualmente válidos). El uso de archivos de encabezados para 
describir archivos evita todos estos problemas.
![[<>]](../../img/index.gif) 
 ![[\/]](../../img/bottom.gif) 
 ![[/\]](../../img/top.gif) 
 
.h) en donde estén descritos todos 
los datos de una persona, como se muestra en el
Listado 1.
Para accesar el archivo de personas el programador debe hacer lo 
siguiente:
person.h" 
  usando la directiva #include del preprocesador del 
  lenguaje C:
   - #include "person.h"
person", basta hacerlo usando la sintaxis C usual:
 
    - float   sueldo, planilla[MAX_planilla];
    - person p1, p2, p[20]; /* p es un vector de 20 personas 
*/
 person.h", para luego recompilar todos los 
  programas que lo utilicen (lo que es relativamente sencillo si 
  se usa el programa MAKE para recompilar programas).En general, la forma de utilizar los archivo de encabezado en una aplicación es la siguiente:
El uso de encabezados para describir los archivos de una aplicación es claramente una necesidad. Los programadores que no utilicen encabezados no sólo pecan por ignorantes, sino que además están incrementando innecesariamente el costo de construcción y mantenimiento de los sistemas de aplicación[1].
Sin embargo, el uso de archivos de encabezado no es el único ingrediente necesario para implementar una interfaz independiente del método de acceso. Antes de discutir en detalle la forma de lograr esta independencia es necesario discutir las ventajas de usar el macro preprocesador del lenguaje C.
![[<>]](../../img/index.gif) 
 ![[\/]](../../img/bottom.gif) 
 ![[/\]](../../img/top.gif) 
 
#define. Ejemplo clásico de macros C son 
MAX() y MIN():
#define MAX(x,y) ((x)>(y) ? (x) : (y)) #define MIN(x,y) ((x)<(y) ? (x) : (y))
     
Antes de compilar el programa cada macro es sustituida por los 
símbolos que aparecen en su definición, sustiyendo 
los argumentos literalmente.  El producto de esta 
sustitución es lo que el compilador C realmente digiere.  
Por ejemplo, si el programador escribe en su programa 
MAX(a+b,MIN(c,d)), el resultado es que el compilador 
procese:
((a+b)>(((c)<(d) ? (c) : (d))) ? (a+b) : (((c)<(d) ? (c) : (d))))
También el preprocesador permite la compilación condicional de programas, con lo que es posible compilar unas partes del código dependiendo del valor de ciertas macro variables (definidas en tiempo de compilación).
Mediante la compilación condicional y el uso de macros es posible escribir código C parametrizable. En su implementación, la interfaz que se presenta en este artículo requiere de estas dos facilidades del preprocesador C.
genridx.h" 
 
 ![[<>]](../../img/index.gif) 
 ![[\/]](../../img/bottom.gif) 
 ![[/\]](../../img/top.gif) 
 
genridx.h"
(GENeRic InDeX)
en el que se definen los verbos para accesar archivos indizados.
     
Las macros de "genridx.h" son la base sobre la que se 
construye la interfaz para archivos indizados.  El programador 
usuario siempre usará estas macros para usar los 
índices de sus archivos.  La idea es que cada macro se 
transforma en una invocación a una función o a otra 
macro, y el nombre del manejador de archivos sirve para distinguir 
la interfaz en uso.  De esta manera es posible utilizar en el 
mismo programa diversos manejadores de archivos, o sustituir uno 
por otro cuando así lo requiera la aplicación.
| 
/* @(#)   btrieve.h             Copyright 1990 Adolfo Di Mare   */
/*      Interface to the Btrieve Record Manager, version 4      */
#ifndef _btrieve_h
#define _btrieve_h  /* evita inclusión múltiple */
#ifdef __cplusplus
extern "C++" {       /* compatibilidad con C++ */
#endif
#include "genridx.h"
/* Función de interfaz con Btrieve */
int BTRV(int OP, char POS_BLOCK[], char DATA_BUF[],
         int *DATA_LEN, char KEY_BUF[], int KEY_NUM);
enum btrieve_constants {
    BT_MAX_KEYS = 24,       /* max indices por archivo */
    POSTION_BLOCK_SZ = 128, /* área de trabajo Btrieve */
    /* ...  */
};
typedef struct { /* ... */ } btr_filespec;  /* archivo */
typedef struct { /* ... */ } btr_keyspec;   /* llaves  */
typedef struct {   /* Descriptor de archivos para "btrieve.c" */
    /* Campos publicos obligatorios */
    INT2         key_number;  /* número de llave en uso */
    int          locking;     /* TRUE al usar bloqueo   */
    INT2         status;      /* último status Btrieve  */
    /* Campos exclusivos de Btrieve */
    char       * file_name;   /* nombre DOS del archivo    */
    const char * type_name;   /* tipo de registro          */
    char       * key_buffer;  /* memoria para la llave     */
    btr_spec   * stat;        /* descriptor Btrieve        */
    char      pos_block[POSTION_BLOCK_SZ]; /* área Btrieve */
    INT4         last_drn;    /* drn del último registro      */
    INT2         open_mode;   /* modo de apertura del archivo */
    INT2         real_length; /* longitud real del registro   */
    INT2         last_length; /* longitud del último registro */
} Btrieve_access;
#define Btrieve_declare  Btrieve_access
/* Constantes para Btrieve */
#define Btrieve_OK                  BT_OK
#define Btrieve_NO_MEM              BT_INSUFFICIENT_MEMORY
/* ... */
/* Operación genérica de lectura [Verbos GET] */
xdret_t Btrieve_generic(
    short            op_code,     /* Btrieve operation code      */
    Btrieve_access * file         /* contains all the file data  */
);
/* Códigos de operación Btrieve */
#define BT_GET_EQUAL   5
#define BT_GET_NEXT    6
#define BT_STEP_NEXT  24
#define Btrieve_find(file)                                       \
    (                                                            \
    Btrieve_build_key(                                           \
        (file).stat,        (file).key_number,                   \
        (file).key_buffer,  (file).data_buffer                   \
    ),                                                           \
    Btrieve_generic(                                             \
          BT_GET_EQUAL,        /* operation code         */      \
        & (file)               /* file descriptor        */      \
    )                                                            \
    )
#define Btrieve_next(file_desc)                                  \
    Btrieve_generic(                                             \
          ( DRN_ACCESS  == (file_desc).key_number                \
            ?  BT_STEP_NEXT                                      \
            :  BT_GET_NEXT     /* operation code         */      \
          ),                                                     \
        & (file_desc)          /* file descriptor        */      \
    )
#define Btrieve_select(file_desc,  key)                          \
    ( (file_desc).key_number =                                   \
        (                                                        \
            ( ( DRN_ACCESS  <= (short) (key))   &&               \
              (short) (key) < (file_desc).stat->file.n_index )   \
            ? (short) (key)                                      \
            : (file_desc).key_number                             \
        )                                                        \
    )
#ifdef __cplusplus
}
#endif
#endif  /* _btrieve_h */
 | 
btrieve.h: extracta del archivo
     
Al crear la interfaz para el Btrieve fue necesario crear varias 
macros y funciones cuyo nombre comienza con 
"Btrieve_", las cuales se encuentran en el archivo de 
encabezado llamado "btrieve.h", parte del cual se 
muestra en la
Figura 3. Este archivo incluye, usando 
la directiva del preprocesador #include, a 
"genridx.h", el archivo en que se definen los verbos 
que forman la interfaz de acceso a archivos indizados.  Cada macro 
o función definida en el archivo de encabezado 
"btrieve.h" es invocada, indirectamente, cuando el 
programador utiliza los verbos de acceso definidos en 
"genridx.h".  El programador nunca utiliza 
directamente las macros y funciones definidas en 
"btrieve.h".  La independencia del manejador de 
archivos se logra al cambiar el valor del primer argumento de cada 
uno de los verbos de acceso definidos en "genridx.h".
     
Por ejemplo, la invocación de la macro 
xd_select(Btrieve, bFILE, 3) se transforma 
en Btrieve_select(bFILE, 3), que a su vez es una 
invocación a una macro (definida en 
"btrieve.h"). Si el programador quisiera usar el 
manejador de archivos DDMPC, entonces bastará que use 
xd_select(DDMPC, bFILE, 3), para así 
obtener DDMPC_select(bFILE, 3). Estas 
invocaciones, a su vez, son macros que se transforman hasta 
obtener el código C necesario ejecutar la operación 
"xd_select()", que sirve para indicar cuál es 
el índice a usar para un archivo.
     
En la práctica, lo que se hace es definir también el 
nombre del manejador de archivos como una macro.  Por eso en el 
archivo "person.h"
(Listado 1), aparece el 
renglón:
     
#define  dm Btrieve
para usar en el programa xd_select(dm,bFILE,3).  Para 
cambiar de manejador de archivo basta cambiar la macro 
"dm":
     
#define  dm DDMPC
Este es un ejemplo de la parametrización que puede 
obtenerse mediante el uso de macros: el manejador de archivos es 
un parámetro más cuyo valor determina el 
código C que el compilador procesa: en este ejemplo en unos 
casos se usa el de Btrieve y en otros el de DDMPC.
     
Todas las macros definidas en "genridx.h" tienen en 
común el primer parámetro, llamado 
"xd".  Este primer argumento se sustituye por el 
nombre del manejador de archivos.  En el caso de Btrieve, el valor 
de la macro "xd" debe ser "Btrieve", para el DDMPC el 
parámetro "xd" debe ser "DDMPC".
     
A continuación se define, con suficiente formalidad, cada 
uno de los verbos de acceso para usar archivos indizados. 
Más adelante se presenta un ejemplo completo que 
ayudará a entender bien cómo usar la interfaz 
"genridx.h", que está en el
Listado 3.
![[<>]](../../img/index.gif) 
 ![[\/]](../../img/bottom.gif) 
 ![[/\]](../../img/top.gif) 
 
xd_open(), xd_key_file(), 
xd_close() permiten abrir y cerrar un archivo y sus 
archivos de índice.  El verbo xd_clear() 
permite borrar todo el contenido de un archivo sin borrar el 
archivo totalmente.
     
La operación xd_reindex() sirve para 
reconstruir uno de los archivos de índice del archivo.  No 
existe una operación para reconstruirlos todos en una 
sóla operación. Es notable que Btrieve no provee 
esta operación, pues ese método de acceso a sido 
diseñado de forma que nunca pueda darse que un 
índice esté desactualizado, pues siempre que se usa 
el archivo automáticamente Btrieve también usa todos 
sus índices.
     
Contrario a lo que dicta el sentido común, al abrir un 
archivo no se obtiene acceso a todos sus índices; esta 
restricción ayuda a consiliar esta interfaz con diversos 
manejadores de archivos que administran el acceso al archivo de 
datos separadamente del acceso a los archivos de índices 
(como Dbase, CBtree, y otros).  La parte menos portable de toda la 
interfaz es precisamente la operación 
xd_open(), pues en general cada manipulador de 
archivos tiene formas diferentes de especificar los modos de 
acceso y de sincronización en ambientes multiusuario 
disponibles al programador.
     
En la práctica, si el programador cambia de manejador de 
archivos debe reprogramar todas las invocaciones a 
xd_key_file() y a xd_open(), lo que en 
general no es un grave problema, más aún si usa 
compilación condicional para evitar borrar el código 
viejo. El xd_open() es la parte de esta interfaz que 
obliga al autor a confesar que con "genridx.h" 
sólo se logra una independencia parcial del manejador de 
archivos.
     
La macro xd_error() invoca a una función que 
transforma un código de error retornado por el manejador de 
archivos en una tira de caracteres que lo describe.  
xd_file() y xd_type() retornan tiras que 
tienen el nombre del archivo en proceso y el tipo de ese archivo.  
Por ejemplo, al usar el archivo "PERSON.DAT", 
xd_file() retornará (un puntero a) la hilera 
"PERSON.DAT" y xd_type() 
retornará "person". xd_status() 
sirve para recuperar el código de retorno o de error de la 
última operación de acceso al archivo indizado.
     
Para mejorar la independencia del manejador de archivos se incluye 
dentro de "genridx.h" la definición de las 
constantes xd_OK(), xd_NO_MEM(), 
xd_CRASH(), xd_NOT_FOUND(), 
xd_DUPLICATE(), xd_LOCKED(), 
xd_BOF(), xd_EOF(), que son 
códigos de retorno muy utilizados en cualquier manejador de 
archivos.
     
xd_declare() y xd_define() permiten 
declarar y definir una variable que contiene los campos necesarios 
para implementar el acceso a un archivo indizado.  Esta variable 
es un descriptor de archivo, y su función es muy similar a 
las variables de tipo FILE* definidas en el archivo 
de encabezado estándar <stdio.h>.  El 
uso de variables descriptoras de archivos posibilita el mantener 
muchos archivos diferentes abiertos.  Cada descriptor de archivo 
sirve para accesar un archivo indizado usando cualquiera de sus 
índices.
     
Una variable declarada con xd_declare() es una 
estructura que al menos siempre contiene los siguientes campos:
INT2 key_number; /* número de la llave en uso */ int locking; /* TRUE cuando se está usando bloqueo */ INT2 status; /* último código de error */
     
INT2 es una macro definido en 
"genridx.h" la que se transforma en un tipo entero 
con signo de dos bytes.  Este truco es usado para lograr un poco 
de independencia del compilador C en uso.
     
El descriptor de archivo debe contener campos que permitan obtener 
el nombre del archivo, el nombre del tipo, y todos los que sean 
necesarios para implementar la interfaz definida en 
"genridx.h".  Por ejemplo, puede ser necesario tener 
un campo suficientemente grande para contener la llave más 
larga del archivo, o un área de control que para el 
manejador de archivos.  Como los descriptores de archivos se 
definen usando una macro es posible aislar el código para 
usar el manejador de archivos de sus particularidades.
Los siguientes son los verbos para recurperar registros del archivo indizado:
|       | xd_find() | Obtiene un registro con base a su llave | 
| xd_seek() | Obtiene un registro con base a su llave | |
| xd_match() | Búsqueda por llave parcial (solo hileras) | |
| xd_first() | Obtiene el primer registro | |
| xd_last() | Obtiene el último registro | |
| xd_next() | Obtiene el siguiente registro | |
| xd_prev() | Obtiene el registro anterior | 
     
Estos son los verbos más utilizados, pues le permiten al 
programador desplazarse en un archivo usando una llave.  La 
diferencia entre xd_find() y xd_seek() 
radica en la forma en que el programador especifica la llave.  
Antes de usar las operaciones xd_next() y 
xd_prev() es necesario establecer una posición 
en el índice por medio de alguna de las otras operaciones.
     
Para usar xd_find() el programador no tiene que 
contruir la llave de acceso, sino que deja que lo haga el 
administrador de archivos, mientras que para 
xd_seek() el programador debe construir la llave y 
pasársela al manejador de archivos.
     
Por ejemplo, al usar el archivo "PERSON.DAT", para 
obtener un registro mediante el xd_find() el 
programador debe insertar en una variable de tipo 
"person", en los campos que forman la llave, los 
valores de la llave del registro que desea recuperar (esta 
definición está en el
Listado 1). Si usara el 
xd_seek(), entonces el programador construiría 
la llave y la pasaría como el último argumento de 
xd_seek(). Estas son las dos formas de acceso que es 
usual encontrar en la mayoría de los programas.
     
Cual método es más atractivo es una cuestión 
de opinión personal, por lo que en esta interfaz se proveen 
los dos.  Para llaves simples es más útil el 
primero, y sucede lo contrario para llaves compuestas. El 
xd_seek() es útil en aquellos casos en que una 
llave no está compuesta de varios campos, por lo que el uso 
de xd_find() más bien hace que el programa sea 
menos claro.  xd_seek() debe usarse para llaves no 
segmentadas, o de lo contrario el programador estará 
obligado a escribir mucho código que se encargue de 
construir la llave.
     
xd_match() es un verbo de acceso que tiene los mismos 
parámetros que xd_find(), y que en general se 
usa sólo para llaves alfabéticas no segmentadas.  El 
xd_match() regresa el registros cuya llave tiene como 
prefijo el valor especificado como argumento de 
xd_match().  En muchas aplicaciones es importante 
identificar registros mediante llaves parciales. Es posible 
duplicar el efecto de xd_match() usando 
xd_find() junto con xd_next().
Todos los manejadores de archivos definen de alguna forma punteros directos a los registros almacenados, pues los registros deben ser almacenados de forma que sea posible recuperarlos muy rápido si se conoce su posición física en el archivo. Esta posición se llama número interno de registro (DRN: Data Record Number). En general, el formato de los DRN's es muy diferente para cada manejador de archivos, aunque sí es posible afirmar que un DRN puede representarse como un entero de cuatro bytes (o sea como un long). En el caso de Btrieve, esos números internos contienen referencias a páginas y bloques que no es fácil adivinar; lo importante es saber que los DRN son números no consecutivos.
     
Las operaciones xd_get() y xd_drn() 
permiten acceso directo a los registros.  xd_get() 
permite recuperar un registro si se sabe su localización 
(DRN), y xd_drn() permite obtener el puntero a un 
registro que acaba de ser obtenido.
     
xd_get() tiene una restricción fundamental, 
pues cuando se accesa un registro de manera directa no establece 
la posición respecto al índice que está en 
uso. Esto quiere decir que el resultado de usar 
xd_next() o xd_prev() después de 
usar xd_get() no resultará en accesar el 
registro siguiente en el orden definido por la llave de acceso.  
Esta restricción es rara, pero en la realidad muchos 
manejadores de archivos no mantienen la posición en el 
índice después de un acceso directo.
     
xd_drn() es una operación fundamental para el 
uso de archivos en modo multiusuario, pues para desbloquear un 
registro es necesario haber obtenido previamente, mediante esta 
operación, su DRN.
     
Las operaciones xd_ins(), xd_mod() y 
xd_del() permiten insertar, modificar y borrar 
registros del archivo.  Al modificar el archivo, estas operaciones 
también actualizan todos los índices que han sido 
abiertos por medio de xd_key_file().
     
xd_select() permite seleccionar la llave a utilizar 
en las operaciones de acceso, pues en general un archivo puede 
tener varias llaves diferentes.  El programador Dbase no debe 
confundir esta selección con el "select" de 
Dbase, con el que se selecciona un archivo y no su índice.  
Mediante el xd_select() el programador escoge 
cuál es la llave por la que accesará el archivo.  
Esta escogencia tiene relevancia para todas las operaciones de 
acceso [xd_find(), ..., xd_prev(), 
xd_get(), etc.], pues define cuál es el la 
llave que se usará para el acceso a varios registros.
     
La operación 
xd_select(dm, f, DRN_ACCESS) cambia el 
significado de los verbos de acceso [xd_find(), ..., 
xd_prev(), etc.].  Por ejemplo, 
xd_first() no retornará el primer registro 
respecto a un índice, sino aque que está 
físicamente almacenado de primero en el archivo.  
DRN_ACCESS es un número de archivo especial, 
que sirve para accesar en secuencia física los registros 
del archivos.
     
xd_use_locks() sirve para que las operaciones de 
acceso se hagan usando bloqueo de registros, lo que es necesario 
para accesar archivos en modo multiusuario.  En un ambiente 
monousuario únicamente puede existir un usuario trabajando 
con el archivo, pero en un escenario multiusuario varios programas 
pueden tratar de accesar el mismo registro simultáneamente, 
con lo que es necesario "enllavar" o "bloquear" un registro antes 
de usarlo. xd_locking() le permite al programador 
definir si cada operación de acceso requiere que el 
registro accesado sea también bloqueado.
Si el ambiente en que un programa corre no es multiusuario, entonces la interfaz no debe permitir que registro alguno sea bloqueado. De esta manera es posible desarrollar programas en una computadora personal, y luego trasaladarlos a un ambiente de producción multiusario.
     
xd_locking(dm,f) retorna TRUE si las 
operaciones sobre el archivo "f" se hacen usando 
bloqueo, y si no retorna FALSE.
     
El verbo xd_unlock() le sirva al programador para 
desbloquear un registro que fue bloqueado anteriormente, cuando 
fue accesado.  Esta operación requiere que el programador 
especifique el DRN del registro a liberar, por lo que el 
programador debe asegurarse de obtenerlo inmediatamente 
después de que accesa el registro, usando 
xd_drn(). No existe una forma de desbloquear todos 
los registros bloqueados de un archivo, aunque muchos manejadores 
de archivos sí proveen este servicio.  En general, en 
aquellas aplicaciones en que se accesan los registros de un 
archivo de uno en uno, lo usual es bloquear sólo un 
registro del archivo, por lo que en la práctica esta 
restricción no afecta mucho al programador de aplicaciones.
     
xd_rec_count(xd, f) retorna el número de 
registros que contiene el archivo indizado "f".  Esta 
operación es usada pocas veces, pero el no disponer de ella 
hace muy difícil la construcción de programas.
     
Las macros xd_field_kpos() y 
xd_field_size() son usadas para definir los campos de 
un archivo que componen una llave.  La primera sirve para 
determinar la posición de cada campo en el registro y la 
segunda su tamaño.
     
Por último, al escribir el archivo de encabezado de un 
archivo indizado el programador debe también declarar un 
procedimiento que sirve para inicializar un registro del archivo.  
Por ejemplo, en el caso de archivo "PERSON.DAT", en 
el archivo de encabezado "person.h" debe definirse la 
función init_person(person*), que permite 
inicializar un registro de persona.  En C++, esto puede 
complementarse definiendo el constructor de la clase 
"person".
     
A continuación se describe el programa "person.c", que 
aparece completo en el
Listado 3, el que ejercita todos 
los verbos de la interfaz uniforme definida en 
"genridx.h"
person.c" 
 
 ![[<>]](../../img/index.gif) 
 ![[\/]](../../img/bottom.gif) 
 ![[/\]](../../img/top.gif) 
 
person.c" es la de crear un archivo de 
personas con dos llaves, y accesarlo usando los verbos definidos 
en "genridx.h".  Si el archivo existe, entonces su contenido es 
borrado, antes de ser creado de nuevo.
     
El programa "person.c", en sus primeras líneas, tiene la 
siguiente directiva de compilación condicional:
     
#ifdef CREATE
que sirve para evitar que se compile todo el programa cuando 
CREATE no está definida.  Cuando la variable 
del preprocesador llamada CREATE está definida 
entonces es posible producir, mediante compilación 
condicional, el programa "person.exe".  Este programa 
incluye la definición de una estructura 
(btr_spec) que describe las calidades de un archivo 
Btrieve de personas.  El código para crear el archivo en 
"person.c" es totalmente dependiente del manejador de 
archivos Btrieve.  Como lo usual es crear cada archivo una 
sóla vez, y dado que es relativamente raro crear archivos, 
esta operación no aparece en la interfaz estándar 
definida en "genridx.h".  Además, la forma de 
la crear archivos varía mucho de un manejador de archivos a 
otro.
     
El programa "person.c" es muy simple: crea un archivo 
vacío de personas indizado por dos llaves, una de las 
cuales es compuesta. Luego lo llena de registros y por 
último lo accesa usando cada una de las dos llaves del 
archivo.  De esa manera se ejercitan todos los verbos de acceso a 
archivos definidos en "genridx.h".
     
En la práctica, un programador creará un archivo 
(.c) para cada uno de los encabezados de archivos 
indizados (.h).  En el caso del archivo "PERSON.DAT", 
la ventaja de delegar en "person.c" la 
creación del archivo inicial es que el programa de 
aplicación se simplifica, pues se escribe asumiendo que el 
archivo en que se trabajará ya existe.  Otra ventaja de 
usar archivos de implementación como 
"person.c" es que la definición del archivo 
queda escrita en C, y no es necesario utilizar utilitarios 
esotéricos para crear archivos vacíos (como el 
BUTIL.exe de Novell).  En la etapa en que se 
está probando el programa de aplicación es muy 
útil tener un programita pequeñito 
(person.exe) que al ser corrido crea de nuevo un 
archivo.
     
Al programar su aplicación, el 
programador debe incluir el archivo "person.h" en 
todos aquellos módulos que usen un archivo de personas.  
También debe compilar "person.c", pero sin 
definir la variable del preprocesador CREATE, pues es 
en este módulo en donde define la rutina 
init_person(), que se encarga de inicializar 
variables de tipo "person".  Cuando la variable de 
preprocesador CREATE no está definida, lo 
único que se compila en "person.c" es este 
procedimiento de
inicialización[2].
     
El programa "person.c" contiene la variable 
"file_description", la que es inicializada con la 
descripción Btrieve del archivo de personas.  Como 
"person.c" incluye al archivo 
"person.h", el que a su vez incluye a 
"btrieve.h", en "person.c" puede hacerse 
referencia a todos los tipos definidos en 
"btrieve.h". Entre estos tipos se encuentra 
"btr_spec", que es una estructura con la que se 
definen los datos de un archivo manejado por Btrieve.
     
La
descripción de cómo se define un archivo Btrieve 
está fuera del alcance de este
artículo[3].
Mas basta decir que si el manejador de archivos fuera otro, 
entonces la forma de describir el archivo sería totalmente 
diferente.  Lo importante es que, cuando la variable del 
preprocesador CREATE está definida, al 
compilar "person.c" se obtiene el programa 
"person.exe", que se encarga de crear un archivo de 
personas.
     
Luego de definir las cualidades del archivo Btrieve a crear, se 
define el vector de personas llamado "people", el que 
se incializa con algunos datos de prueba.  También se 
define la variable "buff" que se usará luego 
para recuperar registros.
     
Seguidamente se define la variable "bFILE", por medio 
de xd_define().  La variable "bFILE" es 
un descriptor de archivo para accesar cualquier archivo de 
registros Btrieve, por lo que en particular sirve para accesar un 
archivo de registros de tipo "person".  Como puede 
verse en el código, el primer argumento de 
xd_define() es la variable del preprocesador 
"dm" definida en "person.h" cuyo valor 
es, exactamente, "Btrieve".  Este primer argumento, 
que se repite en la invocación a todas las operaciones de 
"genridx.h", es el que define al manejador de 
archivos en uso. Luego se incluye el nombre de la variable de 
descriptor de archivo "bFILE" que se desea definir:
xd_define(dm, bFILE).
     
Después de estas definiciones está el programa 
principal, que en C siempre se llama main().  Este 
programa primero imprime la posición en donde empieza y el 
largo de la primera llave, que es el campo "number" 
del archivo "person".
     
El programa luego procede a abrir el archivo de personas llamado 
"PERSON.DAT", usando la variable de descriptor de 
archivo Btrieve "bFILE".  Los dos últimos 
argumentos de xd_open() son el modo de apertura del 
archivo y el modo de sincronización multiusuario.  En el 
caso de Btrieve tiene sentido únicamente el primer 
argumento, y el segundo es ignorado.  Sin embargo, para otros 
manejadores de archivos es necesario definir ambos argumentos.
     
El segundo argumento de xd_open() es el nombre del 
tipo al que se asociará el descriptor "bFILE".  
Es necesario que el programador haya definido en el archivo de 
encabezados "person.h" la variable 
NKEY_person, que debe ser el número de 
índices de un archivo de personas.  Aunque esta 
información no es usada en esta implementación de la 
interfaz para Btrieve, si es necesario saber esta cantidad para 
implementar comodamente la interfaz para otros manejadores de 
archivos.
     
Después de inicializar la variable "bFILE" 
mediante xd_open(), el programa borra el contenido 
anterior del archivo "PERSON.DAT" usando la 
operación xd_clear(dm, bFILE).  A 
diferencia de algunos manejadores de archivos, en esta interfaz es 
necesario que un archivo ya esté abierto para que pueda se 
borrado.
     
Como sucede con xd_clear(), todas las operaciones de 
acceso al archivo de personas necesitan como argumento a la 
variable "bFILE", la que contiene los campos 
necesarios para accesar un archivo Btrieve.
     
El código de control de errores retornados por Btrieve se 
hace siguiendo la costumbre C de examinar un código de 
retorno mediante la instrucción if(), tomando 
la acción correctiva del caso.  El programa 
"person.c" no es muy robusto, aunque siempre examina 
los códigos retornados por Btrieve.
     
El siguiente bloque de código es el que cumple la 
función principal del programa "person.exe", 
que es crear el archivo "PERSON.DAT" junto a todos 
sus índices. Al diseñar "genridx.h" se 
decidió dejar por fuera la operación para crear 
archivos indexados simplemente porque cada manejador de archivos 
lo hace de una manera completamente diferente a los demás, 
y esta operación se realiza sólo una vez para cada 
archivo.
     
El código que sigue incluye una invocación a 
xd_key_file() para cada una de las llaves del 
archivo.  En el caso de Btrieve no es necesario abrir archivos de 
índices, pero otros manejadores de archivo sí lo 
requieren.
     
Luego está un bloque de código que inserta en el 
archivo "PERSON.DAT" todos los registros definidos en 
el arreglo de personas llamado "people".  Lo primero que se hace 
es poner la bandera de bloqueo (locking) para el archivo 
administrado por medio de "bFILE" en 
TRUE, para que todos los accesos a registros del 
archivo dejen el registro bloqueado.  Luego, usando la 
operación xd_drn(), se obtiene el
DRN de cada uno de los registros 
añadidos al archivo. Como en general los DRN's no son 
números consecutivos, es necesario almacenarlos en el 
arreglo drn[] para luego utilizarlos para hacer 
acceso directo a los registros por medio de xd_get().  
También es necesario usar a la operación 
xd_unlock(), que desbloquea el registro recién 
insertado.
     
Después de crear y llenar de registros el archivo, se 
procede a accesarlo de varias formas.  La primera es la forma 
directa, para la que se usa el verbo xd_get() cuyos 
argumentos son un área en donde dejar el registro leido del 
archivo, y un DRN, además de los otros dos argumentos 
comunes a todos los verbos de acceso.
     
Luego el programa accesa el archivo utilizando las dos llaves de 
acceso, llamadas KEY_name y KEY_number, 
nombres que deben haber sido definidos en el archivo de encabezado 
"person.h".  El verbo xd_select() sirve 
para indicar cuál es el índice que se usará 
para recorrer el archivo.  Cada uno de los índices es usado 
para recorrer el archivo hacia adelante y hacia atrás.
     
También en el programa se ejercitan las funciones de acceso 
por llave xd_seek() y xd_find(). Para 
usar xd_seek() es necesario incluir como 
último argumento la llave de acceso, mientras que para usar 
xd_find() lo que se hace es copiar en una variable de 
tipo "person" los valores de los campos que forman la 
llave.
     
La última operación del programa es cerrar el 
archivo usando el verbo xd_close(dm, bFILE). 
Como en el descriptor de archivos "bFILE" se almacena 
el código de retorno de cada una de las operaciones de 
archivos, es perfectamente válido usar el verbo 
xd_status() para averiguar el resultado de la 
última operación de acceso.
![[<>]](../../img/index.gif) 
 ![[\/]](../../img/bottom.gif) 
 ![[/\]](../../img/top.gif) 
 
genridx.h", el programa 
"person.c" del
Listado 3 sería muy 
diferente.  Cada acceso a un archivo Btrieve debe hacerse usando 
un protocolo preestablecido por medio de la función 
BTRV(), suplida en el paquete Btrieve, pues para usar 
Btrieve el programador debe definir, para cada archivo, tres tipos 
de objetos:
BTRV().
     
Cuando se adquiere el programa Btrieve, Novell distribuye con el 
Btrieve varios archivos llamados ????BTRV.c, en donde 
las primeras cuatro letras dependen del compilador que el 
programador usa. Para el Turbo C, el archivo de interfaz con 
Btrieve se llama "turcBTRV.c", y en él se 
incluye el procedimiento BTRV(), que es la llave para 
invocar al Btrieve tanto en ambientes de redes como en DOS.
     
En el caso específico de "turcBTRV.c", el 
autor es testigo de lo difícil que es llegar a entender 
cómo usarlo. Precisamente la poca calidad del código 
contenido en este archivo indujo al autor a escribir la interfaz 
genérica "genridx.h".
| 
int BTRV(            /* Btrieve interface function */
    int      OP,           /* Código de operación  */
    char     POS_BLOCK[],  /* Bloque de posición   */
    char     DATA_BUF[],   /* Memoria para E/S     */
    int    * DATA_LEN,     /* Largo de DATA_BUF    */
    char     KEY_BUF[],    /* Llave de acceso      */
    int      KEY_NUM       /* Número de índice     */
);
 | 
BTRV()     
Cuando un programador necesita accesar un archivo, lo que hace es 
invocar la función BTRV() con los 
parámetros adecuados.  Estos parámetros 
varían de acuerdo a la operación a realizar, pero 
cualquier operación sobre los archivos indizados debe 
hacerse por intermedio de BTRV().  Otros sistemas de 
archivo tienen una o varias funciones similares a 
BTRV(), aunque en general todos proveen las 
operaciones de "genridx.h".  El encabezado de la 
función BTRV() se muestra en la
Figura 4. El primero es el código 
de operación.  El siguiente es un bloque de posición 
en el que Btrieve guarda algunos valores para procesar el archivo, 
como el DRN de los registros que anteceden y siguen al registro 
actual.  El siguiente argumento debe ser un puntero al área 
de memoria en donde está el registro accesado.  Luego sigue 
otro puntero a una variable de dos bytes de largo que contiene el 
tamaño del área de datos.  El siguiente argumento es 
un puntero al área en que el programador ha construido la 
llave de acceso.
     
Las llaves de un archivo Btrieve se numeran consecutivamente de 
cero en adelante.  Cuando el programador necesita accesar el 
archivo por medio de una llave, entonces debe incluir el 
número de de la llave como sexto argumento al invocar a 
BTRV().
El manual de Btrieve describe, en más de cuatrocientas páginas, el detalle de las peculiaridades de las casi cien diferentes operaciones que Btrieve ofrece. Para abrir y luego cerrar un archivo Btrieve, el programador debe escribir el siguiente código:
Nótese lo difícil que es adivinar el significado de cada argumento deBTRV (0, position_block, NULL, 0, "PERSON.DAT", 0); BTRV (1, position_block, NULL, 0, NULL, 0);
BTRV(), aún si se cuenta con 
el manual, y lo estéticamente negativo que es el 
código resultante.  Esto contrasta enormemente con el 
método expuesto en este artículo:
#include "person.h" ret = xd_open(dm, person, bFILE, "PERSON.DAT", MD_ACELERATED, 0); ret = xd_close(dm, bFILE);
     
A todas luces la segunda forma es más clara, elegante y 
completa.  En la siguiente sección se discuten en detalle 
las ventajas principales de la interfaz "genridx.h".
genridx.h" 
 
 ![[<>]](../../img/index.gif) 
 ![[\/]](../../img/bottom.gif) 
 ![[/\]](../../img/top.gif) 
 
genridx.h" 
sobre el crudo método BTRV() es el poder usar 
nombres significativos para las operaciones y para sus argumentos.  
En el método BTRV() es muy difícil 
recordar, para un ser humano normal, que el código 
"0" es el que Btrieve usa para abrir un archivo, y 
que el "1" es el código para cerrarlo.
     
Lo segundo que puede notarse es que para accesar un archivo en 
Btrieve, como es el caso con otros manejadores de archivos, es 
necesario definir una serie de variables de apoyo que sirven para 
mantener el estado del archivo Btrieve. En la
Figura 4 se menciona a la variable 
POS_BLOCK, pero como se verá después es 
necesario definir muchas otras variable de apoyo.  En una 
aplicación de mediano tamaño la proliferación 
de este tipo de variables complica mucho el programa, y obliga al 
programador a invertir grandes esfuerzos para controlar a Btrieve.
     
Cuando el método para definir todos los objetos necesarios 
para usar el manejador de archivos no es automático el 
resultado puede ser un programa incorrecto debido no a un defecto 
de diseño, sino a uno de codificación.  El autor en 
una ocasión perdió más de seis horas antes de 
darse cuenta de que no había definido la variable 
POS_BLOCK del tamaño adecuado, por lo que 
BTRV() retornaba resultados erróneos.  Por eso 
es importante utilizar herramientas, como 
"genridx.h", que permitan simplificar el acceso a los 
archivos, pues de esa manera se evitan errores tontos y costosos.
     
Para el lector que no tiene conocimiento alguno de cómo 
funciona el sistema de archivos Btrieve debe ser muy 
difícil saber (adivinar) cuáles son los 
parámetros de la función BTRV() para 
invocar la operación OPEN(). Para el que tiene 
el manual a la par de este escrito puede ser obvio, pero 
sólo si ya ha usado mucho el Btrieve y además tiene 
la paciencia de buscar en las páginas x-PP o 
y-QQ.  Pero para la mayoría de los demás, 
es simplemente inconcevible el uso de un sistema de archivos que 
no es "amigable", y que requiere de una memoria de elefante y la 
delicadeza de un cirujano para utilizarse.
     
Es fácil dar más argumentos para justificar la 
necesidad de las macros definidas en "btrieve.h" y 
"genridx.h", más hacerlo es gastar 
"pólvora en zopilotes". Es mejor pasar a discutir la forma 
en que están implementados los archivos 
"btrieve.h" y "btrieve.c", que forman la 
implementación Btrieve de la interfaz 
"genridx.h".
     
El código en los archivos "btrieve.h" y 
"btrieve.c" permite transformar una invocación 
elegante, como las del programa "person.c", en el 
poco amigable llamado a BTRV() que Btrieve requiere.  
Mucho del código de estos archivos es definición de 
macros. La depuración de este código es 
difícil pues en general es difícil depurar programas 
que usan macros complejas como las de "btrieve.h".  
Como para crear la interfaz estándar ese trabajo debe 
hacerse una sóla vez, el resultado bien vale el sudor que 
cuesta.
     
Definitivamente la programación Btrieve por medio de 
"genridx.h" es mucho más sencilla que usar 
BTRV() solo.
btrieve.h" 
 
 ![[<>]](../../img/index.gif) 
 ![[\/]](../../img/bottom.gif) 
 ![[/\]](../../img/top.gif) 
 
btrieve.h" nació como una concha alrededor de 
"turcBTRV.c". Paulatinamente se transformó en 
"genridx.h" y por último pasó a 
partirse en dos archivos: "btrieve.h" y 
"genridx.h".
     
Al incluir en un programa el archivo de encabezado 
"btrieve.h" el programador define todas las macros 
que se invocan por medio de "genridx.h", como 
Btrieve_define(), Btrieve_select(), etc. 
También declara diversas funciones de apoyo, y define 
muchas de las constantes relevantes al uso de archivos Btrieve.
     
La mayor parte de las contantes definidas en 
"btrieve.h" son códigos de estado o 
códigos de error.  Por ejemplo, la variable 
KEY_NOT_FOUND (y por ende 
Btrieve_KEY_NOT_FOUND) está definida como el 
valor "4", que es el código que 
BTRV() retorna cuando busca una llave en un archivo y 
no la encuentra.
Para usar Btrieve es necesario definir tres tipos de constantes: códigos de operación, códigos para definir los campos y llaves de los archivos, y códigos de error.
     
Los códigos de operación definidos en 
"btrieve.h" comienzan todos con el prefijo 
"BT_", como BT_OPEN que es el 
código de operación que BTRV() debe 
recibir como primer argumento para abrir un archivo.  Los 
códigos para definir archivos empiezan con 
"XT_", como en XT_MONEY si son 
códigos extendidos (que es un concepto definido en el 
manual de uso del paquete) o tienen un nombre significativo en 
mayúsculas, como BINARY. Por último, 
los nombres de los códigos de error son muy largos, y en la 
mayoría de los casos son muy significativos 
(INVALID_FILE_NAME).
     
La más importante de las funciones declaradas en 
"btrieve.h" es Btrieve_generic(), la que 
se encarga de llamar a BTRV() con la salvedad de que 
usa una estructura de tipo Btrieve_access, que es el 
tipo de una variable descriptora de archivos.  En efecto, la macro 
xd_declare(Btrieve, bFILE) se traduce en definir 
e inicializar "bFILE" como una variable de tipo 
Btrieve_access en la que se guardan los parámetros de 
acceso necesarios para usar Btrieve en un archivo.  La 
función Btrieve_generic() deja en esta 
estructura el código de error retornado por 
BTRV().
     
La única ineficiencia en tiempo de ejecución que el 
uso de estas macros representa es el llamado adicional a 
Btrieve_generic(), que en la mayoría de los 
casos en despreciable.  Lo mismo no puede decirse, sin embargo, 
del tiempo de compilación, pues después de todo el 
tamaño del archivo "btrieve.h" es un poco 
grande, y la mayoría de los compiladores se gastan sus 
momentos para degullirlo.
| 
#define dm Btrieve /* Parametriza el manejador de archivos */
/* invocaciones equivalentes de la macro */
xd_find(Btrieve, bFILE, d)
xd_find(dm,      bFILE, d)
/* resultado de expander la macro */
(
    Btrieve_build_key(
        (bFILE).stat,
        (bFILE).key_number,
        (bFILE).key_buffer,
        (char*)d
    ),
    Btrieve_generic(BT_GET_EQUAL, &(bFILE), d)
)
 | 
xd_find(Btrieve, bFILE, d)
     
La Figura 5 es el resultado de expandir 
la macro xd_find(Btrieve, bFILE, d), que 
resulta en una invocación a Btrieve_generic(). 
El nombre del manejador de archivos en uso, que es 
"Btrieve" en este caso, se usa en la 
implementación de todas las operaciones de acceso: 
Btrieve_generic(), Btrieve_access, etc.
     
"btrieve.h" fue implementado incialmente para el 
compilador Turbo C++ v1.0, por lo que incluye 
código para que la interfaz pueda ser usada en C o 
en C++, y en cualquiera de los modelos de memoria que soporta 
ese compilador.  Esta generalidad obliga a usar compilación 
condicional para el manejo de cada modelo de memoria lo que a 
veces complica un poco las cosas.
btrieve.c" 
 
 ![[<>]](../../img/index.gif) 
 ![[\/]](../../img/bottom.gif) 
 ![[/\]](../../img/top.gif) 
 
btrieve.c" se encuentra el código que 
implementa cada una de las rutinas definidas en 
"btrieve.h".  Además, también 
está la tabla de mensajes de error usada por
Btrieve_error()[4].
     
Tal vez la parte más oscura en "btrieve.c" sea 
el código que está alrededor de la directiva 
#include "turcbtrv.c", archivo que contiene el 
código para invocar a Btrieve desde un programa escrito en 
Turbo C.  El archivo "turcbtrv.c" está 
muy mal programado, pues asume que la versión del 
compilador a usar es de la etapa paleolítica de la 
computación. Para lograr compilar este código en las 
versiones v2.0 y posteriores no queda más que 
engañar un poco al compilador, pues de lo contrario se 
obtienen una veintena de errores de compilación.  Por eso 
el código que se encuentra alrededor del 
#include "turcbtrv.c" declara algunas funciones 
del archivo de encabezado <dos.h> como 
funciones que reciben argumentos de tipo "void*".
     
Al probar la interfaz con Turbo C++ versión 1.0, 
y cuando se usó el compilador de C++ en lugar del 
de C, en muchas ocasiones el ligador de eslabonamiento 
retornó un error de FIXUP OVERFLOW que fue 
imposible eliminar.  Pareciera que este problema sólo 
existe con la versión 1.0, pues en las versiones de 
Turbo C++ posteriores a esa este problema está 
resuelto: la implementación actual de 
"btrieve.c" no presenta estos problemas.
     
Cada archivo Btrieve se accesa usando un descriptor de archivo, de 
tipo "Btrieve_access".  Los campos de este archivo 
contienen punteros a varias áreas de memoria que se asignan 
dinámicamente en la operación 
Btrieve_open_file(), y que se liberan con 
Btrieve_close_file(). Unicamente el campo 
"type_name" no se asigna en la memoria 
dinámica, pues la macro Btrieve_open() genera 
la tira con el nombre del tipo del archivo que se ha abierto.  La 
rutina Btrieve_clear_file() ha debibo programarse 
usando una lógica especial, pues Btrieve no incluye una 
operación que permita borrar todos los registros de un 
archivo sin borrar el archivo completo.
     
Las rutina Btrieve_longest_key() determina el 
tamaño más grande posible para cada una de las 
llaves del archivo. Para esto usa la información sobre la 
posición de cada llave en el archivo que está 
almacenada en un área de memoria dinámica a la que 
apunta el campo "stat". El valor retornado por esta 
función sirve para asignar una área de memoria a la 
que apunta el campo "key_buffer", en donde se 
almacena la llave de acceso al archivo.  La función 
build_user_key() se encarga de construir, en el 
área denotada "key_buffer", la llave del 
usuario con base a los datos almacenados en un registro del 
usuario.  Esta función es necesaria para implementar el 
Btrieve_seek().
     
Al programar esta interfaz para otro manejador de archivos 
será necesario que el programador supla, de alguna manera, 
una estructura de datos que describa cómo está 
formada cada llave. Esta es la estructura que una función 
similar a build_user_key() necesita para contruir la 
llave en xd_seek().  En el caso de Btrieve, la 
estructura retornada por la operación STAT() 
principalmente sirve para describir las llaves del archivo.
     
La otra importante función es 
Btrieve_generic(), la que se encarga de llamar a 
BTRV(), y guardar el código de retorno. 
Además, en los casos en que debe usarse bloqueo de 
registros, esta función se encarga de ajustar el 
código de operación para BTRV().
     
La implementación "btrieve.c" es bastante 
simple, pues la mayor parte del que se presenta en este archivo 
descansa mucho en las macros definidas en 
"btrieve.h".  Esto obedece un poco a que la 
función BTRV() es muy general.  Para otros 
manejadores de archivos el archivo de implementación 
análogo al "btrieve.c" será mucho 
más complicado.
![[<>]](../../img/index.gif) 
 ![[\/]](../../img/bottom.gif) 
 ![[/\]](../../img/top.gif) 
 
#define que nombre al manejador de 
archivos.
     
Como ejemplo, supóngase que es necesario usar el archivo 
descrito en "person.h" y en "account.h".  
Si el primero es un archivo Btrieve entonces la siguiente 
línea de código debe aparecer en 
"person.h":
     
     
     #define dm1 Btrieve
     
Como el segundo es un archivo DDMPC, entonces en el archivo de 
encabezado "account.h" debe aparecer la línea:
     
     
     #define dm2 DDMPC
     
Luego basta usar las operaciones de accesso definidas en 
"genridx.h" en un caso con la variable de 
compilación "dm1" y en el otro con 
"dm2", según corresponda.
genridx.h" 
 
 ![[<>]](../../img/index.gif) 
 ![[\/]](../../img/bottom.gif) 
 ![[/\]](../../img/top.gif) 
 
genridx.h" 
relevante a Btrieve.  Sin embargo, el lector puede 
fácilmente hacer otras implementaciones.  Para esto es 
conveniente seguir los siguientes pasos:
genridx.h".genridx.h".btrieve.c".
![[<>]](../../img/index.gif) 
 ![[\/]](../../img/bottom.gif) 
 ![[/\]](../../img/top.gif) 
 
     
Los programas C que usan la interfaz definida en 
"genridx.h" pueden ser cambiados, con relativa 
facilidad, para que usen un nuevo manejador de archivos.  Por 
ejemplo, es posible escribir un programa usando Btrieve, y luego 
cambiarlo para que la versión definitiva use 
Code Base.  Esta interfaz depende mucho del preprocesador del 
lenguaje C, el que se usa para parametrizar el acceso a 
archivos indizados.  Más aún, 
"genridx.h" es una herramienta que permite la 
coexistencia de más de una manejador de archivos en el 
mismo programa.
     
Aunque en general no es muy fácil implementar la interfaz 
para un manejador de archivos específico, este trabajo debe 
hacerse una sóla vez.  La ventaja de usar 
"genridx.h" es que se libera a muchos programadores 
del aprendizadje de los detalles particulares de diversos 
manejadores de archivos, lo que a la larga abarata el costo de 
producción de sistemas.
![[<>]](../../img/index.gif) 
 ![[\/]](../../img/bottom.gif) 
 ![[/\]](../../img/top.gif) 
 
btrieve.h", "btrieve.c" y 
"genridx.h".
![[<>]](../../img/index.gif) 
 ![[\/]](../../img/bottom.gif) 
 ![[/\]](../../img/top.gif) 
 
| [*] | Esta investigación se realizó dentro del 
       proyecto de investigación 329-89-019 "Estudios en la 
       tecnología de programación por objetos y 
       C++", inscrito ante la Vicerrectoría de 
       Investigación de la Universidad de Costa Rica.  La 
       Escuela de Ciencias de la Computación e 
       Informática también ha portado fondos para 
       realizar este trabajo. | 
| [1] | En opinión del autor, la rebeldía al uso de 
       encabezados para describir la estructura de archivos de 
       datos debe combatirse con una reducción del sueldo 
       del programador, proporcional a la intensidad de la 
       rebelión. | 
| [2] | Para evitar usar el archivo person.cse puede 
       crear una macro en el encabezadoperson.h(con "h"), para inicializar las varibles 
       de tipoperson; esto abulta el código 
       generado. En esto cada programador escoge su propio rumbo. | 
| [3] | En conjunto, las referencias
       [4] y [9]
       representan una manera efectiva y sencilla de dominar los 
       detalles del uso de Btrieve. | 
| [4] | Por razones de espacio el listado completo del archivo 
       " btrieve.c" no se incluye en el texto del 
       artículo, pero está disponible en Internet 
       en:
      http://www.uaca.ac.cr/actas/1994nov/genridx.zip | 
![[<>]](../../img/index.gif) 
 ![[\/]](../../img/bottom.gif) 
 ![[/\]](../../img/top.gif) 
 
| [1] | Borland International:
  Borland C++ v3.1 Programmer's Guide,
  Borland International, California (U.S.A.), 1992. | 
| [2] | Brown, J. Randolph:
  Cross-platform Database Development, Strategies
  for FoxPro Developers,
  Dr. Dobb's Journal,
  No.215,
  Junio 1994. | 
| [3] | Butterfield, Timothy:
  File Conversion using C++
   Templates,
  Dr. Dobb's Journal,
  No.198,
  Marzo 1994. | 
| [4] | Chang,  Chao  Lin:
  C++ class library for Btrieve
  file manipulation,
  disponible en Internet en 
               ftp:// oak.oakland.edu /SimTel/msdos/cpluspls/ bfast11.zip,
  1992. | 
| [5] | Dowgiallo, Edward:
  Database Interoperability and
  Application Transportability,
  Dr. Dobb's Journal,
  No.208,
  Diciembre 1993. | 
| [6] | Duntemann, Jeff:
  Structured Programming: Action at distance,
  Dr. Dobb's Journal,
  No.198,
  Marzo 1993. | 
| [7] | Murph, Thomas:
  Hiding ISAM Function Libraries with OOP,
  The C Users  Journal,
  Vol.11, No.1,
  Enero 1993. | 
| [8] | Pruett, Mark:
  Mixing C with SQL,
  The C Users  Journal,
  Vol.9, No.6,
  Junio 1991. | 
| [9] | Reilly, Douglas:
  Inside Btrieve Files,
  Dr. Dobb's Journal,
  No.198,
  Marzo 1993. | 
| [10] | Reilly, Douglas:
  Accessing NetWare SQL Files
  without NetWare SQL,
  Dr. Dobb's Journal,
  No.204,
  Septiembre 1993. | 
| [11] | Ware, John:
  Mixing C with Informix,
  The C Users  Journal,
  Vol.9, No.6,
  Junio 1991. | 
| [12] | Winchell, Jeff:
  The Limits of PC Databases,
  DBMS, Vol.7, No.7,
  Junio 1994. | 
person.h 
 
 ![[<>]](../../img/index.gif) 
 ![[\/]](../../img/bottom.gif) 
 ![[/\]](../../img/top.gif) 
 
/* @(#)    person.h       Copyright 1994 Adolfo Di Mare      */
/*************************************************************/
/*   Header file to acces the person indexed data file       */
/*************************************************************/
#ifndef _person_h
#define _person_h   /* avoid multiple inclusion   */
/* Parametrize the access method */
#define dm Btrieve  /* use Btrieve record manager */
/* "dm", and not "Btrieve", will be the first argument in
   every invocation to the operations defined in the 
   "genridx.h" interface:
   - Changing record managers can be accomplished by 
     changing the value for "dm", and recompiling.
*/
#include   "btrieve.h"
/* constanst to avoid using "magic numbers" */
#define SZ_number   ( 8+1)  /* force NON alignment */
#define SZ_name     (15+1)
#define SZ_birth    ( 8+1)
/* constants to define each file index */
#define KEY_number    0  /* first key  */
#define KEY_name      1  /* second key */
#define NKEY_person   2  /* number of indexes for this file */
typedef struct {                   
    char  number[SZ_number];          /* id number     */
    float amount;                     /* amount owed   */
    char  name[SZ_name];              /* person's name */
    char  sex;                        /* only safe     */
    char  birth[SZ_birth];            /* date          */
} person;
void init_person(person *);
#endif /* _person_h */
genridx.h 
 
 ![[<>]](../../img/index.gif) 
 ![[\/]](../../img/bottom.gif) 
 ![[/\]](../../img/top.gif) 
 
/* @(#)   genridx.h      Copyright 1990 Adolfo Di Mare       */
/*                                                           */
/*        Macros para parametrizar el metodo de acceso       */
#ifndef _genrixd_h
#define _genrixd_h
#include <stddef.h> /* offsetof() && NULL */
/* Types for different return codes */
typedef  unsigned long drn_t;
typedef  short         xdret_t;
#ifndef fsizeof
#define fsizeof(type, field) sizeof( ((type*)0)->field )
#endif
#define _xd_P2(z,y) _xd_Paste2_i(z, y) /* double indirection */
#define _xd_Paste2_i(z,y)  z##y      /* as suggested by ANSI */
#define field_kpos(type, field) offsetof(type, field)
#define field_size(type, field) fsizeof(type, field)
#define xd_max(a,b) ((a) > (b) ? (a) : (b))
#define xd_min(a,b) ((a) < (b) ? (a) : (b))
/* macros used to define the position and size */
/* of a record field in an indexed file        */
#define xd_field_kpos(xd, t, f)     _xd_P2(xd, _field_kpos)(t, f)
#define xd_field_size(xd, t, f)     _xd_P2(xd, _field_size)(t, f)
/* Most usual return codes */
#define xd_OK(xd)                   _xd_P2(xd, _OK)
#define xd_NO_MEM(xd)               _xd_P2(xd, _NO_MEM)
#define xd_CRASH(xd)                _xd_P2(xd, _CRASH)
#define xd_NOT_FOUND(xd)            _xd_P2(xd, _NOT_FOUND)
#define xd_DUPLICATE(xd)            _xd_P2(xd, _DUPLICATE)
#define xd_LOCKED(xd)               _xd_P2(xd, _LOCKED)
#define xd_BOF(xd)                  _xd_P2(xd, _BOF)
#define xd_EOF(xd)                  _xd_P2(xd, _EOF)
#define xd_NOT_OPEN(xd)             _xd_P2(xd, _NOT_OPEN)
#define xd_DATA_LENGTH(xd)          _xd_P2(xd, _DATA_LENGTH)
#define xd_MAX_FILE_NAME_SZ(xd)     _xd_P2(xd, _MAX_FILE_NAME_SZ)
#define xd_error(   xd, err)        _xd_P2(xd, _error)  (err)
#define xd_file(    xd, f)          _xd_P2(xd, _file)   (f)
#define xd_type(    xd, f)          _xd_P2(xd, _type)   (f)
#define xd_status(  xd, f)          _xd_P2(xd, _status) (f)
/* These macros are used to define global variables where */
/* all the status information for file "t" is stored      */
#define xd_declare( xd)             _xd_P2(xd, _declare)
#define xd_define(  xd, f)          _xd_P2(xd, _define)  (f)
/* File access verbs */
#define xd_bind(    xd, f, ptr)     _xd_P2(xd, _bind)  (  f, ptr)
#define xd_bound(   xd, f)          _xd_P2(xd, _bound) (  f)
#define xd_is_open( xd, f)          _xd_P2(xd, _is_open) (f)
#define xd_open(    xd, t,f,n, m,s) _xd_P2(xd, \
          _open)      ( t,f,n, m,s)
#define xd_key_file(xd, k,f,n, m,s) _xd_P2(xd, \
          _key_file)  ( k,f,n, m,s)
#define xd_reindex( xd, f,k)        _xd_P2(xd, _reindex) (f,k)
#define xd_close(   xd, f)          _xd_P2(xd, _close) (f)
#define xd_clear(   xd, f)          _xd_P2(xd, _clear) (f)
#define xd_find(    xd, f)          _xd_P2(xd, _find)  (f)
#define xd_seek(    xd, f, key)     _xd_P2(xd, _seek)  (f, key)
#define xd_search(  xd, f)          _xd_P2(xd, _search)(f)
#define xd_first(   xd, f)          _xd_P2(xd, _first) (f)
#define xd_last(    xd, f)          _xd_P2(xd, _last)  (f)
#define xd_next(    xd, f)          _xd_P2(xd, _next)  (f)
#define xd_prev(    xd, f)          _xd_P2(xd, _prev)  (f)
#define xd_get(     xd, f, drn)     _xd_P2(xd, _get)   (f, drn)
#define xd_drn(     xd, f, drn)     _xd_P2(xd, _drn)   (f, drn)
#define xd_ins(     xd, f)          _xd_P2(xd, _ins)   (f)
#define xd_mod(     xd, f)          _xd_P2(xd, _mod)   (f)
#define xd_del(     xd, f)          _xd_P2(xd, _del)   (f)
/* Index && direct access selection */
#define xd_select(   xd, f, key)    _xd_P2(xd, _select) (f, key)
/* Force drn access: xd_select(xd, f, DRN_ACCESS) */
#define DRN_ACCESS (-1)
/* Locking directives */
#define xd_use_locks(xd, f, bool) _xd_P2(xd, _use_locks)(f, bool)
#define xd_locking(  xd, f)       _xd_P2(xd, _locking)  (f)
#define xd_unlock(   xd, f, drn)  _xd_P2(xd, _unlock)   (f, drn)
/* file statistics */
#define xd_rec_count(xd, f)       _xd_P2(xd, _rec_count) (f)
/* Macro to conditionally execute a series of file operations */
#define xd_try(xd, f, operation)   \
   (f).status =                    \
       (  xd_OK(xd) == (f).status  \
             ? (operation)         \
             : ((f).status)        \
       )
#ifndef TRUE
    #define TRUE   1
    #define FALSE  0
#endif
#if defined(__TURBOC__)
    #if (sizeof(unsigned long) < 4)
        #error drn_t is not long enough
    #endif
#endif
#endif  /* _genridx_h */
person.c 
 
 ![[<>]](../../img/index.gif) 
 ![[\/]](../../img/bottom.gif) 
 ![[/\]](../../img/top.gif) 
 
/* @(#)    person.c           Copyright 1994 Adolfo Di Mare    */
/***************************************************************/
/* person.c: Maintenance of the "person@ Btrieve record file   */
/*       Creates, inserts, modifies, reads and deletes records */
/***************************************************************/
#include "person.h"
#include <string.h>
void init_person(person *p) {
/* Construct "person" */
    memset(p, 0, sizeof(person));
};
#define CREATE
#ifdef  CREATE
#include <stdio.h>
#include <stdlib.h>
extern unsigned _stklen = 32000u; /* Required by Borland C++ */
/* Btrieve file specifications for the "person" file */
struct {
    btr_filespec   f;
    btr_keyspec    r[3];
} file_description =
{
    {  /* btr_filespec: file definition */
       sizeof(person), /* record length                      */
       512,            /* page size                          */
       NKEY_person,    /* number of indexes                  */
       0,              /* total active (non deleted) records */
       0,              /* bit flag vector: file attributes   */
       {0,0},          /* reserved                           */
       10,        /* # of pages to pre-allocate for the file */
    },
    {  /* btr_keyspec: key && key-segment definition */
       {      /* KEY_number */
       xd_field_kpos(dm,person,number), /* start pos of key arg */
       xd_field_size(dm,person,number), /* key segment length   */
       0,           /* bit flag vector: key attributes   */
       0,           /* unique key count (no dups, nulls) */
       BT_ZSTRING,  /* bit flags vector: data type       */
       0,           /* value for null key (0 or space)   */
       {0,0,0,0},   /* reserved                          */
       },
       {      /* KEY_name */
       xd_field_kpos(dm, person, name), /* start pos of key arg */
       xd_field_size(dm, person, name), /* key segment length   */
       BT_SEGMENTED,/* bit flag vector: key attributes   */
       0,           /* unique key count (no dups, nulls) */
       BT_ZSTRING,  /* bit flags vector: data type       */
       0,           /* value for null key (0 or space)   */
       {0,0,0,0},   /* reserved                          */
       },
       {
       xd_field_kpos(dm,person,number), /* start pos of key arg */
       xd_field_size(dm,person,number), /* key segment length   */
       0,           /* bit flag vector: key attributes   */
       0,           /* unique key count (no dups, nulls) */
       BT_ZSTRING,  /* bit flags vector: data type       */
       0,           /* value for null key (0 or space)   */
       {0,0,0,0},   /* reserved                          */
       },
    },
};  /* <====  global variable */
#define DIM(vec) (sizeof(vec)/sizeof(*vec))
person people [] = {
    { "11111111",   1256.00,   "David",    'M',  "270762", },
    { "22222222",    356.00,   "Ricardo",  'M',  "310189", },
    { "33333333",    100.00,   "Maria",    'F',  "011273", },
    { "44444444",   1103.25,   "Lorena",   'F',  "230475", },
    { "55555555",   4389.50,   "Joaquin",  'M',  "010160", },
    { "12345678",     89.50,   "Max",      'M',  "010190", },
    { "99999999",    122.35,   "Joaquin",  'M',  "030387", },
};
person buff;
void print_person (const person *);
int  check_error  (xd_declare(dm) *pfile);
void main(void) {
    int i;
    drn_t drn[DIM(people)];
    xd_define(dm, bFILE);
    unsigned int key_pos = xd_field_kpos(dm, person, number);
    unsigned int key_len = xd_field_size(dm, person, number);
    printf("\nKey_pos = %d Key_len = %d\n", key_pos, key_len);
    #if dm == Btrieve
        Btrieve_reset();
    #endif
    /* if the file exists, it overwrites it */
    bFILE.status = xd_OK(dm);
    xd_try(dm, bFILE,
        xd_open(dm, person, bFILE,
                            "PERSON.DAT", BT_ACELERATED, +++));
    /* xd_try(dm, bFILE, op) executes "op"  only if the "status" 
       field withing "bFILE" has value equal to xd_OK(dm). 
       - This is a neat trick to delay evaluation of the
         error return code.
    */
       
    if (xd_OK(dm) == bFILE.status) {
        xd_try(dm, bFILE, xd_reindex(dm, bFILE, KEY_person));
        xd_try(dm, bFILE, xd_clear(dm, bFILE));
        xd_try(dm, bFILE, xd_close(dm, bFILE));
        check_error(&bFILE);
    }
    /* create the indexed file */
    i = sizeof(file_description);
    bFILE.status =
        BTRV(BT_CREATE,
            (char*)   bFILE.pos_block,
            (char*) & file_description,
            (int*)  & i,
            (char*)   "PERSON.DAT",
            0);
    check_error(&bFILE);
    /* open the file and its indexes */
    bFILE.status = xd_OK(dm);
    xd_try(dm, bFILE,
        xd_open(dm, person, bFILE,
                        "PERSON.DAT", BT_ACELERATED, +++));
    xd_try(dm, bFILE,
        xd_key_file(dm, KEY_number, bFILE,
                        "PERSON.DAT", BT_ACELERATED, +++));
    xd_try(dm, bFILE,
        xd_key_file(dm, KEY_name,   bFILE,
                        "PERSON.DAT", BT_ACELERATED, +++));
    check_error(&bFILE);    /* check cascade of operations */
    printf ("\n\n Loading the file");
    xd_use_locks(dm, bFILE, TRUE);
    for (i=0; xd_OK(dm) == bFILE.status && i<DIM(people); i++) {
        print_person(&people[i]);
        bFILE.status = xd_OK(dm);
        xd_try(dm, bFILE, xd_bind(dm, bFILE, & people[i]));
        xd_try(dm, bFILE, xd_ins(dm,    bFILE));
        xd_try(dm, bFILE, xd_drn(dm,    bFILE, & drn[i]));
        xd_try(dm, bFILE, xd_unlock(dm, bFILE,   drn[i]));
        check_error(&bFILE);
    }
    bFILE.status = xd_OK(dm);  /* redundant, but OK */
    xd_use_locks(dm, bFILE, FALSE);
    xd_try(dm, bFILE, xd_bind(dm, bFILE, &buff));
    check_error(&bFILE);
    printf ("\n\n File accessed direct");
    xd_select(dm, bFILE, DRN_ACCESS);
    i = 0;
    bFILE.status = xd_get(dm, bFILE, drn[i]);
    while ( xd_OK(dm) == bFILE.status && i < DIM(people) ) {
        print_person(&buff);
        i++;
        bFILE.status = xd_get(dm, bFILE, drn[i]);
    }
    printf ("\n\n File ordered by name (FORWARD)");
    xd_select(dm, bFILE, KEY_name);
    bFILE.status = xd_first(dm, bFILE);
    while ( xd_OK(dm) == bFILE.status ) {
        print_person(&buff);
        bFILE.status = xd_next(dm, bFILE);
    }
    printf ("\n\n File ordered by name (BACKWARDS)");
    xd_select(dm, bFILE, KEY_name);
    bFILE.status = xd_last(dm, bFILE);
    while ( xd_OK(dm) == bFILE.status ) {
        print_person(&buff);
        bFILE.status = xd_prev(dm, bFILE);
    }
    printf ("\n\n File ordered by number-name (FORWARD)");
    xd_select(dm, bFILE, KEY_number);
    bFILE.status = xd_first(dm, bFILE);
    while ( xd_OK(dm) == bFILE.status ) {
        print_person(&buff);
        bFILE.status = xd_next(dm, bFILE);
    }
    printf ("\n\n File ordered by number-name (BACKWARDS)");
    xd_select(dm, bFILE, KEY_number);
    bFILE.status = xd_last(dm, bFILE);
    while ( xd_OK(dm) == bFILE.status ) {
        print_person(&buff);
        bFILE.status = xd_prev(dm, bFILE);
    }
    printf ("\n\n Access by KEY_number");   /* provide the key */
    xd_select(dm, bFILE, KEY_number);       /* directly        */
    xd_seek(dm, bFILE, "55555555");
    check_error(&bFILE);
    print_person(&buff);
    printf ("\n\n Access by KEY_name");
    xd_select(dm, bFILE, KEY_name);
    strcpy(buff.number, "44444444"); /* store key components */
    strcpy(buff.name, "Lorena");     /* in the data buffer   */
    xd_find(dm, bFILE);
    check_error(&bFILE);
    print_person(&buff);
    printf(
        "\n\n Changing amount to 5.0 by number-name (FORWARD)");
    xd_select(dm, bFILE, KEY_number);
    bFILE.status = xd_first(dm, bFILE);
    while ( xd_OK(dm) == bFILE.status ) {
        buff.amount = 5.0;
        xd_mod(dm, bFILE);
        print_person(&buff);
        bFILE.status = xd_next(dm, bFILE);
    }
    if (xd_EOF(dm) != bFILE.status) {
        check_error(&bFILE);
    }
    printf(
        "\n\n File ordered by physical position (FORWARD)");
    xd_select(dm, bFILE, DRN_ACCESS);
    bFILE.status = xd_first(dm, bFILE);
    check_error(&bFILE);
    while ( xd_OK(dm) == bFILE.status ) {
        print_person(&buff);
        bFILE.status = xd_next(dm, bFILE);
    }
    printf("\n\n File ordered by physical position (BACKWARD)");
    xd_select(dm, bFILE, DRN_ACCESS);
    bFILE.status = xd_last(dm, bFILE);
    check_error(&bFILE);
    while ( xd_OK(dm) == bFILE.status ) {
        print_person(&buff);
        bFILE.status = xd_prev(dm, bFILE);
    }
    printf(
        "\n\n [%ld] records processed", xd_rec_count(dm,bFILE));
    printf(
        "\n\n Deleting al records by number-name (BACKWARDS)");
    xd_select(dm, bFILE, KEY_number);
    bFILE.status = xd_last(dm, bFILE);
    while ( xd_OK(dm) == bFILE.status ) {
        print_person(&buff);
        bFILE.status = xd_del(dm, bFILE);
        check_error(&bFILE);
        bFILE.status = xd_last(dm, bFILE);
    }
    if (xd_EOF(dm) != bFILE.status) {
        check_error(&bFILE);
    }
    /* close the file */
    xd_close(dm, bFILE);
    check_error(&bFILE);
    printf ("\n End of process\n");
    exit (0);
}
void print_person(const person *bFILE) {
/* "Pretty" prints a person record.
*/
    printf ("\n N(%s)  Name: %-10.10s Sx(%c) Am(%7.2f) F(%s)",
        bFILE->number, bFILE->name,   bFILE->sex,
        bFILE->amount, bFILE->birth
    );
}
int check_error(xd_declare(dm) *bFILE) {
/* Checks the error status code in bFILE, and bombs
   when it's no good.
*/
    if (xd_OK(dm) != bFILE->status) {
        printf(
            "\nError code(%d) \"%s\" [%s:%s]\n",
            bFILE->status,
            xd_error(dm, bFILE->status),
            xd_type(dm, *bFILE),
            xd_file(dm, *bFILE)
        );
        xd_close(dm, *bFILE);
        exit(bFILE->status);
    }
    return FALSE;
} /* check_error */
#endif /* CREATE */
/* End of file person.c */
![[<>]](../../img/index.gif) 
 ![[\/]](../../img/bottom.gif) 
 ![[/\]](../../img/top.gif) 
 
| [-] | Resumen | 
| [-] | Introducción | 
| [-] | La programación en C | 
| [-] | Definición de archivos | 
| [-] | Uso de encabezados para describir archivos | 
| [-] | El macro preprocesador de C | 
| [-] | Interfaz genérica " genridx.h" | 
| [-] | Verbos de acceso a archivos indizados | 
| [-] | La implementación de " person.c" | 
| [-] | Uso del manejador de archivos Btrieve | 
| [-] | Ventajas de usar " genridx.h" | 
| [-] | El encabezado " btrieve.h" | 
| [-] | Implementación de " btrieve.c" | 
| [-] | Uso de dos manejadores de archivos a la vez | 
| [-] | Implementación de otras interfaces con base a " genridx.h" | 
| [-] | Conclusión | 
| [-] | Agradecimiento | 
|  | |
| [-] | Listado 1: person.h | 
| [-] | Listado 2: genridx.h | 
| [-] | Listado 3: person.c | 
|  | |
|   | Notas de pie de página | 
|   | Bibliografía | 
|   | Indice | 
|   | Acerca del autor | 
|   | Acerca de este documento | 
| ![[/\]](../../img/top.gif)  | Principio ![[<>]](../../img/index.gif) Indice ![[\/]](../../img/bottom.gif) Final | 
![[<>]](../../img/index.gif) 
 ![[\/]](../../img/bottom.gif) 
 ![[/\]](../../img/top.gif) 
 
![[mailto]](../../img/mailbox.gif) Adolfo Di Mare <adolfo@di-mare.com>
Adolfo Di Mare <adolfo@di-mare.com>
![[<>]](../../img/index.gif) 
 ![[\/]](../../img/bottom.gif) 
 ![[/\]](../../img/top.gif) 
 
| Referencia: | Di Mare, Adolfo:
       " genridx.h" Una interfaz uniforme para el uso
       de archivos indizados en C,
       Revista 
       Acta Académica,
       Universidad Autónoma de Centro América,
       
       Número 15,
       pp [3558],
       ISSN 10177507, Noviembre 1994. | 
| Internet: | 
       
        http://www.uaca.ac.cr/actas/1994nov/genridx.htm
      http://www.di-mare.com/adolfo/p/genridx.htm
      
      http://www.di-mare.com/adolfo/p/src/genridx.zip
       | 
| Autor: | Adolfo Di Mare 
  <adolfo@di-mare.com> | 
| Contacto: | Apdo 7637-1000, San José Costa Rica Tel: (506) 234-0701 Fax: (506) 224-0391 | 
| Revisión: | UACA, Enero 1998 | 
| Visitantes: | 
| ACTA ACADEMICA no pone como requisito que los artículos sean inéditos, ni adquiere la propiedad de ellos. Pueden ser citados (pero no reproducidos) libremente, siempre que se indique la fuente. Para reproducir el artículo se necesita permiso del (los) autor(es). Cada autor da permiso para que Acta Académica publique en la Internet la versión electrónica de su artículo. | Los autores deben corregir las artes de su artículo. | 
| ACTA ACADEMICA neither requires for articles to be unpublished, nor acquires their property. They can be quoted (but not reproduced) freely, but always indicating the source. Permisson from the author(s) is required to reproduce an article. Each author gives permission to Acta Académica to publish in the Internet the electronic version of his/her article. | Authors must correct the arts of their article. | 
![[mailto]](../../img/mailbox.gif) ACTA ACADEMICA,
UACA.
  ACTA ACADEMICA,
UACA.
Copyright © 1994 Adolfo Di Mare
| ![[home]](../../img/home.gif) | ![[<>]](../../img/index.gif) | ![[/\]](../../img/top.gif) |