Latest Release: 

dm is an ls-like lister for CBM container formats - that includes both physical-media image formats (like .d64, .t64 .p00) and native/archival formats (like .lnx and [1-4]! zips). dm also includes a growing number of useful sub-commands for performing various tasks on supported containers formats that make sense in the command line context. Think of dm as a command line 'cousin' to DirMaster. dm was originally called cbmls when its only feature was listing.

With the release of v1.5.0 there are six sub-commands; the default when no command is explicitly selected is 'ls' and in general dm will behave exactly like cbmls did. But you can also use 'dm ls' if you want to explicitly invoke the ls/listing feature. i.e. the following are equivalent:

dm ls -lc disk.d64
dm -lc disk.d64      # sub-command omitted

Currently, these sub-commands are available: 'ls', 'diff', 'zip' ,'unzip', 'fgrep', and 'unar'.

As always we invite bug reports and feature requests; those should be directed here.

'dm ls'

If you know what 'ls' is you'll find comfort with some of the command line options (try 'dm --help')

'dm ls' - Features

  • recursively process file system subdirectories

  • recursively process *within* disk images and file containers

  • support for input/output redirection

  • choice of output: canonical "$" style or columns
  • control over column output/order
  • sensible output for cp437 or Unicode
  • use with our font, C64 Pro Mono, for a genuine PETSCII shell experience

'dm ls' - Screenshots:

dm will present as good an output as possible in a default cmd shell... some PETSCII characters map sensibly and color is an option.

cmd on Windows XP

and with colorized output...

It's a little smoother if you switch to a TrueType font. But some TrueType fonts, like the Lucida Console used below, don't have all the applicable characters that we can map to from PETSCII and show filler as seen in the middle...  other TrueType fonts have a fuller set of glyphs like PragmataPro on the right.

Windows 8.1 with Lucida font

Lucida is missing some glyphs...

but PragmataPro has them!

Of course the best way for hardcore c64 freaks to look at their directories is made possible with our C64 Pro Mono font:

Windows 7 and the majesty of load"$",8

dm works similarly on Linux and macOS:

Mate Terminal on Mint

iTerm2 on Mac OS X 10.7

'dm ls' - General use:

dm will try to process all 'loose' arguments on the command line as a potential disk/archive image or directory. Order or mixture with command options does not matter. There is also some support for reading disk/archive data from a pipe or redirection. Default behavior is to list directories in the 'canonical' load"$" format. A column-based listing is also available, and you have full control of column selection and ordering. The following shows the 'long listing' (-l or --long) output which is a short cut for outputting every column: blocks, bytes, index, track/sector, load address, name, splat, type and lock (there are no splatted/locked files on this directory):

output from 'dm -lc happy--style.d64'

Recursion, or directory traversal if you prefer, is possible in two axes: recursion through the os file system and recursion through disk/archive contents. The latter is referred to in the program as a deep listing and when selected dm will show you the contents of archives, subdirectories and partitions, as deep as possible (e.g. an arc inside a lnx inside a zip-4 inside a 1581 subdir).

'dm ls' - Recipes:

List the contents of oldstuff.lbr and disk.d64 using the long column output, with color.

dm oldstuff.lbr -lc disk.d64

Pipe an uncompressed disk image into dm for listing, using the lower/upper character mode instead of the default upper/graphics:

xz -d mule.d64.xz | dm -C lower

List the contents of every recognized disk image and container starting with the current working directory (all files and recursing into all subdirectories):

dm -lR .

Deep list the contents of the .d4m image using color, outputting three columns in the given order - this will include the contents of all partitions and any archiver containers discovered therein:

dm -dc disk.d4m --columns bytes,name,index

Note that the argument for '--columns' must be a comma-separated list, with no spaces! 'dm --help' will show you the possible valid column names.

'dm diff'

'dm diff' will compare two block-based disk images (e.g. a .d81 is block-based, while a .t64 is not) at a byte-by-byte level and report on differences. You can use 'dm diff --help' for a list of options to this command.

'dm diff' - Features

  • brief or detailed (-v) output

  • optional BAM visualization display

  • also reports on differences in error codes, if any

'dm diff' - Recipes:

Show a detailed summary of the differences between a d64 and a g64 (note that the g64 is interpreted as if it were converted to a d64 first!).

dm diff disk.d64 disk.g64 -v

Show the differences between the two disk images using a BAM visualization in addition to a brief summary; also include a legend explaining the BAM symbols:

dm diff disk1.d64 disk2.d64 --bam --legend


'dm fgrep'

'dm fgrep' searches block-based CBM containers (like d64, d81) for a byte pattern taking into account the container's internal block/link based geometry.The byte pattern is a fixed sequence of bytes, ergo, this works like 'fgrep' or 'grep -F' for now.

'dm fgrep' - search patterns:

A byte sequence pattern can be expressed as a serialized hex string, as implicit PETSCII, or as explicit ASCII.

By "serialized hex" we mean simply an ASCII representation of bytes encoded as hexadecimal values. dm is very flexible in what actual text representations it can accept; all non-hexadecimal characters are ignored or used as delimiters. The first character of the pattern must be the dollar sign, '$'. After that, hexadecimal values are read in pairs, or if delimited as single digits. Here are some examples of valid serialized hex strings which are all interpreted as three bytes, values 1, 2 and 3:

"$010203"    "$01 02 03"    "$01, $02, $03"    "$1 2 3"    "$01:02:03"  "$01x02y03z!"

The goal here is compatibility with the variety of formats that sequences of hex bytes appear in software - hex editors, and other tools, for ease of use when copy-pasting strings. Of course the final example above is contrived just to illustrate the earlier point - all non hexadecimal characters act as delimiters or are ignored. Note that some command shells will treat '$' as a special character that invokes some shell feature. Depending on the shell, you may be able to escape the '$' with, e.g., a '\' or some other character. As a final resort, the cli option "-h/--pattern-hex" can be used to force dm to interpret the pattern as hex even if it doesn't start with '$'.

By "implicit PETSCII" we mean PETSCII which is implied from interpreting an ASCII representation as though the current CBM case mode is lower/upper. For example:

dm fgrep "abc" disk.d64  # pattern evaluates to $414243
dm fgrep "ABC" disk.d64  # pattern evaluates to $c1c2c3

By "explicit ASCII" we mean the pattern is taken literally as an ASCII string, which in the context of CBM containers can be useful for searching GEOS related media, for example.

In addition to "-h/--pattern-hex" to declare a serialized hex pattern, you can use "-p/--pattern-petscii" and "-a/--pattern-ascii" to declare that a pattern should be interpreted as PETSCII or ASCII. The default when none of the three pattern declaration options is provided is to check for a leading '$' and interpret as serialized hex if found, or as PETSCII otherwise.

There is nothing inherent in dm that necessitates using quotes around your pattern - space characters in your pattern would obviously require quotes. Depending on the pattern and your command shell, you may need to use quotes when using certain other characters including '$' as already noted above.

'dm fgrep' - output:

The output format when matches are found mimics output from the unix grep command. Here are some example results from searching a disk image:

> dm fgrep abc disk.d64

> dm fgrep "$313233" -b dsk_*.d64 disk.d64

Output format consists of the filename of the OS-native file where a match was located, followed by the track,sector of the match. When a partial match is found at the end of a sector, which is then completed by following a valid chain to the next linked sector, dm will output the match as a pair of track,sector notations as shown in the second match in the first example above (the match spans track 2, sector 2 and track 2, sector 3). 

When using the cli option "-b/--byte-offset', the byte offset of the start of the match is included in output. The second set of results shows where the offset is displayed, either after the track,sector notation, or after the first track,sector and before the second.

Several other cli options are available to control output and which are similar to and influenced by unix fgrep; you can see all options with 'dm fgrep --help'.

'dm zip'

'dm zip' produces zip4 and zip5 sets, which are a multipart cbm full disk archive format. On the native system these are named using a prefix instead of a suffix: 1!filename, 2!filename, ,,, up to 5!filename for a five part set. Four parts are used for 35 track disk images and five parts for 40 track images. By default 'dm zip' creates a zip4 set from a 35 track .d64 and a zip5 set from a 40 track d64 but you can use use -4/--four to force only four parts. Using -f/--free will output unallocated sectors as zero filled in the same way the old "bam zipper" mod to zipdisk worked. Example usage & output:

> dm zip disk.d64 zipped

1!zipped:  38561
2!zipped:  43312
3!zipped:  39846
4!zipped:  41761
          163482 bytes

           11366 bytes saved (6%)

'dm unzip'

'dm unzip'  unarchives zip4 and zip5 sets into d64 images; it will also unarchive zip6 sets, which are multipart archives of a disk using a lower level gcr representation. zip6 sets are named similarly but using two bangs:1!!filename, 2!!filename, ... 6!!filename. By default zip6 sets are unarchived directly into a d64 but you can use -z/--unz to force a zip6 to decompress into a .g64. 'dm unzip' will make a reasonable assumption about what you want when you specify an extension. Examples:

# unzip '1!zipped', '2!zipped', etc into 'disk.d64'
dm unzip zipped disk.d64

# same as above (the default is .d64)
dm unzip zipped disk      

# same as above (you can specify a part explicitly)
dm unzip 3!zipped disk

# unzip '1!!zip6', '2!!zip6', etc into 'disk.d64'
dm unzip zip6 disk

# unzip '1!!zip6', '2!!zip6', etc into 'disk.g64'
dm unzip 1!!zip6 disk.g64

#  same as above, using -z to force output as a g64
dm unzip -z zip6 disk

'dm unar'

'dm unar'  unarchives any of the native CBM single file archive formats that are also supported in DirMaster, including .arc, .ark, .cvt, .lbr, .lha, .lnx, .sda, .sfx, .spy, and .wr3. The results are stored on a new disk image (a d64 or d81). If you don't provide an extension for the disk, unar defaults to d64 but uses a d81 if necessary. dm can identify most archive formats so it usually isn't necessary that the archive file have a matching extension - therefore prg files can be unarchived directly without renaming and even misnamed archive files should be correctly unarchived. Examples:

# unarchive a Lynx file onto a new .d64
dm unar sb146644.lnx disk.d64

# unarchive a Wraptor file onto a new .d64
dm unar scrapit.wr3.prg disk