home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS 1992 December / simtel1292_SIMTEL_1292_Walnut_Creek.iso / msdos / txtutl / toadcr11.arc / TOADCRLF.ASM next >
Assembly Source File  |  1989-08-04  |  13KB  |  492 lines

  1.     TITLE CrLf Unix<>DOS EOL converter
  2.  
  3. Comment    ~
  4. Usage:  CRLF [-r] file1 [>output]
  5. -r    Convert from DOS CR/LF End Of Line (EOL) to Unix LF EOL.
  6. Default output is STDOUT (e.g., redirectable).
  7.  
  8. Written to replace the C version by Steve Creps (which was a slug)
  9. Given to the public domain, 7 Jul 89
  10.  
  11. David Kirschbaum
  12. Toad Hall
  13. kirsch@braggvax.ARPA
  14.  
  15. v1.1, 3 Aug 89
  16.  - Bug report from Erich Neuwirth <A4422DAB%AWIUNI11.BITNET@CUNYVM.CUNY.EDU>
  17.    Unix -> DOS conversion breaks down at the BUFFSIZE boundary.
  18.    Found bug when writing output buffer .. a constant CR or LF (in AH)
  19.    was being lost by destroying AX during the output write.
  20.    Fixed.  Thanks, Erich.
  21.  
  22.  - While I was at it, relocated dynamic file buffers
  23.    to overlay the startup code and messages.
  24.  
  25.  - Changed input and output buffer sizes to eliminate the multiple tests
  26.    of the ES:DI output buffer pointer during conversion.
  27.  
  28.    Now we read an input buffer-full, process it to the output buffer,
  29.    and then write the full output buffer .. not worrying about overrunning
  30.    the output buffer end (and our stack!)
  31.  
  32.    This overrun problem would only arise during Unix --> DOS conversions
  33.    (where output is longer than input).
  34.    There's no telling just HOW much longer the output will be than the
  35.    input .. worst case is input * 2 (a file of all CRs converted to
  36.    a file of all CR/LFs).
  37.  
  38.    Assuming worst case, we're making our output buffer TWICE the size
  39.    of the input buffer (most unlikely .. but still ..).
  40.  
  41.    Surprisingly, we've gained no significant speed!  The more frequent
  42.    input file reads offset the increase in processing speed .. sigh ...
  43.  
  44.    Toad Hall
  45.  
  46. Comment    ends    ~
  47.  
  48.  
  49. CR    EQU    0DH
  50. LF    EQU    0AH
  51. FALSE    EQU    0
  52. TRUE    EQU    NOT FALSE
  53.  
  54. STDOUT    EQU    1            ;DOS Std Out
  55. STDERR    EQU    2            ;DOS Std Err
  56. ;BUFFSIZE EQU    30000            ;likely buffer size (arbitrary)
  57. BUFFSIZE EQU    20000            ;input buffer size        v1.1
  58.                     ;output has space for twice that much
  59.  
  60. CSEG    SEGMENT PARA PUBLIC 'CODE'
  61.     ASSUME    CS:CSEG,DS:CSEG,ES:CSEG
  62.  
  63.     org    80H
  64. cmdline    label    byte
  65.  
  66.     org    100H
  67.  
  68. CrLf    proc    near
  69.     jmp    Start            ;skip over data
  70.  
  71. handle    dw    0            ;input file handle
  72.  
  73. eofFlag    db    FALSE            ;non-0 if EOF
  74. addcr    db    LOW(TRUE)        ;assume we're converting LF to CR/LF
  75.  
  76. unix$    db    ' Unix --> DOS',CR,LF,0
  77. dos$    db    ' DOS --> Unix',CR,LF,0
  78.  
  79. u2d_warn$ db    'Warning: '
  80.       db    'Unix file has CRs!',CR,LF,0    ;Unix -> DOS, but HAS CRs!
  81. d2u_warn$ db    'Warning: '
  82.       db    'DOS file has no CRs!',CR,LF,0    ;DOS -> Unix, but no CRs!
  83.  
  84. openErr$ db    'Can''t open target file',0    ;input file open err msg
  85. readErr$ db    'Input file read error',0    ;input file read err msg
  86. outErr$ db    'Output error',0        ;output write error msg
  87. switchErr$ db    'Unknown switch',0        ;error msg if not -r switch
  88. prompt$    db    'Continue? [Y/N]: ',0        ;prompt string
  89. abort$    db    'User abort',0            ;abort msg
  90.  
  91. CrLf    endp
  92.  
  93.  
  94. Start    proc    near
  95.  
  96.     call    Parse_CmdLine        ;get any switches,
  97.                     ;prepare target filename
  98.     jc    Msg_Exit        ;no action, DX -> error msg
  99.  
  100. ;else DX -> target filename's first character
  101.  
  102.     mov    ax,3D00H        ;open file, read only
  103.     int    21H
  104.     mov    dx,offset openErr$    ;'Can't open target file'
  105.     jc    Msg_Exit        ;open failed, die
  106.  
  107.     mov    handle,ax        ;save input file handle
  108.  
  109.     call    Test_Buffer        ;initial input file read,
  110.                     ;test for funny input file EOLs
  111.     jnc    Read_1            ;ok, skip the read/EOF test seq    v1.1
  112.  
  113.      jmp    short Read_Error    ;first read failed, or empty file v1.1
  114.  
  115. ;We loop here, processing each buffer full, until EOF or file error.
  116.  
  117. Read_Lup:
  118.     mov    si,offset INBUFF    ;DS:SI -> input buffer base
  119.  
  120.     cmp    eofFlag,FALSE        ;hit input EOF yet?
  121.     jnz    Flush            ;yep, flush any remaining processed
  122.                     ;chars, terminate.
  123.  
  124.     mov    dx,si            ;read into input buffer (DS:SI)
  125.     mov    cx,BUFFSIZE        ;try for a full buffer's worth
  126.     mov    bx,handle        ;input file handle
  127.     mov    ah,3FH            ;read from file/device
  128.     int    21H
  129.     jc    Read_Error        ;read failed
  130.  
  131.     or    ax,ax            ;read anything?
  132.     jz    Flush            ;nope, flush any remaining processed
  133.                     ;chars, terminate.
  134.  
  135.     cmp    ax,cx            ;read all we requested?
  136.     adc    eofFlag,0        ;will make flag non-0 if EOF
  137.     mov    cx,ax            ;CX = input buffer count
  138.  
  139. Read_1:
  140.     call    Process_Buff        ;convert input buffer EOLs
  141.     call    Write_Output        ;write output buffer bytes    v1.1
  142.     jnb    Read_Lup        ;went ok            v1.1
  143.     jmp    short Write_Error    ;CF means output write failed    v1.1
  144.  
  145. Flush:
  146.     call    Write_Output        ;write any output buffer bytes
  147.     jc    Write_Error        ;failed
  148.      xor    ax,ax            ;ERRORLEVEL=0
  149.      jmp    short Terminate
  150.  
  151.  
  152. Read_Error:
  153.     mov    dx,offset readErr$    ;'Input file read error'
  154.     jmp    short Msg_Exit        ;terminate
  155.  
  156. Write_Error:
  157.     mov    dx,offset outErr$    ;'Output error'
  158.                     ;fall thru to...
  159.  
  160. ;Come here with any messages in DX
  161. Msg_Exit:
  162.     push    ax            ;save any errors in AL
  163.     call    Write_StdErr        ;output to StdErr (console)
  164.     pop    ax
  165.  
  166. Terminate:
  167.     mov    ah,4CH            ;terminate (errorlevel in AL)
  168.     int    21H            ;we let DOS close the input file.
  169.  
  170. Start    endp
  171.  
  172.  
  173. ;--    Make initial test of input buffer.
  174. ;    Depending on type conversion, gives user a warning
  175. ;    if there are unexpected EOLs in the first bufferfull.
  176.  
  177. Test_Buffer    proc    near
  178.  
  179.     mov    dx,offset INBUFF    ;read into input buffer
  180.     mov    cx,BUFFSIZE        ;try for a full buffer's worth
  181.     mov    bx,handle        ;input file handle
  182.     mov    ah,3FH            ;read from file/device
  183.     int    21H
  184.     jc    TB_Ret            ;read failed, return CF set
  185.  
  186.     or    ax,ax            ;read anything?
  187.     jnz    TB_1            ;yep, continue
  188.      stc                ;zero contents ..
  189.      ret                ;  return CF set
  190.  
  191. TB_1:
  192.     cmp    ax,cx            ;read all we requested?
  193.     adc    eofFlag,0        ;will make flag non-0 if EOF
  194.     mov    cx,ax            ;CX = input buffer count
  195.  
  196.     push    cx            ;save buffer size
  197.     mov    al,CR            ;scan for CRs
  198.     mov    di,dx    ;offset INBUFF    ;input buffer start
  199.     mov    si,dx    ;offset INBUFF    ;may as well prepare SI
  200.     repne    scasb            ;look for a CR
  201.     pop    cx            ;restore
  202.     pushf                ;save those results
  203.  
  204.  
  205.     cmp    addCr,FALSE        ;adding CRs?  (Unix -> DOS)
  206.     jz    TB_D2U            ;nope, DOS -> Unix
  207.  
  208. ;We're doing Unix -> DOS.
  209. ;If there's a CR in the input buffer, maybe this is NOT a Unix file!
  210. ;Warn the user.
  211.  
  212.     mov    dx,offset u2d_warn$    ;'Warning: Unix file has CRs!'
  213.     popf                ;restore the scasb flag
  214.     jnz    TB_Ok            ;no CRs, ok
  215.     jmp    short TB_Warn        ;there WAS a CR.
  216.                     ;Display warning msg, return
  217.  
  218. ;We're doing DOS -> Unix.
  219. ;If there are no CRs in the input buffer, maybe this is NOT a DOS file!
  220. ;Warn the user.
  221.  
  222. TB_D2U:
  223.     mov    dx,offset d2u_warn$    ;'Warning: DOS file has no CRs!'
  224.     popf                ;restore the scasb flag
  225.     jz    TB_Ok            ;there WAS a CR, ok.
  226.  
  227. ;Common warning routine for both conversion modes
  228. TB_Warn:
  229.     call    Write_StdErr        ;display warning msg
  230.     mov    dx,offset prompt$    ;'Continue? [Y/N]: '
  231.     call    Write_StdErr        ;display prompt
  232.     mov    ax,0C08H        ;clear kbd, kbd input w/o echo
  233.     int    21H
  234.     and    al,5FH            ;uppercase response
  235.     cmp    al,'Y'            ;Yes, continue?
  236.     jz    TB_Ok            ;yep
  237.      pop    ax            ;clear the call
  238.      mov    al,1            ;ERRORLEVEL 1
  239.      mov    dx,offset abort$    ;'User abort'
  240.      jmp    Msg_Exit        ;display, terminate
  241.  
  242. TB_Ok:
  243.     mov    di,offset OUTBUFF    ;ES:DI -> output buffer base
  244.     clc                ;but return CF clear
  245. TB_Ret:
  246.     ret
  247.  
  248. Test_Buffer    endp
  249.  
  250.  
  251. ;--    Tests type conversion, jumps to appropriate conversion procedure.
  252.  
  253. Process_Buff    proc    near
  254.  
  255.     mov    bx,offset Unix_To_Dos    ;assume Unix -> DOS conversion
  256.     cmp    addcr,TRUE        ;adding CRs?  (Unix -> DOS)
  257.     jz    PB_Jump            ;yep
  258.      mov    bx,offset Dos_To_Unix    ;nope, CR/LF to LF conversion
  259. PB_Jump:
  260.     jmp    bx            ;return from whichever procedure
  261.  
  262. Process_Buff    endp
  263.  
  264.  
  265. ;--    Converts DOS CR/LF EOLs to Unix-style EOLs (LF)
  266. ;    DS:SI -> input buffer start
  267. ;    ES:DI -> next free output buffer byte
  268. ;    CX = bytes read (e.g., size of input buffer)
  269. ;    Destroys most everything
  270. ;    Preserves DI (output buffer pointer)
  271. ;    Removed output buffer overrun testing at every byte.
  272.  
  273. Dos_To_Unix    proc    near
  274.  
  275.     mov    ah,CR            ;handy constant
  276.  
  277. D2U_Lup:
  278.     lodsb                ;snarf input byte
  279.     cmp    al,ah    ;CR        ;DOS EOL first char?
  280.     jz    D2U_Relup        ;yep, gobble that CR
  281.      stosb                ;stuff normal char or LF
  282. D2U_Relup:
  283.     loop    D2U_Lup            ;do all the input characters.
  284.     ret
  285.  
  286. Dos_To_Unix    endp
  287.  
  288.  
  289. ;--    Converts Unix-style EOLs (LF) to normal DOS CR/LF EOL
  290. ;    DS:SI -> input buffer start
  291. ;    ES:DI -> next free output buffer byte
  292. ;    CX = bytes read (e.g., size of input buffer)
  293. ;    Destroys most everything.
  294. ;    Preserves DI
  295. ;v1.1    Added CR/LF word stuffing.  Some code is redundant,
  296. ;    but this maximizes speed at minimal code increase.
  297. ;    Removed output buffer overrun testing at every byte.
  298.  
  299. Unix_To_Dos    proc    near
  300.  
  301.     mov    ah,LF            ;handy constant
  302.  
  303. U2D_Lup:
  304.     lodsb                ;snarf input byte
  305.     cmp    al,ah    ;LF        ;Unix EOL?
  306.     jz    U2D_EOL            ;yep
  307.      stosb                ;stuff normal char
  308.      loop    U2D_Lup            ;reloop
  309.      ret
  310.  
  311. U2D_EOL:
  312.     mov    al,CR            ;stuff CR/LF
  313.     stosw                ;as a word
  314.     loop    U2D_Lup            ;redundant code, but faster
  315.     ret
  316.  
  317. Unix_To_Dos    endp
  318.  
  319.  
  320. ;--    Write output buffer to StdOut
  321. ;    Output buffer size may be bigger (Unix --> DOS)
  322. ;    or smaller (DOS --> Unix) than input buffer size.
  323. ;    Return CF set if error (with error in AX)
  324. ;    Destroys AX,BX,DX
  325. ;    Returns DI -> output buffer start
  326.  
  327. Write_Output    proc    near
  328.  
  329.     mov    dx,offset OUTBUFF    ;output buffer start
  330.     mov    ax,di            ;output buffer's last byte+1
  331.     sub    ax,dx            ;last byte (+1) - start=bytes to write
  332.     ja    Write_Out1        ;ok, we have output to write
  333.      xor    ax,ax            ;nothing to write
  334.      clc                ;insure CF clear
  335.      ret
  336.  
  337. Write_Out1:
  338.     push    cx            ;preserve CX
  339.     mov    cx,ax            ;bytes to write
  340.     mov    bx,STDOUT        ;output to StdOut
  341.     mov    ah,40H            ;write to file/device
  342.     int    21H
  343.     pop    cx            ;restore CX
  344.     mov    di,dx            ;ES:DI -> output buffer start
  345.     ret                ;CF set if write error
  346.  
  347. Write_Output    endp
  348.  
  349.  
  350. ;--    Enter with DS:DX -> AsciiZ message.
  351. ;    Writes msg to StdErr
  352. ;    Destroys AX,BX
  353.  
  354. Write_StdErr    proc    near
  355.  
  356.     push    di
  357.     push    cx
  358.  
  359.     xor    al,al            ;scan for AsciiZ 0
  360.     mov    cx,0FFFFH        ;max scan
  361.     mov    di,dx            ;ES:DI -> message's first char
  362.     repne    scasb            ;find AsciiZ 0
  363.     not    cx            ;flip, CX = msg length
  364.  
  365.     mov    bx,STDERR        ;write to StdErr
  366.     mov    ah,40H            ;write to file/device
  367.     int    21H
  368.  
  369.     pop    cx
  370.     pop    di
  371.     ret
  372.  
  373. Write_StdErr    endp
  374.  
  375.  
  376. ;Runtime file buffers start here,
  377. ;and will overwrite startup code (Parse_CmdLine)
  378. ;and usage message.
  379.  
  380.     EVEN                    ;make it easy for 8086 family
  381.  
  382. INBUFF        label    byte            ;input buffer start
  383. OUTBUFF        EQU    INBUFF + BUFFSIZE    ;output buffer start
  384. ;v1.1    OUTBUFF has about BUFFSIZE*2 bytes to play with.
  385. ;OUTBUFFEND    EQU    OUTBUFF+BUFFSIZE    ;mark output buffer end
  386.  
  387. usage$    db 'CRLF v1.1 - Convert Unix LF line endings to DOS CR/LF endings.'
  388.     db CR,LF
  389.     db 'Usage:  CRLF [-r] filename.typ [>output]',CR,LF
  390.     db 'Where',CR,LF
  391.     db ' -r              reverses the operation (CR/LF to LF)',CR,LF
  392.     db ' filename.typ    is the target filename',CR,LF
  393.     db 'Default output is to STDOUT (redirect to any file/device).'
  394.     db CR,LF,0
  395.  
  396.  
  397. ;--    Parse PSP command line for -r switch and target filename.
  398. ;    Return CF set if errors, no output, whatever.
  399.  
  400. Parse_CmdLine    proc    near
  401.  
  402.     mov    si,offset cmdline    ;PSP cmdline length byte
  403.     xor    ah,ah            ;clear msb
  404.     lodsb                ;snarf length byte
  405.     mov    cx,ax            ;CX=cmdline length
  406.     mov    dx,offset usage$    ;assume no cmdline
  407.     jcxz    PC_Bad            ;return CF set
  408.  
  409.     call    Next_Char        ;gobble any spaces, tabs
  410.     jcxz    PC_Bad            ;went illegal
  411.  
  412. ;AL = first real cmdline char
  413. ;SI -> next cmdline char
  414. ;CX = remaining cmdline length
  415.  
  416.     cmp    al,'-'            ;got a switch?
  417.     jz    PC_Switch        ;yep
  418.     cmp    al,'/'            ;be nice, test for other switch
  419.     jz    PC_Switch
  420.     cmp    al,'?'            ;asking for help?
  421.     jz    PC_Bad            ;yep, DX -> usage msg
  422.     jmp    short PC_FileName    ;should be target filename's first char
  423.  
  424. ;We got a switch
  425. PC_Switch:
  426.     call    Next_Char        ;get next char
  427.     jcxz    PC_Bad            ;usage, die
  428.  
  429.     mov    dx,offset switchErr$    ;'Unknown switch'
  430.     and    al,5FH            ;uppercase
  431.     cmp    al,'R'            ;we only take 'R' switches for now
  432.     jnz    PC_Bad            ;bad
  433.  
  434.     mov    dx,offset usage$    ;if no filename, usage msg
  435.     not    addCr            ;flip flag to CR/LF -> LF conversion
  436.     call    Next_Char        ;filename should be next
  437.     jcxz    PC_Bad
  438.  
  439. PC_FileName:
  440.     dec    si            ;back up to filename's first char
  441.     mov    dx,si            ;remember in DX
  442.     mov    cx,80H            ;should be long enough!
  443.     mov    ah,CR            ;look for terminating CR
  444. PC_FNLup:
  445.     lodsb                ;snarf next char
  446.     cmp    al,ah    ;CR        ;hit CR?
  447.     jnz    PC_FNLup        ;nope
  448.     dec    si            ;back up to the CR
  449.     mov    byte ptr [si],0        ;AsciiZe it
  450.     
  451.     call    Write_StdErr        ;display filename
  452.     push    dx            ;save filename ptr
  453.     mov    dx,offset unix$        ;assume 'Unix -> DOS' EOL conversion
  454.     cmp    addCr,LOW(TRUE)        ;true?
  455.     jz    PC_1            ;yep
  456.      mov    dx,offset dos$        ;'DOS -> Unix'
  457. PC_1:
  458.     call    Write_StdErr        ;display msg
  459.     pop    dx            ;restore filename ptr
  460.     clc                ;return CF clear
  461.     ret
  462.     
  463. PC_Bad:
  464.     stc                ;return CF set for failure
  465.     ret                ;DX -> error msg
  466.  
  467.  
  468. ;Parse_CmdLine subroutine
  469.  
  470. Next_Char:
  471.     jcxz    NC_Ret            ;cmdline zeroed out, return
  472.  
  473. NC_Lup:
  474.     lodsb                ;snarf cmdline char
  475.     cmp    al,' '            ;space?
  476.     jz    NC_ReLup        ;yep, gobble
  477.     cmp    al,9            ;tab?
  478.     jz    NC_ReLup        ;yep, gobble
  479.     cmp    al,CR            ;CR terminates
  480.     jnz    NC_Ret            ;normal char, return
  481.  
  482. NC_ReLup:
  483.     loop    NC_Lup
  484. NC_Ret:
  485.     ret
  486.  
  487.  
  488. Parse_CmdLine    endp
  489.  
  490. CSEG    ENDS
  491.     END    CrLf
  492.