home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / pascal / library / dos / murutil / update.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1988-11-21  |  15.8 KB  |  613 lines

  1. PROGRAM UPDATE;
  2.  
  3. {$N-  Don't use the numeric coprocessor.}
  4.  
  5. {  Example of use:  UPDATE A:*.* B:*.*
  6.  
  7.    This Turbo Pascal V4.0 program updates files from a source  directory
  8.    (or device) to a target directory (or device);  in the above example,
  9.    updating from  A:*.* to  B:*.*.   Those source files which are not in
  10.    the target directory,  or which are newer than the same  named  files
  11.    in the target directory are copied to the target directory in  alpha-
  12.    betical order.   This updating process ensures that the most  current
  13.    versions of the source files are on the target directory  or device).
  14.  
  15.    UPDATE accepts one of two options as a third parameter,  /S  and  /V.
  16.    /S shows which files would be copied, but suppresses any actual copy-
  17.    ing.   /V verifies each file copy.   These options are  mutually  ex-
  18.    clusive.
  19.  
  20.    Examples:  UPDATE *.* \ZERO\*.* /S
  21.               UPDATE *.* \ZERO\*.* /V
  22.  
  23.    Hitting any key during the update process will cause the  program  to
  24.    pause.  When paused, ^C and Esc will stop the program;  any other key
  25.    will resume the program.
  26.  
  27.    Note:  Files with the Hidden, System, Volume ID, or Directory attri-
  28.           bute and files with ".BAK" or ".~??" extensions are excluded
  29.           from this update process.
  30.  
  31.    Updated 19 May 1988 to show files to be copied.
  32.  
  33.    Updated 21 Nov 1988 to use BLOCKREAD and BLOCKWRITE to do the copying
  34.    and verifying internally,  rather than using EXEC to invoke the  COPY
  35.    utility.
  36.  
  37.    Program by Harry M. Murphy  --  13 February 1988.  }
  38.  
  39. USES
  40.     CRT,
  41.     DOS;
  42.  
  43. CONST
  44.     BUFFSIZE = 8192;
  45.     ERRPROL  = '        >>>  UPDATE ERROR!  '#7;
  46.     FILELEN  = 12;
  47.     MAXLIST  = 1000;
  48.     PATHLEN  = 64;
  49.     SYNTAX   = 'UPDATE syntax:  UPDATE sourcefile targetfile [/S|/V]';
  50.  
  51. TYPE
  52.     BUFFER  = ARRAY[1..BUFFSIZE] OF CHAR;
  53.     FILENAM = STRING[FILELEN];
  54.     FILEREC = RECORD
  55.                 NAME : FILENAM;
  56.                 TIME : LONGINT
  57.               END;
  58.     FILELST = ARRAY [1..MAXLIST] OF FILEREC;
  59.     PATHNAM = STRING[PATHLEN];
  60.  
  61. VAR
  62.     ABUFF : BUFFER;   { Source file buffer.      }
  63.     APARM : PATHNAM;  { Source parameter string. }
  64.     APATH : PATHNAM;  { Source path name.        }
  65.     ALIST : FILELST;  { Source file list.        }
  66.     ANUMB : INTEGER;  { Number of source files.  }
  67.  
  68.     BBUFF : BUFFER;   { Target file buffer.      }
  69.     BPATH : PATHNAM;  { Target parameter string. }
  70.     BPARM : PATHNAM;  { Target path name.        }
  71.     BLIST : FILELST;  { Target file list.        }
  72.     BNUMB : INTEGER;  { Number of target files.  }
  73.  
  74.     NPAR  : WORD;     { Parameter count.         }
  75.  
  76.     COPY  : BOOLEAN;  { Perform copy flag.       }
  77.     ERROR : BOOLEAN;  { Error flag.              }
  78.     VERI  : BOOLEAN;  { Do copy verify flag.     }
  79.  
  80. { -------------------------------- }
  81.  
  82. PROCEDURE CHECKKEYBD;
  83.  
  84. {  This procedure checks if any key has been struck.  If so, it pauses
  85.    and waits for a second key.   If the key is ^C or Esc,  it sets the
  86.    error flag, ERROR, to true;  otherwise it simply returns.  }
  87.  
  88. VAR
  89.   KEY : CHAR;
  90.  
  91. BEGIN
  92.   IF KEYPRESSED
  93.     THEN
  94.       BEGIN
  95.         WHILE KEYPRESSED DO KEY := READKEY;
  96.         HIGHVIDEO;
  97.         WRITE('PAUSING.   ^C or Esc aborts;  any other key to resume.');
  98.         NORMVIDEO;
  99.         SOUND(880);
  100.         DELAY(100);
  101.         NOSOUND;
  102.         WHILE NOT KEYPRESSED DO;
  103.         WRITE(#13);
  104.         CLREOL;
  105.         KEY := READKEY;
  106.         ERROR := ERROR OR (KEY IN [#3,#27]);
  107.         WHILE KEYPRESSED DO KEY := READKEY
  108.       END
  109. END { Procedure CHECKKEYBD };
  110.  
  111. { -------------------------------- }
  112.  
  113. PROCEDURE CHECKPARCOUNT(VAR NPAR: WORD);
  114.  
  115. {  This procedure verifies that the parameter count is two or three.
  116.    If no parameters are furnished, it displays the UPDATE syntax and
  117.    halts.  }
  118.  
  119. BEGIN
  120.   NPAR := PARAMCOUNT;
  121.   IF NPAR = 0
  122.     THEN
  123.       BEGIN
  124.         WRITELN(SYNTAX);
  125.         HALT
  126.       END
  127.     ELSE
  128.       IF (NPAR < 2) OR (NPAR > 3)
  129.         THEN
  130.           BEGIN
  131.             WRITE(ERRPROL);
  132.             IF NPAR < 2
  133.               THEN
  134.                 WRITELN('There must be at least two parameters.')
  135.               ELSE
  136.                 WRITELN('There must be no more than three parameters.');
  137.             HALT
  138.           END
  139. END { Procedure CHECKPARCOUNT };
  140.  
  141. { -------------------------------- }
  142.  
  143. FUNCTION DIRPATH(FPARM: PATHNAM): PATHNAM;
  144.  
  145. {  This STRING function returns the path name from the file parameter
  146.    argument.   For example, if FPARM is "A:\SUBR\*.*" DIRPATH returns
  147.    "A:\SUBR\".  If no path nor device is specified, DIRPATH returns a
  148.    null string.  }
  149.  
  150. VAR
  151.   L : 0..PATHLEN;
  152.  
  153. BEGIN
  154.   L := LENGTH(FPARM);
  155.   WHILE (L > 0) AND NOT (FPARM[L] IN ['\',':']) DO L := PRED(L);
  156.   FPARM[0] := CHR(L);
  157.   DIRPATH := FPARM
  158. END { Procedure DIRPATH };
  159.  
  160. { -------------------------------- }
  161.  
  162. PROCEDURE DOUPDATE;
  163.  
  164. {  This procedure compares the Source and Target file lists,  lists  the
  165.    files to be copied and then lists, copies and  [optionally]  verifies
  166.    the copied files.  }
  167. CONST
  168.   SP = ' ';
  169.  
  170. VAR
  171.   I, J, NCPY  : INTEGER;
  172.   ALPHA, BETA : STRING;
  173.  
  174. { ------ Internal Procedure ------ }
  175.  
  176. PROCEDURE DOCOPY(ALPHA,BETA: STRING; VAR ERROR: BOOLEAN);
  177.  
  178. {  This internal procedure copies file ALPHA to file BETA.  ERROR is set
  179.    to TRUE if an error occurs.  }
  180.  
  181. VAR
  182.   ALPHAF, BETAF : FILE;
  183.   NR, NW        : WORD;
  184.   TIME          : LONGINT;
  185.  
  186. BEGIN
  187.   ASSIGN(ALPHAF,ALPHA);
  188.   RESET(ALPHAF,1);
  189.   GETFTIME(ALPHAF,TIME);
  190.   ASSIGN(BETAF,BETA);
  191.   {$I-} REWRITE(BETAF,1) {$I+};
  192.   ERROR := (IORESULT = 5);
  193.   IF ERROR
  194.     THEN
  195.       WRITELN('  Target file is read-only!',#7)
  196.     ELSE
  197.       BEGIN
  198.         REPEAT
  199.           BLOCKREAD(ALPHAF,ABUFF,BUFFSIZE,NR);
  200.           BLOCKWRITE(BETAF,ABUFF,NR,NW);
  201.           ERROR := (NW <> NR)
  202.         UNTIL (NR = 0) OR ERROR;
  203.         CLOSE(ALPHAF);
  204.         IF NOT ERROR THEN SETFTIME(BETAF,TIME);
  205.         CLOSE(BETAF);
  206.         IF ERROR
  207.           THEN
  208.             BEGIN
  209.               WRITELN('  No room for this file!'#7);
  210.               ERASE(BETAF)
  211.             END
  212.         END
  213. END { Internal Procedure DOCOPY };
  214.  
  215. { ------ Internal Procedure ------ }
  216.  
  217. PROCEDURE DOVERI(ALPHA,BETA: STRING; VAR ERROR: BOOLEAN);
  218.  
  219. {  This internal procedure verifies that file BETA is identical to  file
  220.    ALPHA.  ERROR is set to TRUE if the files are not identical.  }
  221.  
  222. VAR
  223.   ALPHAF, BETAF : FILE;
  224.   I, NRA, NRB   : WORD;
  225.  
  226. BEGIN
  227.   ASSIGN(ALPHAF,ALPHA);
  228.   RESET(ALPHAF,1);
  229.   ASSIGN(BETAF,BETA);
  230.   RESET(BETAF,1);
  231.   ERROR := FILESIZE(ALPHAF) <> FILESIZE(BETAF);
  232.   IF NOT ERROR
  233.     THEN
  234.       REPEAT
  235.         BLOCKREAD(ALPHAF,ABUFF,BUFFSIZE,NRA);
  236.         BLOCKREAD(BETAF,BBUFF,BUFFSIZE,NRB);
  237.         ERROR := (NRA <> NRB);
  238.         IF NOT ERROR
  239.           THEN
  240.             FOR I := 1 TO NRA DO
  241.               ERROR := ERROR OR (ABUFF[I] <> BBUFF[I]);
  242.       UNTIL (NRA = 0) OR ERROR;
  243.   CLOSE(ALPHAF);
  244.   CLOSE(BETAF);
  245.   IF ERROR
  246.     THEN
  247.       WRITELN('  Verification error!'#7)
  248.     ELSE
  249.       WRITELN('  Verified.')
  250. END { Internal Procedure DOVERI };
  251.  
  252. { ------ Internal Procedure ------ }
  253.  
  254. PROCEDURE LISTCOPY;  { Internal to DOUPDATE }
  255.  
  256. {  This internal procedure lists the files to be copied.  }
  257.  
  258. CONST
  259.   PAD = '             ';
  260.  
  261. VAR
  262.   LINE : STRING[80];
  263.   K,KM : BYTE;
  264.   N    : INTEGER;
  265.   REC  : STRING[13];
  266.  
  267. { ------ Internal Procedure ------ }
  268.  
  269. PROCEDURE PUTLINE;  { Internal to LISTCOPY }
  270.  
  271. {  This internal procedure trims and displays each line of files  to  be
  272.    copied.  }
  273.  
  274. VAR
  275.   LL : BYTE;
  276.  
  277. BEGIN { PUTLINE }
  278.   LL := ORD(LINE[0]);
  279.   LINE[0] := CHR(0);
  280.   WHILE LINE[LL] = ' ' DO LL := PRED(LL);
  281.   LINE[0] := CHR(LL);
  282.   WRITELN(LINE);
  283.   LINE := '';
  284.   K := 0;
  285.   KM := 6
  286. END { Internal Procedure PUTLINE };
  287.  
  288. { ------ Internal Procedure ------ }
  289.  
  290. BEGIN { Internal Procedure LISTCOPY }
  291.   K := 0;
  292.   KM := 3;
  293.   LINE := '';
  294.   FOR N := 1 TO NCPY DO
  295.     BEGIN
  296.       REC := ALIST[N].NAME+PAD;
  297.       LINE := LINE+REC;
  298.       K := SUCC(K);
  299.       IF K = KM THEN PUTLINE
  300.     END;
  301.   IF K > 0 THEN PUTLINE
  302. END { Internal Procedure LISTCOPY };
  303.  
  304. { ------ Internal Procedure ------ }
  305.  
  306. BEGIN { Procedure DOUPDATE }
  307.   WRITELN;
  308.  
  309.   WRITELN('Updating files in ',APARM,' to ',BPARM);
  310.   IF ANUMB = 1
  311.     THEN
  312.       WRITELN('    There is one file in ',APARM)
  313.     ELSE
  314.       WRITELN('    There are ',ANUMB,' files in ',APARM);
  315.   IF BNUMB = 1
  316.     THEN
  317.       WRITELN('    There is one file in ',BPARM)
  318.     ELSE
  319.       WRITELN('    There are ',BNUMB,' files in ',BPARM);
  320.  
  321.   {  Scan the source and target directories for:
  322.  
  323.        (1) Files in the source directory which are not in  the  target
  324.            directory;  and
  325.        (2) Files in the source directory which are newer than the same
  326.            files in the target directory.
  327.  
  328.       Do a "pull-down" of the files to be copied in ALIST.  }
  329.  
  330.   I := 1;
  331.   J := 1;
  332.   NCPY := 0;
  333.   REPEAT
  334.     WHILE BLIST[J].NAME < ALIST[I].NAME DO J := SUCC(J);
  335.     IF  (BLIST[J].NAME <> ALIST[I].NAME) OR
  336.        ((BLIST[J].NAME =  ALIST[I].NAME) AND
  337.         (BLIST[J].TIME <  ALIST[I].TIME))
  338.       THEN
  339.         BEGIN
  340.           NCPY := SUCC(NCPY);
  341.           ALIST[NCPY].NAME := ALIST[I].NAME
  342.         END;
  343.     I := SUCC(I)
  344.   UNTIL I > ANUMB;
  345.  
  346.   {  Tell how many files to be copied and list them.  }
  347.  
  348.   IF NCPY = 0
  349.     THEN
  350.       WRITELN('    There are no files to be copied.')
  351.     ELSE
  352.       BEGIN
  353.         IF NCPY = 1
  354.           THEN
  355.             BEGIN
  356.               WRITELN('    There is one file to be copied:  ',ALIST[1].NAME);
  357.               IF VERI THEN WRITELN('    The copy will be verified.')
  358.             END
  359.           ELSE
  360.             BEGIN
  361.               IF NCPY < 10
  362.                 THEN
  363.                   J := 4
  364.                 ELSE
  365.                   IF NCPY < 100
  366.                     THEN
  367.                       J := 3
  368.                     ELSE
  369.                       J := 2;
  370.               WRITE('    There are ',NCPY,' files to be copied:',SP:J);
  371.               LISTCOPY;
  372.               IF VERI THEN WRITELN('    All copies will be verified.')
  373.             END;
  374.  
  375.         {  Copy each file and [optionally] verify it.  }
  376.  
  377.         IF COPY
  378.           THEN
  379.             BEGIN
  380.               WRITELN;
  381.               CHECKBREAK := FALSE;  { Disable Ctl-Break checking. }
  382.               FILEMODE := 0;  { Enable BLOCKREAD of read-only files. }
  383.               CHECKKEYBD;
  384.               IF NOT ERROR
  385.                 THEN
  386.                   BEGIN
  387.                     I := 1;
  388.                     REPEAT
  389.                       ALPHA := APATH+ALIST[I].NAME;
  390.                       BETA := BPATH+ALIST[I].NAME;
  391.                       IF I < 10
  392.                         THEN
  393.                           J := 3
  394.                         ELSE
  395.                           IF I < 100
  396.                             THEN
  397.                               J := 2
  398.                             ELSE
  399.                               J := 1;
  400.                       WRITE('(':J,I,'):  ',ALPHA,' ==> ',BETA);
  401.                       DOCOPY(ALPHA,BETA,ERROR);
  402.                       IF VERI AND (NOT ERROR)
  403.                         THEN
  404.                           DOVERI(ALPHA,BETA,ERROR)
  405.                         ELSE
  406.                           WRITELN;
  407.                       CHECKKEYBD;
  408.                       I := SUCC(I)
  409.                     UNTIL (I > NCPY) OR ERROR
  410.                   END
  411.             END
  412.       END
  413. END { Procedure DOUPDATE };
  414.  
  415. { -------------------------------- }
  416.  
  417. PROCEDURE GETLIST(VAR FPARM: PATHNAM;
  418.                   VAR FLIST: FILELST;
  419.                   VAR FNUM : INTEGER);
  420.  
  421. {  This procedure gets the file names and times for the specified
  422.    file parameter.   Note that files of the form, *.~?? and *.BAK
  423.    are ignored.  }
  424.  
  425. CONST
  426.   ATTR = 1;  { Ordinary files plus read-only files. }
  427.  
  428. VAR
  429.   DTA : SEARCHREC;
  430.   N   : WORD;
  431.  
  432. BEGIN
  433.   N := 0;
  434.   FINDFIRST(FPARM,ATTR,DTA);
  435.   IF DOSERROR = 2
  436.     THEN
  437.       BEGIN
  438.         WRITE(ERRPROL);
  439.         WRITELN('Directory not found.');
  440.         WRITELN('        ',FPARM);
  441.         ERROR := TRUE
  442.       END;
  443.   WHILE (DOSERROR = 0) AND (N < MAXLIST-1) DO
  444.     BEGIN
  445.       IF (POS('.~',  DTA.NAME) = 0) AND
  446.          (POS('.BAK',DTA.NAME) = 0)
  447.         THEN
  448.           BEGIN
  449.             N := SUCC(N);
  450.             FLIST[N].NAME := DTA.NAME;
  451.             FLIST[N].TIME := DTA.TIME
  452.           END;
  453.       FINDNEXT(DTA)
  454.     END;
  455.   FNUM := N
  456. END { Procedure GETLIST };
  457.  
  458. { -------------------------------- }
  459.  
  460. PROCEDURE GETOPTIONS;
  461.  
  462. {  This procedure checks for the mutually exclusive options, /S and /V
  463.    and sets the copy and verification flags accordingly.  }
  464.  
  465. VAR
  466.   OPT : STRING[2];
  467.  
  468. BEGIN
  469.   COPY := TRUE;
  470.   VERI := FALSE;
  471.   IF NPAR = 3
  472.     THEN
  473.       BEGIN
  474.         OPT := PARAMSTR(3);
  475.         IF OPT = '/S'
  476.           THEN
  477.             COPY := FALSE
  478.           ELSE
  479.             IF OPT = '/V'
  480.               THEN
  481.                 VERI := TRUE
  482.               ELSE
  483.                 WRITELN('        Unknown option, "',OPT,'", ignored.')
  484.       END
  485. END { Procedure GETOPTIONS };
  486.  
  487. { -------------------------------- }
  488.  
  489. PROCEDURE SORTFILE(VAR FLIST: FILELST; FNUMB: INTEGER);
  490.  
  491. {  This routine sorts the file name array, FLIST, in ascending order,
  492.    using a modified Shell sort algorithm.  FNUMB is the length of the
  493.    array.  }
  494.  
  495. VAR
  496.   I,IM,J,M : INTEGER;
  497.   SWAP     : BOOLEAN;
  498.   TEMP     : FILEREC;
  499.  
  500. BEGIN  { Procedure SORTFILE }
  501.   IF FNUMB > 1
  502.     THEN
  503.       BEGIN
  504.         M := 1;
  505.         WHILE M < FNUMB DO M := 2*M;
  506.         M := PRED(M);
  507.         WHILE M > 1 DO
  508.           BEGIN
  509.             M := M DIV 2;
  510.             FOR J:=1 TO FNUMB-M DO
  511.               BEGIN
  512.                 I := J;
  513.                 REPEAT
  514.                   IM := I+M;
  515.                   SWAP := FLIST[I].NAME > FLIST[IM].NAME;
  516.                   IF SWAP
  517.                     THEN
  518.                       BEGIN
  519.                         TEMP := FLIST[I];
  520.                         FLIST[I] := FLIST[IM];
  521.                         FLIST[IM] := TEMP;
  522.                         I := I-M
  523.                       END
  524.                 UNTIL (I < 1 ) OR (NOT SWAP)
  525.               END
  526.           END
  527.       END
  528. END { Procedure SORTFILE };
  529.  
  530. { -------------------------------- }
  531.  
  532. PROCEDURE UPPARMS;
  533.  
  534. {  This procedure scans the parameter string in the program's command
  535.    tail at offset 0080H and converts all characters to upper case.
  536.  
  537.    Procedure by Harry M. Murphy,  22 November 1987.
  538.    Updated to Turbo Pascal V4.0 by H.M.M. on 28 November 1987. }
  539.  
  540. CONST
  541.   CT = $0080;
  542.  
  543. VAR
  544.   L,LP : 0..127;
  545.   PSPS : WORD;
  546.  
  547. BEGIN
  548.   PSPS := PREFIXSEG;
  549.   LP := MEM[PSPS:CT];
  550.   IF LP > 0
  551.     THEN
  552.       FOR L := 1 TO LP DO
  553.         IF MEM[PSPS:L+CT] IN [97..122]
  554.           THEN
  555.             MEM[PSPS:L+CT] := MEM[PSPS:L+CT] XOR $20
  556. END { Procedure UPPARMS };
  557.  
  558. { -------------------------------- }
  559.  
  560. PROCEDURE WILDCARD(VAR FPATH, FPARM: PATHNAM);
  561.  
  562. {  This procedure appends wildcard characters to the file parameter,
  563.    if necessary. }
  564.  
  565. BEGIN
  566.   IF FPARM = FPATH
  567.     THEN
  568.       FPARM := FPARM+'*.*'
  569.     ELSE
  570.       IF POS('.',FPARM) = 0 THEN FPARM := FPARM+'.*'
  571. END { Procedure WILDCARD };
  572.  
  573. { -------------------------------- }
  574.  
  575. BEGIN { Program UPDATE }
  576.   NORMVIDEO;
  577.   UPPARMS;
  578.   CHECKPARCOUNT(NPAR);
  579.   GETOPTIONS;
  580.   ERROR := FALSE;
  581.  
  582.   {  Get the source file directory.  }
  583.  
  584.   APARM := PARAMSTR(1);
  585.   APATH := DIRPATH(APARM);
  586.   WILDCARD(APATH,APARM);
  587.   GETLIST(APARM,ALIST,ANUMB);
  588.   IF ANUMB = 0
  589.     THEN
  590.       BEGIN
  591.         WRITE(ERRPROL);
  592.         WRITELN('The source directory is empty.');
  593.         ERROR := TRUE
  594.       END
  595.     ELSE
  596.       IF ANUMB > 1 THEN SORTFILE(ALIST,ANUMB);
  597.  
  598.   {  Get the target file directory.  }
  599.  
  600.   BPARM := PARAMSTR(2);
  601.   BPATH := DIRPATH(BPARM);
  602.   WILDCARD(BPATH,BPARM);
  603.   GETLIST(BPARM,BLIST,BNUMB);
  604.   SORTFILE(BLIST,BNUMB);
  605.   BLIST[BNUMB+1].NAME := '________.___';
  606.   BLIST[BNUMB+1].TIME := MAXLONGINT;
  607.  
  608.   {  Compare the source and target files and do the update.  }
  609.  
  610.   IF NOT ERROR THEN DOUPDATE
  611.  
  612. END.
  613.