Curso de Pascal. Tema 15: Servicios del DOS.

La unidad DOS nos permite acceder a muchos de los servicios que el sistema operativo pone a nuestra disposición, como son:

Vamos a ver algunos ejemplos de su uso...
 

15.1: Espacio libre en una unidad de disco.

{--------------------------}
{  Ejemplo en Pascal:      }
{                          }
{    Espacio libre en la   }
{    unid. de disco actual }
{    ESPACIO.PAS           }
{                          }
{  Este fuente procede de  }
{  CUPAS, curso de Pascal  }
{  por Nacho Cabanes       }
{                          }
{  Comprobado con:         }
{    - Free Pascal 2.2.0w  }
{    - Turbo Pascal 7.0    }
{    - Tmt Pascal Lt 1.20  }
{--------------------------}

program Espacio;  { Mira el espacio libre en la unidad de disco actual }

uses Dos;
begin
  write(' Hay disponibles: ');
  write( DiskFree(0) div 1024 );        { Muestra el espacio libre }
  write(' K de ');
  write( DiskSize(0) div 1024 );        { y el espacio total }
  writeln(' K totales.');
end. 

El 0 de DiskFree y DiskSize indica que se trata de la unidad actual.  Un 1 se referiría a la A, un 2 a la B, un 3 a la C, y así sucesivamente. Si una unidad no existe, DiskSize devuelve -1.


15.2. Fecha del sistema.

Un segundo ejemplo, que muestre la fecha del sistema, sería:


{--------------------------}
{  Ejemplo en Pascal:      }
{                          }
{    Muestra la fecha      }
{    actual                }
{    FECHA.PAS             }
{                          }
{  Este fuente procede de  }
{  CUPAS, curso de Pascal  }
{  por Nacho Cabanes       }
{                          }
{  Comprobado con:         }
{    - Free Pascal 2.2.0w  }
{    - Turbo Pascal 7.0    }
{    - Tmt Pascal Lt 1.20  }
{--------------------------}

program Fecha;

uses Dos;

const
  nombre : array [0..6] of String[15] =
    ('Domingo','Lunes','Martes','Miércoles',
     'Jueves','Viernes','Sábado');
var
  anyo, mes, dia, diaSem : Word;

begin
  GetDate( anyo, mes, dia, diaSem );
  writeln('Hoy es ', nombre[diaSem], ' ',
    dia, '/', mes, '/', anyo);
end.


15.3. Fecha usando interrupciones.

Esto mismo lo podemos hacer usando interrupciones del DOS (os aconsejo no experimentar con esto si no sabeis lo que estais haciendo; una buena fuente de información es la lista de interrupciones recopilada por Ralf Brown). Eso sí, esta forma no funcionará en compiladores de Pascal no basados en MsDos, como FreePascal:


{--------------------------}
{  Ejemplo en Pascal:      }
{                          }
{    Lee la fecha usando   }
{    interrupciones        }
{    FECHA2.PAS            }
{                          }
{  Este fuente procede de  }
{  CUPAS, curso de Pascal  }
{  por Nacho Cabanes       }
{                          }
{  Comprobado con:         }
{    - Turbo Pascal 7.0    }
{    - Tmt Pascal Lt 1.20  }
{--------------------------}

program Fecha2;

uses Dos;

var
  anyo, mes, dia: string;           { Aquí guardaremos cada dato }
  regs: Registers;                  { Necesaria para Intr }
             { El tipo Registers está definido en la unidad Dos }

begin
  regs.ah := $2a;           { Llamamos a la funcion 2Ah }
  with regs do
    intr($21,regs);         { De la interrupción 21h }
  with regs do
    begin
      str(cx, anyo);        { El año está en regs.CX, el mes en DH }
      str(dh, mes);         { y el día en DL.  Pasamos todo a cadena }
      str(dl, dia);         { con Str }
    end;
  writeln('Hoy es ', dia+'/'+mes+'/'+anyo);
end. 


15.4. Lista de ficheros.

Podemos ver la lista de los ficheros que hay en un directorio mediante FindFirst y FindNext.  Por ejemplo, para ver los que tienen extensión .PAS:


{--------------------------}
{  Ejemplo en Pascal:      }
{                          }
{    Lista de ficheros en  }
{    un directorio         }
{    LISTADIR.PAS          }
{                          }
{  Este fuente procede de  }
{  CUPAS, curso de Pascal  }
{  por Nacho Cabanes       }
{                          }
{  Comprobado con:         }
{    - Free Pascal 2.2.0w  }
{    - Turbo Pascal 7.0    }
{--------------------------}
program ListaDir;

uses Dos;

var
  Hallado: SearchRec;            { Información sobre el directorio }
                { El tipo SearchRec está definido en la unidad Dos }

begin
  FindFirst( '*.PAS', AnyFile, Hallado );       { Los busca }
  while DosError = 0 do                         { Mientras existan }
    begin
    Writeln( Hallado.Name );      { Escribe el nombre hallado }
    FindNext( Hallado );          { y busca el siguiente }
    end;
end.

("AnyFile" es una constante, definida también en la unidad DOS y se refiere a que tome cualquier fichero, tenga los atributos que tenga).


15.5. Ejecutar otros programas.

Finalmente, vamos a ver un ejemplo de cómo ejecutar otros programas desde uno nuestro:

{--------------------------}
{  Ejemplo en Pascal:      }
{                          }
{    Ejecuta otro prog.    }
{    desde el nuestro      }
{    EJECUTA.PAS           }
{                          }
{  Este fuente procede de  }
{  CUPAS, curso de Pascal  }
{  por Nacho Cabanes       }
{                          }
{  Comprobado con:         }
{    - Turbo Pascal 7.0    }
{    - Tmt Pascal Lt 1.20  }
{--------------------------}

{$M 2000,0,0 }   { 2000 bytes pila, sin heap }
{ Habrá que suprimir la línea anterior si se usa Tmt Pascal }

program EjecutaOrden;

uses dos;

begin
  SwapVectors;
  Exec('C:\WP51\WP.EXE', 'MITEXTO.TXT');
  SwapVectors;
end. 

Lo del principio es una "directiva de compilación" (se tratan con más detalle en una de las ampliaciones del curso), que indica al compilador cuanta memoria queremos reservar.  En este caso, 2000 bytes para la pila (donde se guardarán las llamadas recursivas, por ejemplo) y nada (0 como mínimo y 0 como máximo) para el Heap o "montón", donde se guardan las variables dinámicas, que no tiene este programa.

Después guardamos los vectores de interrupción con SwapVectors, y ejecutamos el programa, indicando su nombre (camino completo) y los parámetros.  Finalmente, restauramos los vectores de interrupciones.
 


15.6: Ejecutar órdenes del DOS.

Podemos ejecutar ordenes del DOS como DIR y otros programas cuyo nombre completo (incluyendo directorios) no conozcamos, pero para eso hemos de llamar a COMMAND.COM con la opción /C:

{--------------------------}
{  Ejemplo en Pascal:      }
{                          }
{    Ejecuta una orden     }
{    interna de MsDos      }
{    EJECUTA2.PAS          }
{                          }
{  Este fuente procede de  }
{  CUPAS, curso de Pascal  }
{  por Nacho Cabanes       }
{                          }
{  Comprobado con:         }
{    - Turbo Pascal 7.0    }
{    - Tmt Pascal Lt 1.20  }
{--------------------------}

{$M 2000,0,0 }   { 2000 bytes pila, sin heap }
{ Habrá que suprimir la línea anterior si se usa Tmt Pascal }

program EjecutaOrden2;

uses dos;

begin
  SwapVectors;
  Exec('C:\COMMAND.COM', '/C DIR *.PAS');
  SwapVectors;
end. 

¿Y qué ocurre si nuestro intérprete de comandos no es el COMMAND.COM? Esto puede ocurrir, por ejemplo, a quienes usen 4DOS o NDOS, o versiones recientes de Windos, que usen CMD.EXE.  Pues entonces podemos recurrir a la variable de entorno COMSPEC, que nos dice cual es el intérprete de comandos que estamos usando.  Así, nuestra orden Exec quedaría:

Exec( GetEnv('COMSPEC'), '/C DIR *.PAS');

Como se ve en el ejemplo, GetEnv es la función que nos devuelve el valor de una variable de entorno. El fuente completo quedaría:

{--------------------------}
{  Ejemplo en Pascal:      }
{                          }
{    Ejecuta una orden     }
{    interna de MsDos/Win  }
{    EJECUTA3.PAS          }
{                          }
{  Este fuente procede de  }
{  CUPAS, curso de Pascal  }
{  por Nacho Cabanes       }
{                          }
{  Comprobado con:         }
{    - Turbo Pascal 7.0    }
{    - Tmt Pascal Lt 1.20  }
{--------------------------}

{$M 2000,0,0 }   { 2000 bytes pila, sin heap }
{ Habrá que suprimir la línea anterior si se usa Tmt Pascal }

program EjecutaOrden3;

uses dos;

begin
  SwapVectors;
  Exec( GetEnv('COMSPEC'), '/C DIR *.PAS');
  SwapVectors;
end. 

Pues se acabó por hoy. Como siempre, os recuerdo que queda mucho jugo por sacar a esta unidad, así que experimentad.  Recordad que la ayuda on-line os puede sacar de más de un apuro y os puede descubrir más de una cosa.

 Os propongo un par de ejercicios:


(Puedes ver una propuesta de solución -todavía no disponible-, así como un ejemplo -todavía no disponible- que muestra el espacio libre en todas las unidades de disco).

 

15.6b: Principales posibilidades de la unidad DOS.

En el apartado anterior hemos visto algunas de las posibilidades de la unidad DOS.  Ahora vamos a ver una lista de lo que nos permite (en el caso de Turbo Pascal 7.0), para que tengais más base para investigar y experimentar, sin tener que pelearos demasiado con la ayuda... :-)

Indicaré el nombre de cada procedimiento (proc) o función (func), su cometido y el formato de uso... a veces... :-)


Funciones de fecha y hora:

Nombre                    Cometido
-----------------------   ----------------------------------------------
GetDate (proc)            Devuelve la fecha del sistema operativo.
                          Formato: GetDate(anyo, mes, dia, diaSemana)
                          donde anyo, mes, dia, diaSemana son de tipo
                          "word".
SetDate (proc)            Fija la fecha del sistema operativo.
                          Formato: SetDate(anyo, mes, dia)
GetTime (proc)            Devuelve la hora del sistema operativo.
                          Formato: GetTime(hora, min, seg, seg100)
                          con hora, mis, seg, seg100 de tipo "word".
SetTime (proc)            Fija la hora del sistema operativo.
                          Formato: SetTime(hora, min, seg, seg100)
GetFTime (proc)           Fecha y hora de la última modificación de un
                          fichero.  El fichero debe estar asignado y
                          abierto.  La fecha y hora están en formato
                          empaquetado, que se puede descomprimir con
                          UnpackTime.
UnpackTime (proc)         Convierte la fecha y hora del formato que
                          devuelven GetFTime, FindFirst y FindNext en
                          un "record" de formato "DateTime":
                          type
                           TDateTime = record
                              Year,Month,Day,Hour,Min,Sec: Word;
                            end;
SetFTime (proc)           Fija la fecha y hora de un fichero.  La fecha
                          y la hora se pasan como un valor comprimido
                          (longint), que se crea con PackTime.
PackTime (proc)           Convierte un registro DateTime a un valor
                          empaquetado para poder usar SetFTime.


Funciones de estado del disco.

Nombre                 Cometido
--------------------   -------------------------------------------------

DiskFree (func)        Devuelve el número de bytes libres en una unidad
                       de disco.
DiskSize (func)        Devuelve el tamaño total (bytes) de una unidad de
                       disco.
GetVerify (proc)       Devuelve el estado del flag de verificación
                       (verify) del DOS: si es TRUE, las escrituras
                       en disco son verificadas posteriormente; si es
                       FALSE, no se comprueban.
SetVerify (proc)       Fija el flag de verificación a TRUE o FALSE.


Funciones de manejo de ficheros.

Nombre                    Cometido
-----------------------   ---------------------------------------------

FindFirst (proc)          Busca en el directorio que se indique el primer
                          fichero que tenga unos ciertos atributos y un
                          cierto nombre (se permiten comodines * y ?).
                          (Ver un ejemplo en el apartado anterior).
FindNext (proc)           Busca el siguiente fichero que cumpla las
                          condiciones fijadas con FindFirst.  Se
                          comprueba si aún quedan ficheros mirando el
                          valor de la variable DosError.
GetFAttr (proc)           Devuelve los atributos de un fichero.  Estos
                          se encuentran en forma de un "word", en el
                          que cada bit tiene un significado:
                          ReadOnly        $01
                          Hidden          $02
                          SysFile         $04
                          VolumeID        $08
                          Directory       $10
                          Archive         $20
                          AnyFile         $3F
                          Por tratarse de bits, lo habitual será
                          comprobar su valor utilizando el producto
                          lógico "and":
                          if atributos and Hidden <> 0 then ...
SetFAttr (proc)           Fija los atributos de un fichero.
FSearch (func)            Busca un fichero en una lista de directorios.
FExpand (func)            Expande un nombre de fichero a
                          unidad+directorio+nombre+extensión.
FSplit (func)             Parte un nombre de fichero completo en
                          (directorio, nombre y extensión).


Soporte de interrupciones.

Nombre                 Cometido
--------------------   ----------------------------------------------
MsDos (proc)           Ejecuta una llamada a una función del DOS.
                       Para indicar los parámetros de la llamada
                       (valores de los registros del 80x86), se
                       debe usar una variable de tipo "Registers"
                       (ver ejemplo en el apartado anterior).
Intr (proc)            Llama a una cierta interrupción software.
GetIntVec (proc)       Devuelve la dirección a la que apunta un
                       cierto vector de interrupción.  Junto con
                       SetIntVec nos permite modificar las interrup-
                       ciones.  El uso de estas dos ordenes es
                       avanzado, y muy peligroso si no se domina lo
                       que se está haciendo.
SetIntVec (proc)       Fija la dirección a la que apuntará un
                       cierto vector de interrupción.


Manejo de procesos.

Nombre                    Cometido
-----------------------   ----------------------------------------------
Exec (proc)               Ejecuta un cierto programa externo, pasándole
                          una serie de parámetros.
SwapVectors (proc)        Intercambia los vectores de interrupción
                          salvados con los actuales.  Será necesario
                          hacerlo antes y después de Exec, especialmente
                          si hemos redirigido alguna interrupción.
Keep (proc)               Hace que el programa termine y se quede
                          residente (TSR).  El problema está en cómo
                          acceder a él después...  :-)
                          (Se puede conseguir usando GetIntVec y
                          SetIntVec, pero esto cae muyyyy por encima
                          del nivel del curso).
DosExitCode (func)        Devuelve el código de salida de un subproceso,
                          el "errorlevel" que usan los ficheros BAT,
                          y que nosotros podemos fijar haciendo halt(n).


Misceláneas.

Nombre                    Cometido
-----------------------   ----------------------------------------------
DosVersion (func)         Devuelve el número de versión del DOS.  Es
                          un valor de tipo "word", cuyo byte bajo nos
                          indica el número principal de versión (6 en
                          MsDos 6.22, p.ej. ) y cuyo byte alto nos dice
                          el número secundario de versión (22 en el
                          ejemplo anterior).
GetCBreak (proc)          Dice si el DOS comprobará la pulsación de
                          Ctrl-Break (Ctrl+Inter en los teclados
                          españoles).
SetCBreak (proc)          Hace que el DOS compruebe o no la pulsación
                          de Ctrl-Break.