Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Warning re SHELL "DIR filespec > tempfile"
#1
I was running a Quickbasic utility program that I wrote back in 1999. Under certain conditions the program needs to do:
SHELL "DIR filespec > tempfile"

Then it reads the tempfile and selects all the filenames only, not directory names, headings, etc.

Suddenly it's not working. After much scouting I discovered that a DIR under Windows XP produces a listing much different than the original DOS 6.22 that I was using at the time I wrote the program.

I don't know exactly when Windows changed to the new DIR format, but I did some testing and came up with a DIR command which will work for DOS 6.22 and Windows XP. The secret is using the /B switch which produces a "bare" listing of only the filenames (including the subdirectory names), and combining this with the /A-D which eliminates the subdirectory names, to get only the filenames. The new SHELL becomes:
SHELL "DIR filespec /B /A-D > tempfile"

A minor additional change in my program now picks up the filenames left-justified in each record of the tempfile.

So, if you've got an old program that does a DIR to get filenames, take another look at it if you're running a newer Windows.
*****
Reply
#2
Moneo:
Very good information! But, why not add a "sort by name" switch also? Then, your new code line would be:
Code:
SHELL "DIR filespec /B/O /A-D > tempfile"

And, for those who may not know how, to get all the data on any command.com command, use the /? switch, as well as the "show a page and pause" switch, /p, as in:

dir/?/p
Ralph, using QuickBASIC 4.5 and Windows XP Home Edition and Service Pack 2, with HP LaserJet 4L printer.
Reply
#3
Thanks for the good feedback, Ralph.

While I was testing using Windows XP, I discovered that it ALWAYS sorts the filenames by default.

Although it's a good idea, I wouldn't put in any more switches for sorting unless I really needed the file to be in that sequence.

Your other general suggestions about /? and /p are well taken.
*****
Reply
#4
Live and learn!

I tried the dir command with the switches, but without the /o, and, you are right, in my Win XP HE, the directories are shown sorted by name...However! When I tried this on 3 1/2" floppy disks, I had to use the /o switch to get the files sorted by name. More mysteries? Seems as if there is no standard way of saying something, without somebody finding an exception!
Ralph, using QuickBASIC 4.5 and Windows XP Home Edition and Service Pack 2, with HP LaserJet 4L printer.
Reply
#5
Relying on the command interpreter to do your dirty work is just asking for trouble imo...a DOS can have a different output for "DIR" and still be 100% "MS-DOS compatible", such as IBM PC-DOS. Additionally, command shells such as 4DOS change the behavior and output of built-in commands like DIR.

It's much better to use the DOS interrupt (21h) to do things like this; that's why it's there. It's faster, guaranteed to work, and requires no temporary files.
Reply
#6
Quote:Relying on the command interpreter to do your dirty work is just asking for trouble imo...a DOS can have a different output for "DIR" and still be 100% "MS-DOS compatible", such as IBM PC-DOS. Additionally, command shells such as 4DOS change the behavior and output of built-in commands like DIR.

It's much better to use the DOS interrupt (21h) to do things like this; that's why it's there. It's faster, guaranteed to work, and requires no temporary files.

I swear I was gonna post exactly this :o

http://spike.scu.edu.au/~barry/interrupts.html
SCUMM (the band) on Myspace!
ComputerEmuzone Games Studio
underBASIC, homegrown musicians
[img]http://www.ojodepez-fanzine.net/almacen/yoghourtslover.png[/i
Reply
#7
Plasma and Nathan,

The DOS interrupt 21h thingy sounds good, but for someone who has never used it, would you please post the equivalent code to perform the same as: DIR filespec /B /A-D
and be able to retrieve the DIR list of filenames.

Thanks in advance.
*****
Reply
#8
Code:
' DIR$ replacement by Plasma
' QuickBASIC 4.x version
' Remember to start QB with the /L switch: QB /L

DEFINT A-Z
'$INCLUDE: 'QB.BI'

DECLARE FUNCTION DIR2$ (FileSpec$, Attr)

CONST ANYDIR = &H10
CONST ANYFILE = &H27

' Examples:

' To check for the existence of a file:
' IF LEN(DIR2$("C:\AUTOEXEC.BAT", ANYFILE)) THEN
'   PRINT "The file exists"
' ELSE
'   PRINT "The file does not exist"
' END IF


' To check for the existence of a directory:
' IF LEN(DIR2$("C:\WINDOWS", ANYDIR)) THEN
'   PRINT "The directory exists"
' ELSE
'   PRINT "The directory does not exist"
' END IF


' To generate a list of files:
'
' n$ = DIR2$("C:\*.*", ANYFILE)
' DO WHILE LEN(n$)
'   PRINT n$
'   n$ = DIR2$("", ANYFILE)
' LOOP


' To generate a list of directories:
'
' n$ = DIR2$("C:\*.*", ANYDIR)
' DO WHILE LEN(n$)
'   PRINT n$
'   n$ = DIR2$("", ANYDIR)
' LOOP


FUNCTION DIR2$ (FileSpec$, Attr) STATIC

  '==========================================================================
  ' DIR$ replacement by Plasma (1.1)
  '--------------------------------------------------------------------------
  ' Settings for Attr: (may be combined)
  '
  ' &H40 Device
  ' &H20 Archive
  ' &H10 Directory
  ' &H8  Volume Label
  ' &H4  System File
  ' &H2  Hidden File
  ' &H1  Read-Only File
  '==========================================================================

  DIM DTA AS STRING * 44
  FileSpecZ$ = FileSpec$ + CHR$(0)

  DO

    DIM Regs AS RegTypeX

    Regs.ax = &H1A00
    Regs.ds = VARSEG(DTA)
    Regs.dx = VARPTR(DTA)
    InterruptX &H21, Regs, Regs

    IF FileSpecZ$ <> CHR$(0) THEN

      Regs.ax = &H4E00
      Regs.cx = Attr
      Regs.ds = VARSEG(FileSpecZ$)
      Regs.dx = SADD(FileSpecZ$)
    ELSE
      Regs.ax = &H4F00
    END IF

    InterruptX &H21, Regs, Regs

    IF Regs.flags AND 1 THEN
      DIR2$ = ""
      EXIT FUNCTION
    ELSE
      RealAttr = ASC(MID$(DTA, 22, 1))
      IF RealAttr AND Attr THEN
        IF Attr > &H27 AND NOT RealAttr AND &H10 THEN
          FileSpecZ$ = CHR$(0)
        ELSE
          Null = INSTR(31, DTA, CHR$(0))
          DIR2$ = MID$(DTA, 31, Null - 31)
          EXIT FUNCTION
        END IF
      ELSE
        FileSpecZ$ = CHR$(0)
      END IF
    END IF

  LOOP

END FUNCTION

Code:
' DIR$ replacement by Plasma
' QBASIC version

DEFINT A-Z

DECLARE FUNCTION DIR2$ (FileSpec$, Attr)
DECLARE SUB IntX (IntNum, InReg AS ANY, OutReg AS ANY)

TYPE RegTypeX
  ax AS INTEGER
  bx AS INTEGER
  cx AS INTEGER
  dx AS INTEGER
  bp AS INTEGER
  si AS INTEGER
  di AS INTEGER
  flags AS INTEGER
  ds AS INTEGER
  es AS INTEGER
END TYPE

CONST ANYDIR = &H10
CONST ANYFILE = &H27

' Examples:

' To check for the existence of a file:
' IF LEN(DIR2$("C:\AUTOEXEC.BAT", ANYFILE)) THEN
'   PRINT "The file exists"
' ELSE
'   PRINT "The file does not exist"
' END IF


' To check for the existence of a directory:
' IF LEN(DIR2$("C:\WINDOWS", ANYDIR)) THEN
'   PRINT "The directory exists"
' ELSE
'   PRINT "The directory does not exist"
' END IF


' To generate a list of files:
'
' n$ = DIR2$("C:\*.*", ANYFILE)
' DO WHILE LEN(n$)
'   PRINT n$
'   n$ = DIR2$("", ANYFILE)
' LOOP


' To generate a list of directories:
'
' n$ = DIR2$("C:\*.*", ANYDIR)
' DO WHILE LEN(n$)
'   PRINT n$
'   n$ = DIR2$("", ANYDIR)
' LOOP


FUNCTION DIR2$ (FileSpec$, Attr) STATIC

  '==========================================================================
  ' DIR$ replacement by Plasma (1.1)
  '--------------------------------------------------------------------------
  ' Settings for Attr: (may be combined)
  '
  ' &H40 Device
  ' &H20 Archive
  ' &H10 Directory
  ' &H8  Volume Label
  ' &H4  System File
  ' &H2  Hidden File
  ' &H1  Read-Only File
  '==========================================================================

  DIM DTA AS STRING * 44
  FileSpecZ$ = FileSpec$ + CHR$(0)

  DO

    DIM Regs AS RegTypeX

    Regs.ax = &H1A00
    Regs.ds = VARSEG(DTA)
    Regs.dx = VARPTR(DTA)
    IntX &H21, Regs, Regs

    IF FileSpecZ$ <> CHR$(0) THEN

      Regs.ax = &H4E00
      Regs.cx = Attr
      Regs.ds = VARSEG(FileSpecZ$)
      Regs.dx = SADD(FileSpecZ$)
    ELSE
      Regs.ax = &H4F00
    END IF

    IntX &H21, Regs, Regs

    IF Regs.flags AND 1 THEN
      DIR2$ = ""
      EXIT FUNCTION
    ELSE
      RealAttr = ASC(MID$(DTA, 22, 1))
      IF RealAttr AND Attr THEN
        IF Attr > &H27 AND NOT RealAttr AND &H10 THEN
          FileSpecZ$ = CHR$(0)
        ELSE
          Null = INSTR(31, DTA, CHR$(0))
          DIR2$ = MID$(DTA, 31, Null - 31)
          EXIT FUNCTION
        END IF
      ELSE
        FileSpecZ$ = CHR$(0)
      END IF
    END IF

  LOOP

END FUNCTION

SUB IntX (IntNum, InReg AS RegTypeX, OutReg AS RegTypeX)

  '==========================================================================
  ' IntX 1.0 by Plasma (4/29/2004)
  ' Use interrupts without CALL INTERRUPT or CALL ABSOLUTE
  '==========================================================================

  STATIC Version, VarptrSeg&, VarptrOff, IntAsm(), IntOff

  IF VarptrSeg& = 0 THEN

    DefSeg& = VARSEG(DefSeg$)
    VarptrSeg& = DefSeg& - &H400
    DO WHILE VarptrSeg& > 0
      DEF SEG = VarptrSeg&
      FOR i = &H9 TO &H3FF3
        IF PEEK(i) = &H87 AND PEEK(i + 1) = &HDA AND PEEK(i + 2) = &H8B THEN
          IF PEEK(i + 3) = &H46 AND PEEK(i + 4) = &H6 AND PEEK(i + 5) = &H88 THEN
          IF PEEK(i + 6) = &H7 AND PEEK(i + 7) = &H8B AND PEEK(i + 8) = &H46 THEN
          IF PEEK(i + 9) = &H8 AND PEEK(i + 10) = &H89 AND PEEK(i + 11) = &H47 THEN
          IF PEEK(i + 12) = &H1 AND PEEK(i + 13) = &H92 THEN
            VarptrOff = i - 9
            Version = 45
            EXIT DO
          END IF
          END IF
          END IF
          END IF
        ELSEIF PEEK(i) = &HB9 AND PEEK(i + 1) = &H3 AND PEEK(i + 2) = &H0 THEN
          IF PEEK(i + 6) = &H8D AND PEEK(i + 7) = &H76 AND PEEK(i + 8) = &H6 THEN
          IF PEEK(i + 9) = &HA4 AND PEEK(i + 10) = &H46 AND PEEK(i + 11) = &HA5 THEN
            VarptrOff = i + 12
            Version = 71
            EXIT DO
          END IF
          END IF
        END IF
      NEXT
      VarptrSeg& = VarptrSeg& - &H3F0
    LOOP
    IF i = &H3FF4 THEN
      VarptrSeg& = 0
      EXIT SUB
    END IF

    IntAsm$ = "8E1F1E8B060E0050A100008B1E02008B0E04008B1606008B2E08008B360A"
    IntAsm$ = IntAsm$ + "008B3E0C008E0612008E1E10009DCD001E53506658665B9C66"
    IntAsm$ = IntAsm$ + "C1CB108EDB66C1EB10891E1000A3000066C1E8108906020089"
    IntAsm$ = IntAsm$ + "0E040089160600892E080089360A00893E0C005889060E008C"
    IntAsm$ = IntAsm$ + "0612009D071F61"
    IF Version = 45 THEN
      IntAsm$ = "5589E5601E069C8B5E08" + IntAsm$ + "5D31C0CA0400"
      IntOff = 55
    ELSE
      IntAsm$ = "601E069C4F4F268B1D" + IntAsm$ + "5F5E5DCA0400"
      IntOff = 54
    END IF

    DIM IntAsm(LEN(IntAsm$) / 4)
    DEF SEG = VARSEG(IntAsm(0))
    FOR i = 1 TO LEN(IntAsm$) STEP 2
      POKE (i - 1) / 2, VAL("&H" + MID$(IntAsm$, i, 2))
    NEXT

  END IF

  DEF SEG = VarptrSeg&
  POKE VarptrOff + 0, &HEA
  POKE VarptrOff + 1, 0
  POKE VarptrOff + 2, 0
  POKE VarptrOff + 3, VARSEG(IntAsm(0)) AND &HFF
  POKE VarptrOff + 4, (VARSEG(IntAsm(0)) AND &HFF00&) \ &H100

  DEF SEG = VARSEG(IntAsm(0))
  POKE IntOff, IntNum

  DIM Regs(10)
  Regs(0) = InReg.ax
  Regs(1) = InReg.bx
  Regs(2) = InReg.cx
  Regs(3) = InReg.dx
  Regs(4) = InReg.bp
  Regs(5) = InReg.si
  Regs(6) = InReg.di
  Regs(7) = InReg.flags
  Regs(8) = InReg.ds
  Regs(9) = InReg.es
  RegSeg = VARSEG(Regs(0))

  rc$ = VARPTR$(RegSeg)

  OutReg.ax = Regs(0)
  OutReg.bx = Regs(1)
  OutReg.cx = Regs(2)
  OutReg.dx = Regs(3)
  OutReg.bp = Regs(4)
  OutReg.si = Regs(5)
  OutReg.di = Regs(6)
  OutReg.flags = Regs(7)
  OutReg.ds = Regs(8)
  OutReg.es = Regs(9)

  DEF SEG = VarptrSeg&
  IF Version = 45 THEN
    POKE VarptrOff + 0, &H55
    POKE VarptrOff + 1, &H8B
    POKE VarptrOff + 2, &HEC
    POKE VarptrOff + 3, &HBB
    POKE VarptrOff + 4, &H3
  ELSE
    POKE VarptrOff + 0, &H5F
    POKE VarptrOff + 1, &H5E
    POKE VarptrOff + 2, &H5D
    POKE VarptrOff + 3, &HCA
    POKE VarptrOff + 4, &H4
  END IF

END SUB
Reply
#9
Wow, Plasma, thanks.

It looks complicated. I'll have to study it more.
BTW, where does the list of filenames go?
*****
Reply
#10
It's not really as bad as it looks. The QBasic version is more complicated because there is no call interrupt. (I could have just used call absolute, but I made that version for somebody at qbasic.com who didn't want to use call absolute or something...so basically IntX is just a call interrupt hack.)

Anyway, it works by setting the DTA to a local buffer, and then calling FindFirst and FindNext with the appropriate filename and attributes. This is how nearly all "professional" DOS applications generate their directory listings.

The list of filenames/directories isn't stored anywhere; it's generated by DOS as you request the next entry. If you want to store the list somewhere, you can always stick it in an array.
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)