'===========================================================================
' Subject: LONG FILENAMES FROM DOS DEMO       Date: 04-18-98 (18:51)       
'  Author: Alex Warren                        Code: QB, PDS                
'  Origin: comp.lang.basic.misc             Packet: DOS.ABC
'===========================================================================
'Quite a few people have asked how long filenames are used from DOS in the last
'few months - below is some DOS BASIC code that will show you how! It's released
'into the public domain so feel free to use for whatever purpose you see fit.

'The program doesn't show you *everything* you can do with long filenames but it
'should be useful enough that you can modify it fairly easily with information
'from Ralf Brown's Interrupts List (address in the program).

'If you're lucky (actually, if *I* have the time) I'll write an article to
'explain the program (though that might not be necessary, the comments should
'explain all you need to know).

'I claim no responsibility for anything this program does - or doesn't - do.

'The code requires QuickBasic 4.5 or better (maybe worse, I dunno) - make sure
'you start it with /L
'---


' LONG FILENAMES FROM DOS DEMONSTRATION v1.0
' by Alex Warren, 18th April 1998
' Released into the Public Domain

' This program is intended to show you how to use the long filename
' functions. It's not particularly neat but it should be good enough to
' show you how to incorporate long filenames in your applications without
' having to use the incredibly messy method of using SHELL.

' If you want to save or open files with long file names, you could save
' using a short filename using OPEN and then rename the file using the
' rename interrupt (int 21h, service 7156h - check Ralf Brown's Interrupts
' List at www.ctyme.com/rbrown.htm), open by retrieving the equivalent
' short filename (see the showdirectory sub)

DECLARE SUB setdirectory ()
DECLARE SUB getfileattribs ()
DECLARE SUB showdirectory ()
DECLARE SUB createfile ()
DECLARE SUB createdirectory ()
DECLARE SUB getvolumeinfo (drive$)
DECLARE FUNCTION truncate$ (s$)
DECLARE FUNCTION getcurrentdir$ ()
DECLARE SUB int21 ()
'$INCLUDE: 'qb.bi'
DIM SHARED inregs AS RegTypeX, outregs AS RegTypeX

DIM SHARED filesystem$, casesensitive%, preservecase%, unicode%, lfnfunctions%, compressed%

CLS

PRINT "Long Filenames from DOS Demonstration, by Alex Warren"
PRINT "version 1.0, 18th April 1998"
PRINT "Released into the Public Domain"
PRINT

getvolumeinfo ("C:\")

PRINT "File system is: "; filesystem$
PRINT
PRINT "Searches are case sensitive:"; casesensitive%
PRINT "Preserves case in directory entries:"; preservecase%
PRINT "Uses unicode chars in file and dir names:"; unicode%
PRINT "Supports DOS LFN functions:"; lfnfunctions%
PRINT "Compressed volume:"; compressed%
DO
PRINT "---"
PRINT
PRINT "Current directory is "; getcurrentdir$
PRINT
PRINT "Choose:"
PRINT "1: Create directory; 2: Set current directory; 3: Show directory;"
PRINT "4: Create file; 5: Change drive; 6: Exit"

' Not included but worth investigating are:
' - Remove directory (service 713Ah)
' - Delete file (7141h)
' - Move/Rename file (7156h)
' - Get/Set file attributes (7143h)
' +++ others on Ralf Brown's Interrupts List

DO: a$ = INPUT$(1): LOOP UNTIL a$ >= "1" AND a$ <= "6"
selected = VAL(a$)

SELECT CASE selected
	CASE 1
		createdirectory
	CASE 2
		setdirectory
	CASE 3
		showdirectory
	CASE 4
		createfile
	CASE 5
		INPUT "Enter drive letter: ", driveletter$
		driveletter$ = driveletter$ + ":"
		SHELL driveletter$
	CASE 6
		END
END SELECT

LOOP

SUB createdirectory

DIM pathname AS STRING * 255

INPUT "Enter FULL path of sub-directory to create: "; p$
pathname = p$

' inregs:
' .ax: service - 7139h
' .ds: segment of pathname
' .dx: offset of pathname

inregs.ax = &H7139
inregs.ds = VARSEG(pathname)
inregs.dx = VARPTR(pathname)
int21

END SUB

SUB createfile
END SUB

FUNCTION getcurrentdir$

DIM thedir AS STRING * 255

' inregs:
' .ax: service - 7147h
' .dx: 0=default, 1=A:, 2=B:, 3=C:, 4=D: etc.
' .ds: segment of buffer
' .si: offset of buffer

inregs.dx = &H0
inregs.ax = &H7147
inregs.ds = VARSEG(thedir)
inregs.si = VARPTR(thedir)
int21

' This line truncates thedir up to the first null character:
d$ = truncate$(thedir)

' This line adds the backslash if not present, as the backslash may or may
' not be present already. If you don't want the backslash there ever, modify
' this line:
IF RIGHT$(d$, 1) <> "\" THEN d$ = d$ + "\"

getcurrentdir$ = d$

END FUNCTION

SUB getvolumeinfo (drive$)

' inregs:
' .ax: service - 71A0h
' .ds: segment of rootname
' .dx: offset of rootname
' .es: segment of buffer for file system name
' .di: offset of buffer for file system name
' .cx: size of file system name buffer

DIM rootname AS STRING * 4
DIM filesysname AS STRING * 255
rootname = drive$ + CHR$(0)

inregs.ax = &H71A0
inregs.ds = VARSEG(rootname)
inregs.dx = VARPTR(rootname)
inregs.es = VARSEG(filesysname)
inregs.di = VARPTR(filesysname)
inregs.cx = 255
int21

IF outregs.ax = &H7100 THEN
	BEEP
	PRINT "LFN functions are not supported; exiting program..."
	END
END IF

filesystem$ = truncate$(filesysname)
IF outregs.bx AND 2 ^ 0 THEN casesensitive% = 1 ELSE casesensitive% = 0
IF outregs.bx AND 2 ^ 1 THEN preservecase% = 1 ELSE preservecase% = 0
IF outregs.bx AND 2 ^ 2 THEN unicode% = 1 ELSE unicode% = 0
IF outregs.bx AND 2 ^ 14 THEN lfnfunctions% = 1 ELSE lfnfunctions% = 0
IF outregs.bx AND 2 ^ 15 THEN compressed% = 1 ELSE compressed% = 0

END SUB

SUB int21

CALL INTERRUPTX(&H21, inregs, outregs)

END SUB

SUB setdirectory

DIM pathname AS STRING * 255

INPUT "Enter FULL path of sub-directory: "; p$
pathname = p$

' inregs:
' .ax: service - 713Bh
' .ds: segment of pathname
' .dx: offset of pathname

inregs.ax = &H713B
inregs.ds = VARSEG(pathname)
inregs.dx = VARPTR(pathname)
int21


END SUB

SUB showdirectory

quitloop = 0

' What's done:
' first, call 714Eh to find first matching file, then call 714Fh to find
' next matching file and so on. Use 71A1h to stop the search.

' Attributes masks:
' Bit: 4 = directory
'      3 = volume label
'      2 = system
'      1 = hidden
'      0 = read-only

' ax=&h714E (service)
' ds:dx = filespec, e.g. *.*
' es:di = finddata record (see below)
' cl = allowable attributes mask, set here to FF so ALL attributes allowed
' ch = required attributes mask, set here to 00 so NO attributes required

DIM finddatarecord AS STRING * 500
DIM filespec AS STRING * 4

filespec = "*.*" + CHR$(0)

inregs.ax = &H714E
inregs.ds = VARSEG(filespec)
inregs.dx = VARPTR(filespec)
inregs.es = VARSEG(finddatarecord)
inregs.di = VARPTR(finddatarecord)
inregs.cx = &HFF
inregs.si = &H0
int21
filefindhandle = outregs.ax

numfiles = 1

DO

' finddatarecord$ is now filled with loads of information about the file
' we've retrieved (info from Ralf Brown's Interrupts List):
' offset 00h bits 0-6 are standard DOS file attribs
' 04h QWORD file creation time
' 0Ch QWORD last access time
' 14h QWORD last modification time
' 1Ch DWORD file size (high 32 bits)
' 20h DWORD file size (low 32 bits)
' 2Ch 260bytes ASCIZ full filename
' 130h 14bytes ASCIZ short filename

' If required, you could interpret all that stuff but we're only interested
' in the filenames for now:

longfilename$ = truncate$(MID$(finddatarecord, &H2D, 260))
shortfilename$ = truncate$(MID$(finddatarecord, &H131, 14))

' NOTE that shortfilename$ will contain nothing if the short filename
' is *exactly* the same as the long file name.

PRINT longfilename$; TAB(30); shortfilename$

' If longfilename$ is null, we've reached the end of the list.
IF longfilename$ = "" THEN PRINT "*** END OF LIST; PRESS A KEY ***": EXIT DO


' reset finddatarecord
finddatarecord = STRING$(500, 0)

inregs.ax = &H714F
inregs.bx = filefindhandle
int21

IF numfiles MOD 20 = 0 THEN
	COLOR 14
	PRINT ">> Press a key <<"
	COLOR 7
	a$ = INPUT$(1)
	IF a$ = CHR$(27) THEN quitloop = 1
END IF

numfiles = numfiles + 1

LOOP UNTIL quitloop = 1

IF quitloop = 0 THEN a$ = INPUT$(1)

' It's important to reset the filefind handle:
inregs.ax = &H71A1
inregs.bx = filefindhandle
int21

END SUB

FUNCTION truncate$ (s$)

' Truncates s$ to first null character

truncate$ = LEFT$(s$, INSTR(s$, CHR$(0)) - 1)
END FUNCTION
