Pastey IsUsbDevice with pack

--win api, show free space and info on devices.  
-- cut back features & docs for debugging. 
-- for testing, there should be some USB storage drive installed. 
-- 
-- IsUsbDevice 
-- http://banderlogi.blogspot.com/2011/06/enum-drive-letters-attached-for-usb.html 
-- http://www.codeproject.com/KB/winsdk/usbdisks.aspx 
-- 
-- seems to have struct access or dll version conlict 
-- memstruct STORAGE_PROPERTY_QUERY with pack 4 
--	double AdditionalParameters[1] works 
--	UCHAR AdditionalParameters[1] fails when accessing the struct 
--	UCHAR AdditionalParameters[4] fails, until recently was silent exit. 
--	UCHAR AdditionalParameters[1+3] syntax or other error. bug. 
--  see --FIXME   
-- 
-- pack doesn't seem to work or at least is the same without pack 
-- since UCHAR AdditionalParameters[4] also doesn't work 
-- I can't be sure it's related to packing. 
-- comments on various forum suggest ddk structs are pack sensitive. 
-- 
-- should not be getting BusTypeUnknown on some of my drives. 
-- I know it needs more work to determine removable from floppy. 
-- other fixed disk and cd/dvd are not marked as USB and should be. 
-- may have to reconvert back to c to find out if it's buggy there too 
 
   
include std/dll.e 
include std/machine.e 
include std/io.e 
include std/filesys.e 
include std/memstruct/windows.e 
include std/math.e 
include std/map.e 
 
 
atom BYTE_STANDARD = 1024000		--A.G.  
 
--paste in or include winerror reporting module 
ifdef WINDOWS then 
	include std/dll.e 
	include std/error.e 
	include std/io.e 
	include std/machine.e 
	include std/console.e 
 
	constant  
	kernel32 = dll:open_dll("kernel32.dll") 
	,xOutputDebugString = dll:define_c_proc(kernel32 
		,"OutputDebugStringA",{dll:C_POINTER}) 
	,xSetLastError = dll:define_c_proc(kernel32 
		, "SetLastError", {dll:C_DWORD}), 
	FORMAT_MESSAGE_ALLOCATE_BUFFER = #00000100, 
	FORMAT_MESSAGE_IGNORE_INSERTS = #00000200, 
	FORMAT_MESSAGE_FROM_STRING = #00000400, 
	FORMAT_MESSAGE_FROM_HMODULE = #00000800, 
	FORMAT_MESSAGE_FROM_SYSTEM = #00001000, 
	FORMAT_MESSAGE_ARGUMENT_ARRAY = #00002000, 
	FORMAT_MESSAGE_MAX_WIDTH_MASK = #000000FF 
	,xSetErrMode = dll:define_c_proc(kernel32 
		, "SetErrMode", {dll:C_DWORD}) 
 ,SEM_FAILCRITICALERRORS = #0001 
 ,SEM_NOGPFAULTERRORBOX = #0002 
 ,SEM_NOALIGNMENTFAULTEXCEPT = #0004 
 ,SEM_NOOPENFILEERRORBOX = #8000 
 
	if kernel32 = dll:NULL then 
		crash("kernel32 or dll not found") 
	end if 
 
 
procedure OutputDebugString(sequence text) 
	ifdef WINDOWS then  
		atom lptext = machine:allocate_string(text,1) 
		c_proc(xOutputDebugString,{lptext}) 
	elsedef -- 
		printf(2,"Db, %s ",{text}) 
	end ifdef 
end procedure  
 
public function OutputDebugStringf(sequence text) 
 
	OutputDebugString(text) 
	return sprintf("%s",{text}) 
end function 
 
 
procedure log_file(object fn, sequence lines, sequence mode="a") 
	object rtn 
	 
	if eu:compare(mode, "a")=0 then 
		rtn = io:append_lines(fn, lines) 
	else 
		rtn = io:write_lines(fn, lines) 
	end if 
	if rtn then 
		--can't write? 
	end if 
end procedure 
 
function GetLastError() 
	integer cGetLastError =  
		define_c_func(kernel32, "GetLastError", {}, C_DWORD) 
 
   return c_func(cGetLastError,{}) 
 
end function 
 
function FormatMessage(atom dwFlags, atom lpSource 
				, atom dwMessageId, atom dwLanguageId 
			,object lpBuffer, atom nSize, atom Arguments) 
 
  integer xFormatMessage = define_c_func(kernel32, "FormatMessageA" 
		,{C_DWORD, C_POINTER, C_DWORD, C_DWORD 
			,C_POINTER, C_DWORD, C_POINTER}, C_DWORD) 
 
	if sequence(lpBuffer) then 
		lpBuffer = allocate_string(lpBuffer, 1)  
	end if 
 
   return c_func(xFormatMessage 
		,{dwFlags,lpSource,dwMessageId,dwLanguageId 
			,lpBuffer,nSize, Arguments}) 
end function 
 
function get_winerror_string(atom eror = GetLastError() ) 
	 
	integer MSGSIZE = 1022 
	atom lpMsgBuf = allocate(MSGSIZE+2, 1) 
	FormatMessage( 
  		or_all({ 
  				FORMAT_MESSAGE_FROM_SYSTEM,  
  				FORMAT_MESSAGE_IGNORE_INSERTS}), 
  		NULL, 
  		eror, 
  		0, -- Default language 
  		lpMsgBuf,  --NULL 
  		MSGSIZE, --0 
  		NULL ) 
 
	return sprintf("0x%x %s", {eror, peek_string(lpMsgBuf)}) 
end function 
 
OutputDebugString("eu4 Dbg ") 
end ifdef	--error reporting 
  
 
public constant  
	DRIVE_UNKNOWN = 0, 
	DRIVE_NO_ROOT_DIR = 1, 
	DRIVE_REMOVABLE = 2, 
	DRIVE_FIXED = 3, 
	DRIVE_REMOTE = 4, 
	DRIVE_CDROM = 5, 
	DRIVE_RAMDISK = 6, 
	$ 
 
public constant		--C.B.  
 WRITE_WATCH_FLAG_RESET = #01      
 ,FILE_READ_DATA = #0001     
 ,FILE_LIST_DIRECTORY = #0001     
 ,FILE_WRITE_DATA = #0002     
 ,FILE_ADD_FILE = #0002     
 ,FILE_APPEND_DATA = #0004     
 ,FILE_ADD_SUBDIRECTORY = #0004     
 ,FILE_CREATE_PIPE_INSTANCE = #0004     
 ,FILE_READ_EA = #0008     
 ,FILE_WRITE_EA = #0010     
 ,FILE_EXECUTE = #0020     
 ,FILE_TRAVERSE = #0020     
 ,FILE_DELETE_CHILD = #0040     
 ,FILE_READ_ATTRIBUTES = #0080     
 ,FILE_WRITE_ATTRIBUTES = #0100     
 
 
 ,FILE_SHARE_READ = #00000001   
 ,FILE_SHARE_WRITE = #00000002   
 ,FILE_SHARE_DELETE = #00000004   
 ,FILE_ATTRIBUTE_READONLY = #00000001   
 ,FILE_ATTRIBUTE_HIDDEN = #00000002   
 ,FILE_ATTRIBUTE_SYSTEM = #00000004   
 ,FILE_ATTRIBUTE_DIRECTORY = #00000010   
 ,FILE_ATTRIBUTE_ARCHIVE = #00000020   
 ,FILE_ATTRIBUTE_DEVICE = #00000040   
 ,FILE_ATTRIBUTE_NORMAL = #00000080   
 ,FILE_ATTRIBUTE_TEMPORARY = #00000100   
 ,FILE_ATTRIBUTE_SPARSE_FILE = #00000200   
 ,FILE_ATTRIBUTE_REPARSE_POINT = #00000400   
 ,FILE_ATTRIBUTE_COMPRESSED = #00000800   
 ,FILE_ATTRIBUTE_OFFLINE = #00001000   
 ,FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = #00002000   
 ,FILE_ATTRIBUTE_ENCRYPTED = #00004000   
 ,FILE_NOTIFY_CHANGE_FILE_NAME = #00000001    
 ,FILE_NOTIFY_CHANGE_DIR_NAME = #00000002    
 ,FILE_NOTIFY_CHANGE_ATTRIBUTES = #00000004    
 ,FILE_NOTIFY_CHANGE_SIZE = #00000008    
 ,FILE_NOTIFY_CHANGE_LAST_WRITE = #00000010    
 ,FILE_NOTIFY_CHANGE_LAST_ACCESS = #00000020    
 ,FILE_NOTIFY_CHANGE_CREATION = #00000040    
 ,FILE_NOTIFY_CHANGE_SECURITY = #00000100    
 ,FILE_ACTION_ADDED = #00000001    
 ,FILE_ACTION_REMOVED = #00000002    
 ,FILE_ACTION_MODIFIED = #00000003    
 ,FILE_ACTION_RENAMED_OLD_NAME = #00000004    
 ,FILE_ACTION_RENAMED_NEW_NAME = #00000005    
 
 ,FILE_CASE_SENSITIVE_SEARCH = #00000001   
 ,FILE_CASE_PRESERVED_NAMES = #00000002   
 ,FILE_UNICODE_ON_DISK = #00000004   
 ,FILE_PERSISTENT_ACLS = #00000008   
 ,FILE_FILE_COMPRESSION = #00000010   
 ,FILE_VOLUME_QUOTAS = #00000020   
 ,FILE_SUPPORTS_SPARSE_FILES = #00000040   
 ,FILE_SUPPORTS_REPARSE_POINTS = #00000080   
 ,FILE_SUPPORTS_REMOTE_STORAGE = #00000100   
 ,FILE_VOLUME_IS_COMPRESSED = #00008000   
 ,FILE_SUPPORTS_OBJECT_IDS = #00010000   
 ,FILE_SUPPORTS_ENCRYPTION = #00020000   
 ,FILE_NAMED_STREAMS = #00040000   
 
 ,CREATE_NEW = 1 
 ,CREATE_ALWAYS = 2 
 ,OPEN_EXISTING = 3 
 ,OPEN_ALWAYS = 4 
 ,TRUNCATE_EXISTING = 5 
 
public constant cGetDriveTypeA =  
	define_c_func(kernel32, "GetDriveTypeA", {C_POINTER}, C_UINT) 
 
 
public function GetDriveType(object lpRootPathName) 
	if sequence(lpRootPathName) then 
		lpRootPathName = allocate_string(lpRootPathName, 1) 
	end if 
 
	 return c_func(cGetDriveTypeA,{lpRootPathName}) 
end function 
 
 
public constant cGetLogicalDriveStringsA =  
	define_c_func(kernel32, "GetLogicalDriveStringsA", 
 		{C_DWORD,C_POINTER}, C_DWORD) 
 
-- some code from the petzold wrap in archive 
-- hardwiring to just return a sequence of drives 
public function GetLogicalDriveStrings() 
  
	atom bufferSize = c_func(cGetLogicalDriveStringsA,{NULL, NULL}) 
	atom pBuffer = allocate(bufferSize) 
	bufferSize = c_func(cGetLogicalDriveStringsA,{bufferSize, pBuffer}) 
	sequence buffer = peek({pBuffer,bufferSize}) 
	free(pBuffer) 
	sequence drives = {}, 
					cur = "" 
	for i = 1 to length(buffer) do 
		if buffer[i] = 0 then 
			if length(cur) > 0 then 
				drives &= {cur} 
				cur = "" 
			end if 
		else 
			cur &= buffer[i] 
		end if 
	end for 
 
	return drives 
end function 
 
public function CreateFile(atom lpFileName, atom dwDesiredAccess, atom dwShareMode, 
	atom lpSecurityAttributes, atom dwCreationDisposition, atom dwFlagsAndAttributes, 
	atom hTemplateFile) 
 
	integer cCreateFile = define_c_func(kernel32, "CreateFileA", { 
		C_POINTER,C_POINTER,C_POINTER, 
		C_POINTER,C_POINTER,C_POINTER,C_POINTER}, C_POINTER) 
	return c_func(cCreateFile,{lpFileName,dwDesiredAccess,dwShareMode,lpSecurityAttributes, 
			dwCreationDisposition,dwFlagsAndAttributes,hTemplateFile}) 
end function 
 
--tested w/C version. returns correct value 
public function CTL_CODE(atom t,atom f,atom m,atom a) 
	--#define CTL_CODE(t,f,m,a) (((t)<<16)|((a)<<14)|((f)<<2)|(m)) 
	atom tmp = or_bits( shift_bits(f, -2), m) --| << 
	tmp = or_bits(tmp, shift_bits(a, -14))--| << 
	tmp = or_bits(tmp, shift_bits(t, -16)) --<< 
	 return tmp  
end function 
 
 
/*  winioctl.h */ 
public constant 
 METHOD_BUFFERED	= 	0 
 ,METHOD_IN_DIRECT	= 	1 
 ,METHOD_OUT_DIRECT		= 2 
 ,METHOD_NEITHER	 	=    3 
 
/*  Also in ddk/winddk.h */ 
 ,FILE_ANY_ACCESS		= 	0x00000000 
 ,FILE_SPECIAL_ACCESS	= 	FILE_ANY_ACCESS 
 ,FILE_READ_ACCESS		= 0x00000001 
 ,FILE_WRITE_ACCESS	= 0x00000002 
--  ,DEVICE_TYPE   =  DWORD 
 ,FILE_DEVICE_BEEP  =  	1 
 ,FILE_DEVICE_CD_ROM  =  	2 
 ,FILE_DEVICE_CD_ROM_FILE_SYSTEM  =  	3 
 ,FILE_DEVICE_CONTROLLER  =  	4 
 ,FILE_DEVICE_DATALINK  =  	5 
 ,FILE_DEVICE_DFS	  =  6 
 ,FILE_DEVICE_DISK	  =  7 
 ,FILE_DEVICE_DISK_FILE_SYSTEM  =  	8 
 ,FILE_DEVICE_FILE_SYSTEM	  =  9 
 ,FILE_DEVICE_INPORT_PORT  =  	10 
 ,FILE_DEVICE_KEYBOARD	  =  11 
 ,FILE_DEVICE_MAILSLOT	  =  12 
 ,FILE_DEVICE_MIDI_IN  =  	13 
 ,FILE_DEVICE_MIDI_OUT  =  	14 
 ,FILE_DEVICE_MOUSE  =  	15 
 ,FILE_DEVICE_MULTI_UNC_PROVIDER   =  16 
 ,FILE_DEVICE_NAMED_PIPE  =  	17 
 ,FILE_DEVICE_NETWORK	  =  18 
 ,FILE_DEVICE_NETWORK_BROWSER	  =  19 
 ,FILE_DEVICE_NETWORK_FILE_SYSTEM	  =  20 
 ,FILE_DEVICE_NULL	  =  21 
 ,FILE_DEVICE_PARALLEL_PORT	  =  22 
 ,FILE_DEVICE_PHYSICAL_NETCARD	  =  23 
 ,FILE_DEVICE_PRINTER  =  	24 
 ,FILE_DEVICE_SCANNER  =  	25 
 ,FILE_DEVICE_SERIAL_MOUSE_PORT	  =  26 
 ,FILE_DEVICE_SERIAL_PORT  =  	27 
 ,FILE_DEVICE_SCREEN  =  	28 
 ,FILE_DEVICE_SOUND	  =  29 
 ,FILE_DEVICE_STREAMS  =  	30 
 ,FILE_DEVICE_TAPE  =  	31 
 ,FILE_DEVICE_TAPE_FILE_SYSTEM	  =  32 
 ,FILE_DEVICE_TRANSPORT	  =  33 
 ,FILE_DEVICE_UNKNOWN	  =  34 
 ,FILE_DEVICE_VIDEO	  =  35 
 ,FILE_DEVICE_VIRTUAL_DISK	  =  36 
 ,FILE_DEVICE_WAVE_IN  =  	37 
 ,FILE_DEVICE_WAVE_OUT	  =  38 
 ,FILE_DEVICE_8042_PORT	  =  39 
 ,FILE_DEVICE_NETWORK_REDIRECTOR  =  	40 
 ,FILE_DEVICE_BATTERY	  =  41 
 ,FILE_DEVICE_BUS_EXTENDER	  =  42 
 ,FILE_DEVICE_MODEM          =     43 
 ,FILE_DEVICE_VDM            =     44 
 ,FILE_DEVICE_MASS_STORAGE    =    45 
 ,FILE_DEVICE_SMB            =     46 
 ,FILE_DEVICE_KS             =     47 
 ,FILE_DEVICE_CHANGER         =    48 
 ,FILE_DEVICE_SMARTCARD       =    49 
 ,FILE_DEVICE_ACPI            =    50 
 ,FILE_DEVICE_DVD              =   51 
 ,FILE_DEVICE_FULLSCREEN_VIDEO  =   52 
 ,FILE_DEVICE_DFS_FILE_SYSTEM   =   53 
 ,FILE_DEVICE_DFS_VOLUME        =   54 
 ,FILE_DEVICE_SERENUM           =   55 
 ,FILE_DEVICE_TERMSRV           =   56 
 ,FILE_DEVICE_KSEC            =     57 
 ,PARTITION_ENTRY_UNUSED	  =  0 
 ,PARTITION_FAT_12  =  	1 
 ,PARTITION_XENIX_1	  =  2 
 ,PARTITION_XENIX_2	  =  3 
 ,PARTITION_FAT_16	  =  4 
 ,PARTITION_EXTENDED  =  	5 
 ,PARTITION_HUGE  =  	6 
 ,PARTITION_IFS	  =  7 
 ,PARTITION_FAT32   =  0x0B 
 ,PARTITION_FAT32_XINT13   =  0x0C 
 ,PARTITION_XINT13   =  0x0E 
 ,PARTITION_XINT13_EXTENDED   =  0x0F 
 ,PARTITION_PREP  =  	0x41 
 ,PARTITION_LDM	  =  0x42 
 ,PARTITION_UNIX  =  	0x63 
 ,PARTITION_NTFT  =  	128 
 ,VALID_NTFT  =  	0xC0 
 ,SERIAL_LSRMST_ESCAPE	   =     0 
 ,SERIAL_LSRMST_LSR_DATA    =  	1 
 ,SERIAL_LSRMST_LSR_NODATA  =  	2 
 ,SERIAL_LSRMST_MST	  =  3 
 
 ,IOCTL_STORAGE_BASE   =         FILE_DEVICE_MASS_STORAGE 
 
 
atom IOCTL_STORAGE_QUERY_PROPERTY = 
	CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS) 
 
printf(1,"IOCTL_STORAGE_QUERY_PROPERTY %d 0x%x\n",{ 
	IOCTL_STORAGE_QUERY_PROPERTY, IOCTL_STORAGE_QUERY_PROPERTY}) 
 
 
enum --typedef _STORAGE_PROPERTY_ID { 
	StorageDeviceProperty = 0, 
	StorageAdapterProperty, 
	StorageDeviceIdProperty 
--} STORAGE_PROPERTY_ID, *PSTORAGE_PROPERTY_ID; 
 
 
enum --typedef _STORAGE_QUERY_TYPE { 
	PropertyStandardQuery = 0,  
	PropertyExistsQuery,  
	PropertyMaskQuery,  
	PropertyQueryMaxDefined  
--} STORAGE_QUERY_TYPE, *PSTORAGE_QUERY_TYPE; 
 
memtype unsigned char as UCHAR 
 
memstruct STORAGE_PROPERTY_QUERY with pack 4 
	short PropertyId  --STORAGE_PROPERTY_ID 
	short QueryType --STORAGE_QUERY_TYPE 
	--UCHAR AdditionalParameters[1] --FIXME  
	double AdditionalParameters[1] 
	-- object instead of uchar same, double ok 
	--	pack 4 or [4] doesn't solve problem 
end memstruct 
?sizeof(STORAGE_PROPERTY_QUERY)  --should be 12 using UCHAR 
 
 
memstruct STORAGE_DEVICE_DESCRIPTOR --with pack 4 
	ULONG  Version 
	ULONG  Size 
	UCHAR  DeviceType 
	UCHAR  DeviceTypeModifier 
	BOOL  RemovableMedia 
	BOOL  CommandQueueing 
	ULONG  VendorIdOffset 
	ULONG  ProductIdOffset 
	ULONG  ProductRevisionOffset 
	ULONG  SerialNumberOffset 
	--UINT  BusType  --FIXME enum STORAGE_BUS_TYPE 
	UCHAR  BusType	--UCHAR seems to work better than UINT 
	ULONG  RawPropertiesLength 
	UCHAR  RawDeviceProperties[1] 
	--double  RawDeviceProperties[1]  --double no diff here 
end memstruct 
 
 
enum --typedef _STORAGE_BUS_TYPE { 
	BusTypeUnknown = 0x00, 
	BusTypeScsi, 
	BusTypeAtapi, 
	BusTypeAta, 
	BusType1394, 
	BusTypeSsa, 
	BusTypeFibre, 
	BusTypeUsb, 
	BusTypeRAID, 
	BusTypeMaxReserved = 0x7F 
--} STORAGE_BUS_TYPE, *PSTORAGE_BUS_TYPE 
 
map BusTypeMap = map:new() 
	map:put(BusTypeMap, BusTypeUnknown,"BusTypeUnknown") 
	map:put(BusTypeMap, BusTypeScsi,"BusTypeScsi") 
	map:put(BusTypeMap, BusTypeAtapi,"BusTypeAtapi") 
	map:put(BusTypeMap, BusTypeAta,"BusTypeAta") 
	map:put(BusTypeMap, BusType1394,"BusType1394") 
	map:put(BusTypeMap, BusTypeSsa,"BusTypeSsa") 
	map:put(BusTypeMap, BusTypeFibre,"BusTypeFibre") 
	map:put(BusTypeMap, BusTypeUsb,"BusTypeUsb") 
	map:put(BusTypeMap, BusTypeRAID,"BusTypeRAID") 
	map:put(BusTypeMap, BusTypeMaxReserved,"BusTypeMaxReserved") 
 
public function DeviceIoControl(atom hDevice, atom dwIoControlCode, 
	 atom lpInBuffer, atom nInBufferSize, 
		atom lpOutBuffer, atom nOutBufferSize, 
		 atom lpBytesReturned, atom lpOverlapped) 
 
	integer cDeviceIoControl = define_c_func(kernel32, "DeviceIoControl", { 
		C_HANDLE,C_DWORD, 
		C_POINTER,C_DWORD, 
		C_POINTER,C_DWORD,C_POINTER,C_POINTER}, C_BOOL) 
 
	return c_func(cDeviceIoControl,{hDevice,dwIoControlCode, 
		lpInBuffer,nInBufferSize, 
		lpOutBuffer,nOutBufferSize,lpBytesReturned,lpOverlapped}) 
end function 
 
 
public function CloseHandle(atom hObject) 
	integer cCloseHandle = define_c_func(kernel32, "CloseHandle", 
							{C_POINTER}, C_BOOL) 
	 return c_func(cCloseHandle,{hObject}) 
end function 
 
 
function IsUsbDevice( integer letter )	--was bool 
 
	sequence volumeAccessPath = "\\\\.\\X:" 
	volumeAccessPath[5] = letter 
 
	atom deviceHandle = CreateFile( 
	 allocate_string(volumeAccessPath, 1), 
	 0,                -- no access to the drive, non admin 
	 or_all({FILE_SHARE_READ, -- | share mode 
	 			FILE_SHARE_WRITE}),  
	 NULL,             -- default security attributes 
	 OPEN_EXISTING,    -- disposition 
	 0,                -- file attributes 
	 NULL)            -- do not copy file attributes 
 
--printf(1,"%s CreateFile er=%s ",{volumeAccessPath, get_winerror_string()}) 
 c_proc( xSetLastError, {0} )  
 
	-- setup query 
	atom query = allocate(sizeof(STORAGE_PROPERTY_QUERY), 1) 
	mem_set(query, 0, sizeof(STORAGE_PROPERTY_QUERY)) 
	query.STORAGE_PROPERTY_QUERY.PropertyId = StorageDeviceProperty 
	query.STORAGE_PROPERTY_QUERY.QueryType = PropertyStandardQuery 
	 
	-- issue query 
	atom bytes = allocate(8, 1 )  -- max pointer just in case 
 
	atom devd = allocate(sizeof(STORAGE_DEVICE_DESCRIPTOR), 1) 
  
	integer busType = BusTypeUnknown 
 
	if DeviceIoControl(deviceHandle, 
		 IOCTL_STORAGE_QUERY_PROPERTY, 
		 query, sizeof(STORAGE_PROPERTY_QUERY),	--& 
		 devd, sizeof(STORAGE_DEVICE_DESCRIPTOR),	--& 
		 bytes, NULL)	--& 
	 then 
	 		busType = devd.STORAGE_DEVICE_DESCRIPTOR.BusType 
	else 
		atom eror = GetLastError() 
 
		printf(1, "\n\tFailed to define bus type for: %s: winerr%s\n", { 
			letter, get_winerror_string(eror) 
			}) 
	end if 
	  
	CloseHandle(deviceHandle) 
	return busType	-- was BusTypeUsb=  
end function 
 
 
 
sequence drives = GetLogicalDriveStrings() 
 
--?drives 
--printf(1,"%s\n",{hexdump( join(drives, " _ ") ) }) 
--     drives = { 
--                "A:\\", 
--                "U:\\" 
--              } 
 
for x = 1 to length(drives) do 
 
	sequence 
			 warn = "", 
			 bar = "" 
	atom min_file_size = 0 
 
	integer rstate = GetDriveType(drives[x])     
 
	atom bt = IsUsbDevice(drives[x][1]) 
	if rstate = DRIVE_REMOVABLE then 
		 
		bar = sprintf(" -----\tREMOVABLE/FLOPPY ", {}) 
		 
	elsif rstate = DRIVE_CDROM then 
		bar = sprintf(" -----\tDRIVE_CDROM ", {}) 
	elsif rstate  != DRIVE_FIXED then     
		bar = sprintf(" -----\tDRIVE_UNKNOWN %d ", {rstate}) 
	else 
	end if 
 
	if length(bar) then 
		bar &= map:get(BusTypeMap, bt,"BTUNKNOWN") 
	else 
		sequence res = disk_size(drives[x])  
		printf(1, "Drive %s has %3.0f%% free space %s\n", {   
				drives[x], 
				res[FREE_BYTES] / res[TOTAL_BYTES]*100,  
				map:get(BusTypeMap, bt,"BTUNKNOWN") 
		})  
		res = disk_metrics(drives[x])  
		min_file_size = res[SECTORS_PER_CLUSTER] * res[BYTES_PER_SECTOR]  
	 
		atom tot= (res[TOTAL_NUMBER_OF_CLUSTERS] *res[BYTES_PER_SECTOR]  
						*res[SECTORS_PER_CLUSTER])/BYTE_STANDARD 
							  
		atom fs = (((res[SECTORS_PER_CLUSTER]) 
					*(res[BYTES_PER_SECTOR]) 
					*(res[NUMBER_OF_FREE_CLUSTERS]))/1048576) 
 
		bar = sprintf("%d MB %s %d",{ 
					fs/1000, "-------------", tot/1000}) 
	end if 
	 
	printf(1,"%s %s \t%s  %d\n",{warn, drives[x][1], bar, rstate}) 
	 
end for 
 
 
 
/* 
 
	Failed to define bus type for: A: winerr0x1 Incorrect function. 
 A   -----  REMOVABLE/FLOPPY BusTypeUnknown  2 
Drive C:\ has  17% free space BusTypeUnknown 
 C  1 MB ------------- 10  3 
 F   -----  DRIVE_CDROM BusTypeUnknown  5 
 G   -----  DRIVE_CDROM BusTypeSsa  5 
 H   -----  REMOVABLE/FLOPPY BusTypeUnknown  2 
Drive U:\ has  21% free space BusTypeUnknown 
 U  2 MB ------------- 12  3 
 
G,H,U are USB 
 
 
*/