home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS 1992 June / SIMTEL_0692.cdr / msdos / txtutl / concat10.arc / CONCAT10.ASM < prev    next >
Encoding:
Assembly Source File  |  1989-05-02  |  11.8 KB  |  506 lines

  1.     TITLE    Concat
  2.  
  3. ;Input:
  4. ;    Read in multiple files (as named on the DOS cmd line).
  5. ;    Wildcards and full pathing are supported.
  6. ;
  7. ;Output:
  8. ;    To StdOut (e.g., via redirection at the DOS command line).
  9. ;    (No ^Z is appended to the output.)
  10. ;
  11. ;End of File:
  12. ;    Assumes the first ^Z in each file is EOF (and the rest is garbage),
  13. ;    so will discard everything after that first ^Z.
  14. ;Notes:
  15. ;    Makes no attempt to add terminating CR/LF to each file
  16. ;    or any other such cleanup.
  17.  
  18. ;Released to the public domain
  19. ;David Kirschbaum
  20. ;Toad Hall
  21. ;kirsch@braggvax.ARPA
  22.  
  23. CR    equ    0DH
  24. LF    equ    0AH
  25. CTRLZ    equ    1AH        ;Ctrl-Z
  26.  
  27. STDOUT    equ    1        ;DOS StdOut
  28. STDERR    equ    2        ;DOS StdErr (screen)
  29.  
  30. DTA_TYPE    struc
  31. reserved    db    21 DUP(?)    ;Used by DOS for find next
  32. attrib        db    ?        ;file attribute
  33. time        dw    ?        ;file time stamp
  34. date        dw    ?        ;file date stamp
  35. filesize    dw    ?,?        ;file size in bytes (long integer)
  36. filename    db    13 DUP(?)    ;AsciiZ filename string
  37. DTA_TYPE    ends
  38.  
  39. CSEG    segment    public    'CODE'
  40.     assume CS:CSEG,DS:CSEG
  41.  
  42.     org    80H
  43. dta    label    DTA_TYPE
  44. nchar    db    ?            ;for Args
  45. params    db    ?
  46.  
  47. ;    org    fcb_dta + 26        ;dta filesize
  48. ;filesize dw    ?,?
  49. ;
  50. ;    org    dta + 30        ;DTA.filename
  51. ;dtaname    db    ?            ;for wildcard expansion
  52.  
  53.     org    100H            ;yep, a .COM file
  54.  
  55. Concat    proc    near
  56.     jmp    Start            ;skip over data
  57.  
  58. usagemsg db    'CONCAT - Concatenate text files to StdOut',CR,LF
  59.     db    'Usage:  CONCAT [d:\]file1.txt file2.txt >output.txt',CR,LF
  60.     db    'Input:  Wildcards and full pathing permitted.',CR,LF
  61.     db    'Output: DOS Standard Output (redirectable).',CR,LF
  62.     db    'David Kirschbaum, Toad Hall, v1.0'
  63. crlf        db    CR,LF
  64. USAGEMSGLEN    equ    $ - usagemsg
  65. CRLFLEN        equ    $ - crlf
  66.  
  67. notfoundmsg    db    ' not found',CR,LF
  68. NOTFOUNDLEN    equ    $ - notfoundmsg    ;msg length
  69.  
  70. openerrmsg    db    ' File open error',CR,LF
  71. OPENERRLEN    equ    $ - openerrmsg
  72.  
  73. readerrmsg    db    ' File read error',CR,LF
  74. READERRLEN    equ    $ - readerrmsg
  75.  
  76. writerrmsg    db    ' File write error',CR,LF
  77. WRITERRLEN    equ    $ - writerrmsg
  78.  
  79. zerofilemsg    db    ' Empty file, skipped',CR,LF
  80. ZEROFILELEN    equ    $ - zerofilemsg
  81.  
  82. handle    dw    0            ;input file handle
  83.  
  84. Concat    endp
  85.  
  86.  
  87. ;Returns position of char in string
  88. ;DI -> string start or end
  89. ;AL =  char to seek
  90. ;Returns psn in CX
  91. ;It'll work in reverse also if you enter with DI -> string end
  92. ;and STD instead of CLD
  93.  
  94. Find_Char    proc    near
  95.     mov    cx,0FFFFH        ;max scan
  96.     repne    scasb
  97.     not    cx            ;CX=char psn
  98.     ret
  99. Find_Char    endp
  100.  
  101.  
  102. ;Display an AsciiZ string to StdErr
  103. ;Enters with SI -> AsciiZ string
  104. ;Destroys AX,DI
  105. ;Returns CX = string length, DX, SI -> AsciiZ string
  106.  
  107. Pr_AsciiZ    proc    near
  108.     xor    al,al            ;find terminating 0
  109.     mov    di,si            ;from string start
  110.     cld                ;go forward
  111.     call    Find_Char        ;CX = psn of 0, SI unchanged
  112.     dec    cx            ;- 1 for actual length
  113.     mov    dx,si            ;string start
  114.     mov    bx,STDERR        ;output to StdErr
  115.     mov    ah,40H            ;write to file/device
  116.     int    21H
  117.     ret
  118.  
  119. Pr_AsciiZ    endp
  120.  
  121.  
  122. ;Separate a drive:\path from an arg filename.
  123. ;SI -> arg (an AsciiZ string)
  124. ;Pointer pathend   -> beyond last char in path (filename[0] if none)
  125.  
  126. Split_Path    proc    near
  127.  
  128.     mov    di,si            ;from string start
  129.     xor    al,al            ;find AsciiZ arg end
  130.     cld                ;go forward
  131.     call    Find_Char        ;CX=psn of 0, DI -> beyond 0
  132.     dec    di            ;DI -> AsciiZ 0
  133.     dec    di            ;DI -> last char in filename
  134.  
  135.     mov    al,'\'            ;scan for subdirectories
  136.     std                ;backwards towards start
  137.     repne    scasb            ;CX has filename length
  138.     cld                ;insure forward again
  139.     or    cx,cx            ;any path?
  140.     jnz    Got_Path        ;yep
  141.  
  142. ;No '\',how about ':'?
  143.  
  144.     xor    cx,cx            ;assume no path, no move
  145.     mov    di,si            ;string start
  146.     cmp    byte ptr 1[di],':'    ;divider should be after drive char
  147.     jnz    No_Path            ;nope, DI -> filename's first char
  148.  
  149. ;In either case, DI should be pointing to the char
  150. ;BEFORE the path or drive separator.
  151.  
  152. Got_Path:
  153.     add    di,2            ;bump beyond '\' or ':'
  154.     mov    cx,di            ;path\filename end
  155.     sub    cx,si            ;- path end = bytes to move
  156. No_Path:
  157.     mov    di,offset path        ;move into path
  158.     jcxz    No_Path_Move
  159.      rep    movsb            ;copy args path into buffer
  160. No_Path_Move:
  161.     mov    word ptr pathptr,di    ;pathptr -> char beyond args path
  162.                     ;where we'll move the filename
  163. Split_Path    endp
  164.  
  165.  
  166.  
  167. ;Move our expanded filename after our arg path.
  168.  
  169. Add_Path    proc    near
  170.     mov    si,offset dta.filename        ;expanded filename
  171.     mov    di,si                ;Find_Char wants it in DI
  172.     xor    al,al                ;get AsciiZ name length
  173.     cld                    ;forward
  174.     call    Find_Char            ;CX = char psn, SI unchanged
  175.     mov    di,word ptr pathptr        ;-> arg path + 1 (or start)
  176.     rep    movsb                ;move in the name
  177.                         ;(include the AsciiZ 0)
  178.     mov    si,offset path            ;-> path start
  179.     ret
  180. Add_Path    endp
  181.  
  182.  
  183. ;Displays expanded filename
  184. ;Copies the wildcard file to StdOut
  185. ;Enter with SI -> expanded filename
  186. ;On return:
  187. ;  handle = file handle (or 0 if never opened)
  188. ;  CF set = error
  189. ;  DX=CR/LF or error msg (if error)
  190. ;  CX = msg length
  191.  
  192. Copy_File    proc    near
  193.     mov    handle,0        ;insure file handle is cleared
  194.  
  195.     call    Add_Path        ;add arg path to expanded filename
  196.                     ;(we could be doing a Find Next)
  197.                     ;returns SI, DX -> path:\name
  198.     call    Pr_AsciiZ        ;display AsciiZ name
  199.                     ;DX -> AsciiZ path:\name
  200.  
  201.     mov    ax,3D00H        ;open for read only
  202.     int    21H
  203.     jb    Open_Err        ;failed
  204.  
  205.     mov    handle,ax        ;save the handle
  206.  
  207.     mov    si,offset dta.filesize    ;DTA filesize long integer
  208.     lodsw                ;get filesize.lo
  209.     or    ax,[si]            ;test filesize.hi
  210.     jz    Zero_File        ;empty file, forget it
  211.  
  212. ReadLup:
  213.     mov    dx,offset readbuff    ;file read buffer
  214.     mov    cx,READBUFFSIZE        ;bytes to read
  215.     mov    bx,handle        ;file handle
  216.     mov    ah,3FH            ;read from file/device
  217.     int    21H
  218.     jb    Read_Err        ;read failed
  219.  
  220.     mov    cx,ax            ;bytes read
  221.     jcxz    File_Eof1        ;nothing read, it's EOF
  222.  
  223.     mov    bx,ax            ;remember bytes read
  224.     mov    di,dx            ;DI -> read buffer
  225.     mov    al,CTRLZ        ;scan for terminating Ctrl Z
  226.     repne    scasb
  227.     jz    File_EOF        ;it's logical EOF
  228.  
  229.     mov    cx,bx            ;restore nr bytes read
  230.     mov    bx,STDOUT        ;output to StdOut
  231.     mov    ah,40H            ;write to file/device
  232.     int    21H
  233.     jnb    ReadLup            ;wrote ok, keep going
  234.  
  235. Write_Err:
  236.     mov    dx,offset writerrmsg    ;' File write error'
  237.     mov    cx,WRITERRLEN
  238.     ret                ;CF set
  239.  
  240. Zero_File:
  241.     mov    dx,offset zerofilemsg    ;' Zero file size .. skipped'
  242.     mov    cx,ZEROFILELEN
  243.     ret                ;CF clear
  244.     
  245. Read_Err:
  246.     mov    dx,offset readerrmsg    ;' File read error'
  247.     mov    cx,READERRLEN
  248.     ret                ;CF set
  249.  
  250. File_Eof:
  251.     inc    cx            ;adjust for that Ctrl Z
  252.     sub    bx,cx            ; bytes read - Ctrl Z psn
  253.     mov    cx,bx            ;= bytes to write
  254.     mov    bx,STDOUT        ;output to StdOut
  255.     mov    ah,40H            ;write to file/device
  256.     int    21H
  257.     jb    Write_Err        ;final write failed
  258.  
  259. File_Eof1:
  260.     mov    dx,offset crlf        ;new line after filename
  261.     mov    cx,CRLFLEN
  262.     ret                ;CF should be clear
  263.  
  264. Open_Err:
  265.     mov    dx,offset openerrmsg    ;'File open error'
  266.     mov    cx,OPENERRLEN
  267.     ret                ;CF set
  268.  
  269. Copy_File    endp
  270.  
  271.  
  272. ;Do a wildcard copy.
  273. ;SI -> AsciiZ filename
  274.  
  275. OutPut    proc    near
  276.  
  277.     mov    dx,si            ;DX = AsciiZ filename ptr
  278.     xor    cx,cx            ;no special attributes
  279.     mov    ah,4EH            ;find first
  280.     int    21H
  281.     or    ax,ax            ;find anything?
  282.     jz    Found_First        ;yep
  283.  
  284.      call    Pr_AsciiZ        ;display filename (still in SI)
  285.      mov    dx,offset notfoundmsg    ;' not found'
  286.      mov    cx,NOTFOUNDLEN        ;msg length
  287.      stc                ;CF set to exit
  288.      jmp    short Not_Found        ;display error msg, return
  289.  
  290. ;Remember, a Find First or Find Next will give us an expanded filename
  291. ;(in the DTA) but will NOT give us a path!
  292. ;We have to process the arg (SI still points to it) and split off
  293. ;the path from the filename.
  294.  
  295. Found_First:
  296.     call    Split_Path        ;separate arg path from arg
  297.                     ;arg filename (for Find First only).
  298.                     ;fall thru to...
  299. Find_Next:                ;our wildcard loop
  300.     call    Copy_File        ;open, copy the file
  301.  
  302. ;On return:
  303. ;  handle = file handle (or 0 if never opened)
  304. ;  CF set = error
  305. ;  DX=CR/LF or error msg (if error)
  306. ;  CX = msg length
  307.  
  308. Not_Found:
  309.     pushf                ;save copy flags
  310.     mov    bx,STDERR        ;Std Err handle
  311.     mov    ah,40H            ;write to file/device
  312.     int    21H
  313.  
  314.     mov    bx,handle        ;restore input file handle
  315.     or    bx,bx            ;ever opened?
  316.     jz    No_Handle        ;nope
  317.      mov    ah,3EH            ;close input file
  318.      int    21H
  319. No_Handle:
  320.  
  321.     popf                ;restore copy flags
  322.     jb    OutPutX            ;some sort of error
  323.  
  324. ;Now for the Find Next
  325.     mov    ah,4FH            ;continue file search
  326.     int    21H
  327.     cmp    al,18            ;no more files to be found?
  328.     jnz    Find_Next        ;nope, found one, continue
  329. OutPutX:
  330.     ret                ;all done with this file name
  331.  
  332.  
  333. OutPut    endp
  334.  
  335.  
  336. Start    proc    near
  337.     call    _Args            ;process cmdline into argv array
  338.     or    ax,ax            ;any args?
  339.     jnz    Concat_Lup        ;yep, concat them
  340.  
  341.     mov    dx,offset usagemsg    ;'Usage:'
  342.     mov    cx,USAGEMSGLEN
  343.     mov    bx,STDERR
  344.     mov    ah,40H            ;write to file/device
  345.     int    21H
  346.     jmp    short Concat_Term    ;terminate
  347.  
  348. Concat_Lup:
  349.     mov    si,word ptr argv[2]    ;snarf an arg
  350.     or    si,si            ;null arg?
  351.     jz    Concat_Done        ;yep
  352.      call    OutPut            ;copy file to StdOut
  353.      call    _Shift            ;shift the args down
  354.      jmp    Concat_Lup
  355.  
  356. Concat_Done:
  357.  
  358. Concat_Term:
  359.     mov    ax,4C00H
  360.     int    21H
  361.  
  362. Start    endp
  363.  
  364.  
  365. ;---- args.asm ----------------------------------------------------------
  366. ;
  367. ; Args parses the command line into a unix-style parameter array.
  368. ; Argc and Argv are just as in C; argc = # of params on cmd line,
  369. ; argv is an array of pointers to strings (null terminated, of course). 
  370. ; Because MS-DOS doesn't pass us the name used to invoke the program,
  371. ; argv[0] always points to a null string.
  372. ; _Shift updates argc.
  373. ;
  374. ; Do not call NEW before calling _Args.
  375. ;
  376. ;--------------------------------------------------------------------------
  377.  
  378. ; Maximum number of parameters
  379. MAXPARMS    equ    32
  380.  
  381.  
  382. Args2    proc    near
  383.  
  384.     jmp    Start
  385.  
  386. Args2    endp
  387.  
  388. _Args    proc    near
  389.  
  390. ; initialize
  391.  
  392.     cld                ;insure fwd
  393.  
  394.     mov    di,offset argv        ;first arg
  395.     mov    cx,MAXPARMS        ;max parms allowed
  396.     xor    ax,ax
  397.     rep    stosw            ;point all args at null
  398.  
  399.     mov    bx, 2            ; argc = 0 (will sub 2 from bx at end)
  400.  
  401.     mov    si,offset nchar        ;cmdline char count
  402.     lodsb                ;snarf, bump SI ptr
  403.     mov    cl,al            ;into CX for counter
  404.     xor    ch,ch
  405.     jcxz    A_Done            ; no arg chars -> we're done.
  406.  
  407.     mov    di,offset argbuff    ;big arg buffer
  408.  
  409. ; Move arguments out of default DTA.
  410.  
  411.     push    cx
  412.     push    di
  413.     mov    dx,2000H + 'a'        ;handy constant
  414. A_UpperLup:
  415.     lodsb                ;snarf PSP cmdline char
  416.     cmp    al,CR            ;CR?
  417.     jz    Stuff0            ;yep
  418.     cmp    al,dh            ;space?
  419.     jnz    A_NotSpace        ;nope, more checking
  420. Stuff0:
  421.      xor    al,al            ;replace with a 0
  422.      jmp    short A_Stuff
  423.  
  424. A_NotSpace:
  425.     cmp    al,dl    ;'a'        ;lower case?
  426.     jb    A_Stuff            ;nope
  427.      sub    al,dh    ;20H        ;uppercase it
  428. A_Stuff:
  429.     stosb                ;stuff arg char into argbuff
  430.     loop    A_UpperLup
  431.     xor    al,al
  432.     stosb                ;insure terminating 0
  433.     pop    si
  434.     pop    cx
  435.  
  436.     ; Big loop- find arguments...
  437.  
  438. A_ParmL:
  439.     lodsb                ; al = [si++]
  440.     or    al,al            ;former space separator?
  441.     loopnz    A_ParmL            ;keep gobbling until we find one
  442.     jcxz    A_Done            ; found no unblank chars -> we're done.
  443.  
  444.     mov    word ptr argv[bx],si    ; save pointer to this string
  445.  
  446.     add    bx,2            ; argc++;
  447.  
  448.     cmp    bx, MAXPARMS*2
  449.     jae    A_Done            ;done
  450.      or    cx,cx            ;stop any CX wraparound to 0FFFFH!
  451.      loopnz    A_ParmL            ;decr CX, reloop
  452.  
  453. A_Done:
  454. ; All done finding parms; now share argc with caller.
  455.  
  456.     mov    ax,bx
  457.     sub    ax, 2        ;adjust for argv[0]
  458.                 ;(traditionally program name)
  459.  
  460.     shr    ax, 1
  461.     mov    word ptr argc,ax    ;save argc
  462.     ret
  463.  
  464. _Args    endp
  465.  
  466. ;---- _Shift: --------------------------------------------
  467. ; Shifts %2 to %1, %3 to %2, etc.  Leaves %0 alone.
  468. ; Works by shuffling argv[*].
  469.  
  470. _Shift    proc    near
  471.  
  472.     cld
  473.     mov    si, offset argv[4]
  474.     mov    di, offset argv[2]
  475.     mov    cx,word ptr argc    ;move this many args
  476.     rep    movsw
  477.     dec    word ptr argc        ;decr arg counter
  478.     ret
  479.  
  480. _Shift    endp
  481.  
  482.  
  483. ;---- parameter count, array ----------------
  484. ; Pointers to up to MAXPARMS parameter strings are held here.
  485.  
  486.     even                ;keep things neat
  487.  
  488. argc    equ    $            ;really just a byte
  489.                     ;same as dw ?
  490. argv    equ    argc + 2        ;dynamic argv array of pointers
  491.                     ;same as dw MAXPARMS DUP(?)
  492. argbuff    equ    argv + (MAXPARMS ShL 1)        ;dynamic args buffer
  493.                     ;same as db 128 DUP(?)
  494.  
  495. readbuff    equ    argbuff + 128    ;dynamic read buffer
  496.                     ;same as db 4096 DUP(?)
  497. READBUFFSIZE    equ    4096        ;4Kb for now
  498.  
  499. pathptr    equ    readbuff + 4096        ;pointer to path start
  500.                     ;same as dw ?
  501. path    equ    pathptr + 2        ;path:\filename buffer
  502.                     ;same as db 128 dup(?)
  503.  
  504. CSEG    ENDS
  505.     end    Concat
  506.