├── .appveyor.yml ├── .depend ├── .gitignore ├── .travis.yml ├── COPYING ├── INSTALL ├── Makefile.in ├── NEWS ├── README ├── README.md ├── README.win32-libdsk ├── README.win32.cygwin.txt ├── badfs ├── Makefile ├── blocknumber ├── doubleext ├── extension ├── extno ├── hugecom ├── label ├── lcr ├── multipleblocks ├── name ├── recordcount ├── status └── timestamps ├── config.guess ├── config.h.in ├── config.sub ├── configure ├── configure.in ├── cpm.5 ├── cpm.5.in ├── cpm.ps ├── cpmchattr.1 ├── cpmchattr.1.in ├── cpmchattr.c ├── cpmchmod.1 ├── cpmchmod.1.in ├── cpmchmod.c ├── cpmcp.1 ├── cpmcp.1.in ├── cpmcp.c ├── cpmdir.h ├── cpmfs.c ├── cpmfs.h ├── cpmls.1 ├── cpmls.1.in ├── cpmls.c ├── cpmrm.1 ├── cpmrm.1.in ├── cpmrm.c ├── device.h ├── device_libdsk.c ├── device_posix.c ├── device_win32.c ├── diskdefs ├── diskdefs.5 ├── diskdefs.5.in ├── fsck.cpm.1 ├── fsck.cpm.1.in ├── fsck.cpm.c ├── fsed.cpm.1 ├── fsed.cpm.1.in ├── fsed.cpm.c ├── getopt.c ├── getopt1.c ├── getopt_.h ├── getopt_int.h ├── install-sh ├── makefile.nt ├── mkfs.cpm.1 ├── mkfs.cpm.1.in └── mkfs.cpm.c /.appveyor.yml: -------------------------------------------------------------------------------- 1 | version: '{build}' 2 | os: Windows Server 2012 3 | 4 | environment: 5 | CYG_MIRROR: http://cygwin.mirror.constant.com 6 | CYG_PACKAGES: make,gcc-core,mingw64-i686-gcc-core,mingw64-x86_64-gcc-core,w32api-headers,libncurses-devel 7 | matrix: 8 | - CYG_ROOT: C:\cygwin 9 | CYG_CACHE: C:\cygwin\var\cache\setup 10 | CYG_SETUP: setup-x86.exe 11 | BASH: C:\cygwin\bin\bash 12 | CC: gcc 13 | - CYG_ROOT: C:\cygwin64 14 | CYG_CACHE: C:\cygwin64\var\cache\setup 15 | CYG_SETUP: setup-x86_64.exe 16 | BASH: C:\cygwin64\bin\bash 17 | CC: gcc 18 | 19 | install: 20 | - ps: if (Test-Path Env:\CYG_ROOT) { Start-FileDownload "https://cygwin.com/$env:CYG_SETUP" -FileName "$env:CYG_SETUP" } 21 | - cmd: if defined CYG_ROOT (%CYG_SETUP% --quiet-mode --no-shortcuts --only-site --root "%CYG_ROOT%" --site "%CYG_MIRROR%" --local-package-dir "%CYG_CACHE%" --packages "%CYG_PACKAGES%" --upgrade-also) 22 | # - cmd: if defined CYG_ROOT (cygcheck -drs) 23 | 24 | init: 25 | - git config --global core.autocrlf input 26 | 27 | build_script: 28 | - cmd: if defined BASH (%BASH% -lc "cd $(cygpath ${APPVEYOR_BUILD_FOLDER}) && CPPFLAGS=-D_WIN32 ./configure --with-diskdefs=/usr/local/share/diskdefs") 29 | - cmd: if defined BASH (%BASH% -lc "cd $(cygpath ${APPVEYOR_BUILD_FOLDER}) && make all && make fsck.test && mkdir -p /usr/local/share /usr/local/share/man/man1 /usr/local/share/man/man5 && make install") 30 | 31 | test: false 32 | 33 | # artifacts: 34 | # - path: '*.tar.bz2' 35 | # - path: '*.tar.gz' 36 | # - path: '*.tar.xz' 37 | # - path: '*.zip' 38 | 39 | cache: 40 | - '%CYG_CACHE%' 41 | -------------------------------------------------------------------------------- /.depend: -------------------------------------------------------------------------------- 1 | cpmchattr.o: cpmchattr.c config.h getopt_.h cpmfs.h device.h 2 | cpmchmod.o: cpmchmod.c config.h getopt_.h cpmfs.h device.h 3 | cpmcp.o: cpmcp.c config.h getopt_.h cpmfs.h device.h 4 | cpmfs.o: cpmfs.c config.h cpmdir.h cpmfs.h device.h 5 | cpmls.o: cpmls.c config.h getopt_.h cpmfs.h device.h 6 | cpmrm.o: cpmrm.c config.h getopt_.h cpmfs.h device.h 7 | device_libdsk.o: device_libdsk.c config.h device.h 8 | device_posix.o: device_posix.c config.h device.h 9 | device_win32.o: device_win32.c config.h cpmdir.h cpmfs.h device.h 10 | fsck.cpm.o: fsck.cpm.c config.h getopt_.h cpmdir.h cpmfs.h device.h 11 | fsed.cpm.o: fsed.cpm.c config.h cpmfs.h device.h 12 | getopt.o: getopt.c config.h getopt_.h getopt_int.h 13 | getopt1.o: getopt1.c config.h getopt_.h getopt_int.h 14 | mkfs.cpm.o: mkfs.cpm.c config.h getopt_.h cpmfs.h device.h 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | config.h 2 | config.log 3 | config.status 4 | Makefile 5 | *.o 6 | cpm.5 7 | cpmchattr 8 | cpmchattr.1 9 | cpmchmod 10 | cpmchmod.1 11 | cpmcp 12 | cpmcp.1 13 | cpmls 14 | cpmls.1 15 | cpmrm 16 | cpmrm.1 17 | fsck.cpm 18 | fsck.cpm.1 19 | fsed.cpm 20 | fsed.cpm.1 21 | mkfs.cpm 22 | mkfs.cpm.1 23 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | compiler: gcc 3 | before_install: 4 | - sudo apt-get update -qq 5 | - sudo apt-get install -y fakeroot 6 | - sudo apt-get install -y build-essential 7 | - sudo apt-get install -y libncurses-dev 8 | script: ./configure --prefix=/usr && make all && make fsck.test && fakeroot mkdir -p ${HOME}/sysroot/usr/share ${HOME}/sysroot/usr/bin ${HOME}/sysroot/usr/share/man/man1 ${HOME}/sysroot/usr/share/man/man5 && fakeroot make BINDIR=${HOME}/sysroot/usr/bin MANDIR=${HOME}/sysroot/usr/share/man prefix=${HOME}/sysroot/usr install 9 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | Options for "./configure" include: 2 | 3 | --with-defformat= 4 | Set the standard format the cpmtools will use. 5 | Default is ibm-3740 6 | --with-libdsk=path 7 | Use LibDsk to access raw files, .DSK files, MYZ80 8 | files and Unix/Windows floppy drives. This library 9 | is available at: 10 | http://www.seasip.demon.co.uk/Unix/LibDsk/ 11 | --with-dmalloc 12 | Use the dmalloc library 13 | --disable-floppy 14 | Disable the direct floppy access in LibDsk, so that it only 15 | contains drivers for raw files, .DSK files and MYZ80 files. 16 | 17 | All the cpmtools that use LibDsk have an extra option: -T . This 18 | sets the drive type used by LibDsk, and is one of: 19 | 20 | "floppy" - The computer's floppy drive (only supported on Linux and Win32) 21 | "raw" - Raw file (or the floppy drive on other Unix systems) 22 | "dsk" - .DSK file 23 | "myz80" - MYZ80 hard drive file 24 | 25 | It will normally only be necessary to include "-T" when you are 26 | accessing a MYZ80 file, because LibDsk can detect the other file types 27 | automatically. "-T" options are ignored if LibDsk is not being used. 28 | 29 | 30 | Basic Installation 31 | ================== 32 | 33 | These are generic installation instructions. 34 | 35 | The `configure' shell script attempts to guess correct values for 36 | various system-dependent variables used during compilation. It uses 37 | those values to create a `Makefile' in each directory of the package. 38 | It may also create one or more `.h' files containing system-dependent 39 | definitions. Finally, it creates a shell script `config.status' that 40 | you can run in the future to recreate the current configuration, a file 41 | `config.cache' that saves the results of its tests to speed up 42 | reconfiguring, and a file `config.log' containing compiler output 43 | (useful mainly for debugging `configure'). 44 | 45 | If you need to do unusual things to compile the package, please try 46 | to figure out how `configure' could check whether to do them, and mail 47 | diffs or instructions to the address given in the `README' so they can 48 | be considered for the next release. If at some point `config.cache' 49 | contains results you don't want to keep, you may remove or edit it. 50 | 51 | The file `configure.in' is used to create `configure' by a program 52 | called `autoconf'. You only need `configure.in' if you want to change 53 | it or regenerate `configure' using a newer version of `autoconf'. 54 | 55 | The simplest way to compile this package is: 56 | 57 | 1. `cd' to the directory containing the package's source code and type 58 | `./configure' to configure the package for your system. If you're 59 | using `csh' on an old version of System V, you might need to type 60 | `sh ./configure' instead to prevent `csh' from trying to execute 61 | `configure' itself. 62 | 63 | Running `configure' takes awhile. While running, it prints some 64 | messages telling which features it is checking for. 65 | 66 | 2. Type `make' to compile the package. 67 | 68 | 3. Optionally, type `make check' to run any self-tests that come with 69 | the package. 70 | 71 | 4. Type `make install' to install the programs and any data files and 72 | documentation. 73 | 74 | 5. You can remove the program binaries and object files from the 75 | source code directory by typing `make clean'. To also remove the 76 | files that `configure' created (so you can compile the package for 77 | a different kind of computer), type `make distclean'. There is 78 | also a `make maintainer-clean' target, but that is intended mainly 79 | for the package's developers. If you use it, you may have to get 80 | all sorts of other programs in order to regenerate files that came 81 | with the distribution. 82 | 83 | Compilers and Options 84 | ===================== 85 | 86 | Some systems require unusual options for compilation or linking that 87 | the `configure' script does not know about. You can give `configure' 88 | initial values for variables by setting them in the environment. Using 89 | a Bourne-compatible shell, you can do that on the command line like 90 | this: 91 | CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure 92 | 93 | Or on systems that have the `env' program, you can do it like this: 94 | env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure 95 | 96 | Compiling For Multiple Architectures 97 | ==================================== 98 | 99 | You can compile the package for more than one kind of computer at the 100 | same time, by placing the object files for each architecture in their 101 | own directory. To do this, you must use a version of `make' that 102 | supports the `VPATH' variable, such as GNU `make'. `cd' to the 103 | directory where you want the object files and executables to go and run 104 | the `configure' script. `configure' automatically checks for the 105 | source code in the directory that `configure' is in and in `..'. 106 | 107 | If you have to use a `make' that does not supports the `VPATH' 108 | variable, you have to compile the package for one architecture at a time 109 | in the source code directory. After you have installed the package for 110 | one architecture, use `make distclean' before reconfiguring for another 111 | architecture. 112 | 113 | Installation Names 114 | ================== 115 | 116 | By default, `make install' will install the package's files in 117 | `/usr/local/bin', `/usr/local/man', etc. You can specify an 118 | installation prefix other than `/usr/local' by giving `configure' the 119 | option `--prefix=PATH'. 120 | 121 | You can specify separate installation prefixes for 122 | architecture-specific files and architecture-independent files. If you 123 | give `configure' the option `--exec-prefix=PATH', the package will use 124 | PATH as the prefix for installing programs and libraries. 125 | Documentation and other data files will still use the regular prefix. 126 | 127 | In addition, if you use an unusual directory layout you can give 128 | options like `--bindir=PATH' to specify different values for particular 129 | kinds of files. Run `configure --help' for a list of the directories 130 | you can set and what kinds of files go in them. 131 | 132 | If the package supports it, you can cause programs to be installed 133 | with an extra prefix or suffix on their names by giving `configure' the 134 | option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. 135 | 136 | Optional Features 137 | ================= 138 | 139 | Some packages pay attention to `--enable-FEATURE' options to 140 | `configure', where FEATURE indicates an optional part of the package. 141 | They may also pay attention to `--with-PACKAGE' options, where PACKAGE 142 | is something like `gnu-as' or `x' (for the X Window System). The 143 | `README' should mention any `--enable-' and `--with-' options that the 144 | package recognizes. 145 | 146 | For packages that use the X Window System, `configure' can usually 147 | find the X include and library files automatically, but if it doesn't, 148 | you can use the `configure' options `--x-includes=DIR' and 149 | `--x-libraries=DIR' to specify their locations. 150 | 151 | Specifying the System Type 152 | ========================== 153 | 154 | There may be some features `configure' can not figure out 155 | automatically, but needs to determine by the type of host the package 156 | will run on. Usually `configure' can figure that out, but if it prints 157 | a message saying it can not guess the host type, give it the 158 | `--host=TYPE' option. TYPE can either be a short name for the system 159 | type, such as `sun4', or a canonical name with three fields: 160 | CPU-COMPANY-SYSTEM 161 | 162 | See the file `config.sub' for the possible values of each field. If 163 | `config.sub' isn't included in this package, then this package doesn't 164 | need to know the host type. 165 | 166 | If you are building compiler tools for cross-compiling, you can also 167 | use the `--target=TYPE' option to select the type of system they will 168 | produce code for and the `--build=TYPE' option to select the type of 169 | system on which you are compiling the package. 170 | 171 | Sharing Defaults 172 | ================ 173 | 174 | If you want to set default values for `configure' scripts to share, 175 | you can create a site shell script called `config.site' that gives 176 | default values for variables like `CC', `cache_file', and `prefix'. 177 | `configure' looks for `PREFIX/share/config.site' if it exists, then 178 | `PREFIX/etc/config.site' if it exists. Or, you can set the 179 | `CONFIG_SITE' environment variable to the location of the site script. 180 | A warning: not all `configure' scripts look for a site script. 181 | 182 | Operation Controls 183 | ================== 184 | 185 | `configure' recognizes the following options to control how it 186 | operates. 187 | 188 | `--cache-file=FILE' 189 | Use and save the results of the tests in FILE instead of 190 | `./config.cache'. Set FILE to `/dev/null' to disable caching, for 191 | debugging `configure'. 192 | 193 | `--help' 194 | Print a summary of the options to `configure', and exit. 195 | 196 | `--quiet' 197 | `--silent' 198 | `-q' 199 | Do not print messages saying which checks are being made. To 200 | suppress all normal output, redirect it to `/dev/null' (any error 201 | messages will still be shown). 202 | 203 | `--srcdir=DIR' 204 | Look for the package's source code in directory DIR. Usually 205 | `configure' can determine that directory automatically. 206 | 207 | `--version' 208 | Print the version of Autoconf used to generate the `configure' 209 | script, and exit. 210 | 211 | `configure' also accepts some other, not widely useful, options. 212 | 213 | -------------------------------------------------------------------------------- /Makefile.in: -------------------------------------------------------------------------------- 1 | srcdir= @srcdir@ 2 | VPATH= @srcdir@ 3 | libdir= @libdir@ 4 | BINDIR= @bindir@ 5 | MANDIR= @mandir@ 6 | includedir= @includedir@ 7 | exec_prefix= @exec_prefix@ 8 | prefix= @prefix@ 9 | datarootdir= @datarootdir@ 10 | 11 | INSTALL= @INSTALL@ 12 | INSTALL_DATA= @INSTALL_DATA@ 13 | CC= @CC@ 14 | CFLAGS= @CFLAGS@ 15 | LDFLAGS= @LDFLAGS@ 16 | DEFFORMAT= @DEFFORMAT@ 17 | DISKDEFS= @DISKDEFS@ 18 | DEVICE= @DEVICE@ 19 | FSED_CPM= @FSED_CPM@ 20 | EXEEXT = @EXEEXT@ 21 | OBJEXT = .@OBJEXT@ 22 | LIBS= @LIBS@ @LDLIBS@ 23 | LDDEPS= @LDDEPS@ 24 | CPPFLAGS= @CPPFLAGS@ -DDISKDEFS=\"$(DISKDEFS)\" -DFORMAT=\"$(DEFFORMAT)\" 25 | # 26 | # I'm only a novice and I haven't worked out how to autoconf this one 27 | # 28 | 29 | #MAKEDEPEND= mkdep -d 30 | MAKEDEPEND= gcc -MM 31 | #MAKEDEPEND= makedepend -f- 32 | 33 | DEVICEOBJ= device_$(DEVICE)$(OBJEXT) 34 | 35 | ALL= cpmls$(EXEEXT) cpmrm$(EXEEXT) cpmcp$(EXEEXT) \ 36 | cpmchmod$(EXEEXT) cpmchattr$(EXEEXT) mkfs.cpm$(EXEEXT) \ 37 | fsck.cpm$(EXEEXT) $(FSED_CPM) 38 | 39 | all: $(ALL) 40 | 41 | LibDsk/libdsk.a: 42 | cd LibDsk && make 43 | 44 | cpmls$(EXEEXT): cpmls$(OBJEXT) cpmfs$(OBJEXT) getopt$(OBJEXT) getopt1$(OBJEXT) $(DEVICEOBJ) $(LDDEPS) 45 | $(CC) $(LDFLAGS) -o $@ cpmls$(OBJEXT) cpmfs$(OBJEXT) getopt$(OBJEXT) getopt1$(OBJEXT) $(DEVICEOBJ) $(LIBS) 46 | 47 | cpmrm$(EXEEXT): cpmrm$(OBJEXT) cpmfs$(OBJEXT) getopt$(OBJEXT) getopt1$(OBJEXT) $(DEVICEOBJ) $(LDDEPS) 48 | $(CC) $(LDFLAGS) -o $@ cpmrm$(OBJEXT) cpmfs$(OBJEXT) getopt$(OBJEXT) getopt1$(OBJEXT) $(DEVICEOBJ) $(LIBS) 49 | 50 | cpmcp$(EXEEXT): cpmcp$(OBJEXT) cpmfs$(OBJEXT) getopt$(OBJEXT) getopt1$(OBJEXT) $(DEVICEOBJ) $(LDDEPS) 51 | $(CC) $(LDFLAGS) -o $@ cpmcp$(OBJEXT) cpmfs$(OBJEXT) getopt$(OBJEXT) getopt1$(OBJEXT) $(DEVICEOBJ) $(LIBS) 52 | 53 | cpmchmod$(EXEEXT): cpmchmod$(OBJEXT) cpmfs$(OBJEXT) getopt$(OBJEXT) getopt1$(OBJEXT) $(DEVICEOBJ) $(LDDEPS) 54 | $(CC) $(LDFLAGS) -o $@ cpmchmod$(OBJEXT) cpmfs$(OBJEXT) getopt$(OBJEXT) getopt1$(OBJEXT) $(DEVICEOBJ) $(LIBS) 55 | 56 | cpmchattr$(EXEEXT): cpmchattr$(OBJEXT) cpmfs$(OBJEXT) getopt$(OBJEXT) getopt1$(OBJEXT) $(DEVICEOBJ) $(LDDEPS) 57 | $(CC) $(LDFLAGS) -o $@ cpmchattr$(OBJEXT) cpmfs$(OBJEXT) getopt$(OBJEXT) getopt1$(OBJEXT) $(DEVICEOBJ) $(LIBS) 58 | 59 | mkfs.cpm$(EXEEXT): mkfs.cpm$(OBJEXT) cpmfs$(OBJEXT) getopt$(OBJEXT) getopt1$(OBJEXT) $(DEVICEOBJ) $(LDDEPS) 60 | $(CC) $(LDFLAGS) -o $@ mkfs.cpm$(OBJEXT) cpmfs$(OBJEXT) getopt$(OBJEXT) getopt1$(OBJEXT) $(DEVICEOBJ) $(LIBS) 61 | 62 | fsck.cpm$(EXEEXT): fsck.cpm$(OBJEXT) cpmfs$(OBJEXT) getopt$(OBJEXT) getopt1$(OBJEXT) $(DEVICEOBJ) $(LDDEPS) 63 | $(CC) $(LDFLAGS) -o $@ fsck.cpm$(OBJEXT) cpmfs$(OBJEXT) getopt$(OBJEXT) getopt1$(OBJEXT) $(DEVICEOBJ) $(LIBS) 64 | 65 | fsed.cpm$(EXEEXT): fsed.cpm$(OBJEXT) cpmfs$(OBJEXT) getopt$(OBJEXT) getopt1$(OBJEXT) $(DEVICEOBJ) $(LDDEPS) 66 | $(CC) $(LDFLAGS) -o $@ fsed.cpm$(OBJEXT) cpmfs$(OBJEXT) getopt$(OBJEXT) getopt1$(OBJEXT) $(DEVICEOBJ) $(LIBS) 67 | 68 | fsck.test: fsck.cpm 69 | -./fsck.cpm -f ibm-3740 -n badfs/status 70 | -./fsck.cpm -f ibm-3740 -n badfs/extno 71 | -./fsck.cpm -f ibm-3740 -n badfs/lcr 72 | -./fsck.cpm -f ibm-3740 -n badfs/name 73 | -./fsck.cpm -f ibm-3740 -n badfs/extension 74 | -./fsck.cpm -f ibm-3740 -n badfs/blocknumber 75 | -./fsck.cpm -f ibm-3740 -n badfs/recordcount 76 | -./fsck.cpm -f ibm-3740 -n badfs/hugecom 77 | -./fsck.cpm -f ibm-3740 -n badfs/timestamps 78 | -./fsck.cpm -f ibm-3740 -n badfs/multipleblocks 79 | -./fsck.cpm -f ibm-3740 -n badfs/doubleext 80 | -./fsck.cpm -f pcw -n badfs/label 81 | 82 | install: all 83 | [ -d $(DESTDIR)$(MANDIR)/man1 ] || $(INSTALL) -m 755 -d $(DESTDIR)$(MANDIR)/man1 84 | [ -d $(DESTDIR)$(MANDIR)/man5 ] || $(INSTALL) -m 755 -d $(DESTDIR)$(MANDIR)/man5 85 | [ -d $(DESTDIR)$(BINDIR) ] || $(INSTALL) -m 755 -d $(DESTDIR)$(BINDIR) 86 | $(INSTALL) -m 755 cpmls $(DESTDIR)$(BINDIR)/cpmls 87 | $(INSTALL) -m 755 cpmcp $(DESTDIR)$(BINDIR)/cpmcp 88 | $(INSTALL) -m 755 cpmrm $(DESTDIR)$(BINDIR)/cpmrm 89 | $(INSTALL) -m 755 cpmchmod $(DESTDIR)$(BINDIR)/cpmchmod 90 | $(INSTALL) -m 755 cpmchattr $(DESTDIR)$(BINDIR)/cpmchattr 91 | $(INSTALL) -m 755 mkfs.cpm $(DESTDIR)$(BINDIR)/mkfs.cpm 92 | $(INSTALL) -m 755 fsck.cpm $(DESTDIR)$(BINDIR)/fsck.cpm 93 | [ "$(FSED_CPM)" == '' ] || $(INSTALL) -m 755 fsed.cpm $(DESTDIR)$(BINDIR)/fsed.cpm 94 | $(INSTALL_DATA) diskdefs $(DESTDIR)$(datarootdir)/diskdefs 95 | $(INSTALL_DATA) cpmls.1 $(DESTDIR)$(MANDIR)/man1/cpmls.1 96 | $(INSTALL_DATA) cpmcp.1 $(DESTDIR)$(MANDIR)/man1/cpmcp.1 97 | $(INSTALL_DATA) cpmrm.1 $(DESTDIR)$(MANDIR)/man1/cpmrm.1 98 | $(INSTALL_DATA) cpmchmod.1 $(DESTDIR)$(MANDIR)/man1/cpmchmod.1 99 | $(INSTALL_DATA) cpmchattr.1 $(DESTDIR)$(MANDIR)/man1/cpmchattr.1 100 | $(INSTALL_DATA) mkfs.cpm.1 $(DESTDIR)$(MANDIR)/man1/mkfs.cpm.1 101 | $(INSTALL_DATA) fsck.cpm.1 $(DESTDIR)$(MANDIR)/man1/fsck.cpm.1 102 | $(INSTALL_DATA) fsed.cpm.1 $(DESTDIR)$(MANDIR)/man1/fsed.cpm.1 103 | $(INSTALL_DATA) cpm.5 $(DESTDIR)$(MANDIR)/man5/cpm.5 104 | $(INSTALL_DATA) diskdefs.5 $(DESTDIR)$(MANDIR)/man5/diskdefs.5 105 | 106 | clean: 107 | rm -f *$(OBJEXT) 108 | 109 | distclean: clean 110 | rm -rf $(ALL) autom4te.cache config.log config.cache config.h config.status Makefile *.out 111 | 112 | tar: distclean 113 | (b=`pwd`; b=`basename $$b`; cd ..; tar zcvf $$b.tar.gz $$b) 114 | 115 | depend: 116 | $(MAKEDEPEND) $(CPPFLAGS) *.c >.depend 117 | 118 | include .depend 119 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | Changes since 2.20: 2 | 3 | o rc759 diskdef renamed to rc75x, as it works for the series 4 | o diskdefs.5 added 5 | o Many disk formats from Larry Kraemer added 6 | o Renamed ampdsdd to ampro400d for consistency with libdsk and because 7 | ampdsdd very likely was wrong 8 | o Check for invalid block size 9 | o Output line number for diskdefs errors 10 | o Correctly output create or access time for CP/M 3 in cpmls 11 | o Spectravideo SVI-728 diskdef added 12 | o $DESTDIR support 13 | o Correctly handle empty files 14 | o Fix block allocation for large directories. 15 | o Fix time stamp conversion 16 | o Allow user number 16-31 for CP/M 2.2 17 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This package allows to access CP/M file systems similar to the well-known 2 | mtools package, which accesses MSDOS file systems. I use it for file 3 | exchange with a Z80-PC simulator, but it works on floppy devices as well. 4 | Currently it contains: 5 | 6 | o cpmls - list sorted directory with output similar to ls, DIR, P2DOS 7 | DIR and CP/M3 DIR[FULL] 8 | o cpmcp - copy files from and to CP/M file systems 9 | o cpmrm - erase files from CP/M file systems 10 | o cpmchmod - change file permissions 11 | o cpmchattr - change file attributes 12 | o mkfs.cpm - make a CP/M file system 13 | o fsck.cpm - check and repair a CP/M file system (only simple errors 14 | can be repaired so far). Some images of broken file systems are provided 15 | for testing. 16 | o fsed.cpm - view CP/M file system 17 | o manual pages for everything including the CP/M file system format 18 | 19 | All CP/M file system features are supported. Password protection 20 | is ignored, because passwords are easy to decrypt, but a pseudo file 21 | [passwd] contains them, if you are curious what your old password has 22 | been. The disk label is read as special file [label]. User numbers 23 | are specified as user:file. 24 | 25 | Cpmtools should compile and work out of the box on each POSIX compliant 26 | system. It can be additionally compiled for Win32 systems. The source 27 | is available as a GNU zipped tape archive from: 28 | 29 | http://www.moria.de/~michael/cpmtools/ 30 | 31 | This program is free software; you can redistribute it and/or modify it 32 | under the terms of the GNU General Public License as published by the 33 | Free Software Foundation; either version 3 of the License, or (at your 34 | option) any later version. 35 | 36 | This program is distributed in the hope that it will be useful, but 37 | WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 38 | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 39 | for more details. 40 | 41 | You should have received a copy of the GNU General Public License along 42 | with this program. If not, write to the Free Software Foundation, Inc., 43 | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build status](https://ci.appveyor.com/api/projects/status/2ogybcn80lyv9rj2/branch/cpm4l/cpmtools-2.21?svg=true)](https://ci.appveyor.com/project/rexut/cpmtools/branch/cpm4l/cpmtools-2.21) 2 | [![Build Status](https://travis-ci.org/lipro-cpm4l/cpmtools.svg?branch=cpm4l%2Fcpmtools-2.21)](https://travis-ci.org/lipro-cpm4l/cpmtools) 3 | 4 | cpmtools - Tools to access CP/M file systems 5 | ============================================ 6 | 7 | This package allows to access CP/M file systems similar to the well-known 8 | mtools package, which accesses MSDOS file systems. It can be used for file 9 | exchange with a Z80-PC simulator, but it works on floppy devices as well. 10 | 11 | Currently it contains: 12 | 13 | - `cpmls` - list sorted directory with output similar to ls, DIR, P2DOS DIR 14 | and CP/M3 DIR 15 | - `cpmcp` - copy files from and to CP/M file systems 16 | - `cpmrm` - erase files from CP/M file systems 17 | - `cpmchmod` - change file permissions 18 | - `cpmchattr` - change file attributes 19 | - `mkfs.cpm` - make a CP/M file system 20 | - `fsck.cpm` - check and repair a CP/M file system 21 | - `fsed.cpm` - view CP/M file system 22 | - manual pages for everything including the CP/M file system format 23 | 24 | All features of CP/M file systems are supported. 25 | 26 | ## Compilation 27 | 28 | ### Requirements 29 | 30 | You will need an ANSI standard C compiler, prefered is the GNU CC. 31 | Optional you can build against the disk image library `libdsk`. 32 | You should by able to compile and work out of the box on each POSIX 33 | compliant system (Linux, BSD and MacOS). It can be additionally 34 | compiled for Win32 or Cygwin systems. 35 | 36 | ```bash 37 | sudo apt-get install libncurses-dev 38 | ``` 39 | 40 | **Only when you want to compile against the disk image library libdsk**: 41 | ```bash 42 | sudo apt-get install libdsk4-dev ### OR: libdsk5-dev 43 | ``` 44 | 45 | ### Get the Code 46 | 47 | ```bash 48 | git clone https://github.com/lipro-cpm4l/cpmtools.git 49 | cd cpmtools 50 | ``` 51 | 52 | ### Build and install the binary 53 | 54 | ```bash 55 | ./configure 56 | make all 57 | sudo make install 58 | ``` 59 | 60 | ## Documentation 61 | 62 | - Manpage [CP/M disk and file system format](cpm.ps) (cpm.5) 63 | - Abstract of the [floppy user guide](http://www.moria.de/~michael/floppy/). 64 | The user guide is available as: 65 | [PostScript](http://www.moria.de/~michael/floppy/floppy.ps) and 66 | [PostScript](http://www.moria.de/~michael/floppy/floppy.pdf) file. 67 | - Debian [[HOWTO]](http://forums.debian.net/viewtopic.php?f=16&t=112244) 68 | "Access CP/M Floppy's via libdsk & cpmtools" 69 | - [[RC2014-Z80]](https://groups.google.com/d/msg/rc2014-z80/VD22SEht0PY/V3MYDmK4AgAJ) 70 | "Using cpmtools to copy CP/M software to the compact flash card" 71 | - [[RC2014-Z80]](https://groups.google.com/d/msg/rc2014-z80/CExA08NHClg/erg4P84XCAAJ) 72 | "82c55 IDE adapter - FATFS for CP/M" 73 | - [CP/M 2.2 for Exidy FDS](www.vcfed.org/forum/showthread.php?73156) 74 | (Floppy Disk Subsystem for the Sorcerer) 75 | - [[CP/M-68K]](http://home.earthlink.net/~schultdw/cpm68/) 76 | "In order to use cpmtools you must either use a ..." 77 | - Aminet [Retro-Computing](http://aminet.net/docs/hard/Retro-Computing.pdf) 78 | © 2008 – 2015 by Peter Sieg, PDF in German, 2015/08, 79 | [Aminet Package Info](http://aminet.net/package/docs/hard/Retro-Computing), 80 | [Peter Sieg at GitHub](https://github.com/petersieg/Retro-Computing) 81 | 82 | --- 83 | 84 | This is an unofficial fork! 85 | =========================== 86 | 87 | Original written by Michael Haardt and John Elliott 88 | and distributed at first under the GNU General 89 | Public License version 2 (original) and now version 3. 90 | 91 | *Primary-site*: http://www.moria.de/~michael/cpmtools/ 92 | 93 | ## License terms and liability 94 | 95 | The author provides the software in accordance with the terms of 96 | the GNU-GPL. The use of the software is free of charge and is 97 | therefore only at your own risk! **No warranty or liability!** 98 | 99 | **Any guarantee and liability is excluded!** 100 | 101 | ## Authorship 102 | 103 | *Primary-site*: http://www.moria.de/~michael/cpmtools/ 104 | 105 | ### Source code 106 | 107 | **Michael Haardt is the originator of the C source code and** 108 | as well as the associated scripts, descriptions and help files. 109 | This part is released and distributed under the GNU General 110 | Public License (GNU-GPL) Version 3. 111 | 112 | **A few parts of the source code are based on the work of other 113 | authors:** 114 | 115 | > - **John Elliott**: 116 | > LibDsk integration and bringing cpmtools to Windows. 117 | > - **Bill Buckels**: 118 | > Building cpmtools-2.9 in Windows XP, the Cygwin port. 119 | > - **David Schmidt**, **Udo Munk**, **Peter Dassow**: 120 | > For Cygwin feedback. 121 | > - **Stevo Tarkin**, **Volker Pohlers**: 122 | > For Msys feedback. 123 | > - **Rolf Harmann**: 124 | > For Linux feedback. 125 | > - **Richard Brady**: 126 | > who may or may not know watfor:) 127 | > - **Raoul Golan**: 128 | > Contributed code to read apple II disk images. 129 | 130 | *see*: [COPYING](COPYING), [README](README), 131 | [README.win32.cygwin.txt](README.win32.cygwin.txt), 132 | [README.win32-libdsk](README.win32-libdsk) 133 | -------------------------------------------------------------------------------- /README.win32-libdsk: -------------------------------------------------------------------------------- 1 | Under Windows 95/98, if a program name has more than one dot, you have to 2 | add the ".exe" extension to the command. So you should use "fsck.cpm.exe" 3 | rather than just "fsck.cpm". 4 | 5 | If you have appropriate rights, the CPMTOOLS should be able to access 6 | the floppy drive by using "A:" or "B:" as the name of the disc image. 7 | "mkfs.cpm" and "fsed.cpm" don't have this capability. 8 | 9 | John Elliott, 18 June 2000 10 | -------------------------------------------------------------------------------- /badfs/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Dummy makefile 3 | # 4 | all: 5 | clean: 6 | distclean: 7 | 8 | -------------------------------------------------------------------------------- /badfs/blocknumber: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lipro-cpm4l/cpmtools/e534e20c15973a9559e981efb498a102020e5db7/badfs/blocknumber -------------------------------------------------------------------------------- /badfs/doubleext: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lipro-cpm4l/cpmtools/e534e20c15973a9559e981efb498a102020e5db7/badfs/doubleext -------------------------------------------------------------------------------- /badfs/extension: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lipro-cpm4l/cpmtools/e534e20c15973a9559e981efb498a102020e5db7/badfs/extension -------------------------------------------------------------------------------- /badfs/extno: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lipro-cpm4l/cpmtools/e534e20c15973a9559e981efb498a102020e5db7/badfs/extno -------------------------------------------------------------------------------- /badfs/hugecom: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lipro-cpm4l/cpmtools/e534e20c15973a9559e981efb498a102020e5db7/badfs/hugecom -------------------------------------------------------------------------------- /badfs/label: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lipro-cpm4l/cpmtools/e534e20c15973a9559e981efb498a102020e5db7/badfs/label -------------------------------------------------------------------------------- /badfs/lcr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lipro-cpm4l/cpmtools/e534e20c15973a9559e981efb498a102020e5db7/badfs/lcr -------------------------------------------------------------------------------- /badfs/multipleblocks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lipro-cpm4l/cpmtools/e534e20c15973a9559e981efb498a102020e5db7/badfs/multipleblocks -------------------------------------------------------------------------------- /badfs/name: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lipro-cpm4l/cpmtools/e534e20c15973a9559e981efb498a102020e5db7/badfs/name -------------------------------------------------------------------------------- /badfs/recordcount: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lipro-cpm4l/cpmtools/e534e20c15973a9559e981efb498a102020e5db7/badfs/recordcount -------------------------------------------------------------------------------- /badfs/status: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lipro-cpm4l/cpmtools/e534e20c15973a9559e981efb498a102020e5db7/badfs/status -------------------------------------------------------------------------------- /badfs/timestamps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lipro-cpm4l/cpmtools/e534e20c15973a9559e981efb498a102020e5db7/badfs/timestamps -------------------------------------------------------------------------------- /config.h.in: -------------------------------------------------------------------------------- 1 | #define HAVE_FCNTL_H 0 2 | #define HAVE_LIMITS_H 0 3 | #define HAVE_UNISTD_H 0 4 | #define HAVE_WINDOWS_H 0 5 | #define HAVE_WINIOCTL_H 0 6 | #define HAVE_LIBDSK_H 0 7 | #define HAVE_SYS_TYPES_H 0 8 | #define HAVE_SYS_STAT_H 0 9 | #define HAVE_MODE_T 0 10 | #define NEED_NCURSES 0 11 | #define HAVE_NCURSES_NCURSES_H 0 12 | 13 | #if HAVE_SYS_STAT_H 14 | #include 15 | #endif 16 | 17 | #if HAVE_SYS_TYPES_H 18 | #include 19 | #endif 20 | 21 | #if HAVE_LIMITS_H 22 | #include 23 | #endif 24 | 25 | #if HAVE_UNISTD_H 26 | #include 27 | #endif 28 | 29 | #if HAVE_WINDOWS_H 30 | #include 31 | #endif 32 | 33 | #if HAVE_WINIOCTL_H 34 | #include 35 | #endif 36 | 37 | #if HAVE_LIBDSK_H 38 | #include 39 | #endif 40 | 41 | #if HAVE_FCNTL_H 42 | #include 43 | #endif 44 | 45 | #ifndef _POSIX_PATH_MAX 46 | #define _POSIX_PATH_MAX _MAX_PATH 47 | #endif 48 | 49 | #include 50 | 51 | /* Define either for large file support, if your OS needs them. */ 52 | #undef _FILE_OFFSET_BITS 53 | #undef _LARGE_FILES 54 | 55 | /* Define if using dmalloc */ 56 | #undef USE_DMALLOC 57 | -------------------------------------------------------------------------------- /configure.in: -------------------------------------------------------------------------------- 1 | AC_INIT(cpmfs.c) 2 | AC_CONFIG_HEADER(config.h) 3 | AC_CANONICAL_HOST 4 | VERSION=2.21 5 | UPDATED='Jan 23, 2019' 6 | 7 | DEVICE="posix" 8 | 9 | if test "$prefix" = NONE 10 | then 11 | case $host in 12 | *-linux-*) 13 | ;; 14 | *-pc-mingw32) 15 | CFLAGS_LIBDSK=-DNOTWINDLL 16 | ;; 17 | esac 18 | fi 19 | 20 | AC_PROG_CC 21 | AC_PROG_INSTALL 22 | AC_PROG_CPP 23 | 24 | if test "$GCC" = yes 25 | then 26 | CFLAGS="${CFLAGS} ${EXTRA_GCFLAGS}-pipe -Wall -Wextra -Wshadow -Wno-unused-parameter -Wunused -Wbad-function-cast -Wmissing-prototypes -Wstrict-prototypes -Wcast-align -Wcast-qual -Wpointer-arith -Wwrite-strings -Wmissing-declarations -Wnested-externs -Wundef -pedantic -fno-common" 27 | LDFLAGS="${LDFLAGS} ${EXTRA_GLDFLAGS}-g" 28 | else 29 | CFLAGS="${CFLAGS} ${EXTRA_CFLAGS}" 30 | LDFLAGS="${LDFLAGS} ${EXTRA_LDFLAGS}" 31 | fi 32 | 33 | AC_CYGWIN 34 | AC_MINGW32 35 | dnl Choose between posix and win32 drivers... 36 | DEVICE="posix" 37 | 38 | DISKDEFS='${datarootdir}/diskdefs' 39 | 40 | if test "$CYGWIN" = "yes" 41 | then 42 | DEVICE="win32" 43 | # DISKDEFS='%USERPROFILE%/diskdefs' 44 | fi 45 | if test "$MINGW32" = "yes" 46 | then 47 | DEVICE="win32" 48 | # DISKDEFS='%USERPROFILE%\\diskdefs' 49 | fi 50 | 51 | AC_ARG_WITH(diskdefs,[ --with-diskdefs Specify diskdefs location], 52 | [DISKDEFS="$withval"], [DISKDEFS="$DISKDEFS"]) 53 | AC_ARG_WITH(defformat,[ --with-defformat Specify default format (ibm-3740)], 54 | [DEFFORMAT="$withval"], [DEFFORMAT="ibm-3740"]) 55 | AC_ARG_WITH(libdsk, [ --with-libdsk Specify path to libdsk library], 56 | [LIBDSK="$withval"], [LIBDSK=""]) 57 | AC_ARG_WITH(dmalloc, [ --with-dmalloc Specify path to dmalloc library], 58 | [CPPFLAGS="$CPPFLAGS -I$with_dmalloc/include" 59 | LDFLAGS="$LDFLAGS -L$with_dmalloc/lib" 60 | LIBS="$LIBS -ldmalloc" 61 | AC_DEFINE(USE_DMALLOC)]) 62 | 63 | dnl Check for curses. If not found, don't build fsed.cpm 64 | dnl Try both curses and ncurses 65 | AC_CHECK_LIB(curses, printw, FSED_CPM=fsed.cpm LIBS="-lcurses $LIBS", FSED_CPM=) 66 | if test x"$FSED_CPM" = x""; then 67 | AC_CHECK_LIB(ncurses, printw, FSED_CPM=fsed.cpm LIBS="-lncurses $LIBS", FSED_CPM=) 68 | if test x"$FSED_CPM" != x""; then 69 | AC_DEFINE(NEED_NCURSES) 70 | AC_CHECK_HEADERS(ncurses/ncurses.h,have_ncurses_ncurses_h=yes) 71 | fi 72 | fi 73 | 74 | dnl If using libdsk, check it's available. 75 | if test "$LIBDSK" != ""; then 76 | DEVICE="libdsk" 77 | CPPFLAGS="$CPPFLAGS -I$LIBDSK/include" 78 | CFLAGS="$CFLAGS -I$LIBDSK/include $CFLAGS_LIBDSK" 79 | LDFLAGS="$LDFLAGS -L$LIBDSK/lib" 80 | AC_CHECK_LIB(dsk, dsk_open) 81 | AC_CHECK_HEADERS(libdsk.h, ,[echo "No libdsk.h - aborting"; exit 1]) 82 | fi 83 | 84 | dnl If using win32, check it's available. 85 | if test x"$DEVICE" = x"win32"; then 86 | AC_CHECK_HEADERS(windows.h, ,[echo "Device win32, but not found - aborting"; exit 1] ) 87 | AC_CHECK_HEADERS(winioctl.h, ,[echo "Device win32, but not found - aborting"; exit 1], 88 | [#ifdef HAVE_WINDOWS_H 89 | #include 90 | #endif 91 | ]) 92 | fi 93 | 94 | dnl Checks for header files. 95 | AC_HEADER_STDC 96 | AC_CHECK_HEADERS(fcntl.h sys/types.h sys/stat.h limits.h unistd.h) 97 | 98 | dnl Checks for typedefs, structures, and compiler characteristics. 99 | AC_C_CONST 100 | AC_TYPE_MODE_T 101 | AC_TYPE_OFF_T 102 | AC_TYPE_PID_T 103 | AC_TYPE_SIZE_T 104 | AC_STRUCT_TM 105 | AC_EXEEXT 106 | AC_OBJEXT 107 | AC_SYS_LARGEFILE 108 | 109 | dnl add EXE extension to fsed.cpm 110 | if test x"$FSED_CPM" != x""; then 111 | FSED_CPM="$FSED_CPM$EXEEXT" 112 | fi 113 | 114 | dnl Checks for library functions. 115 | AC_FUNC_MEMCMP 116 | AC_FUNC_STRFTIME 117 | AC_CHECK_FUNCS(mktime strerror) 118 | 119 | AC_SUBST(LDLIBS) 120 | AC_SUBST(LDDEPS) 121 | AC_SUBST(DEVICE) 122 | eval DATADIR=$datadir 123 | AC_SUBST(DATADIR) 124 | AC_SUBST(DISKDEFS) 125 | AC_SUBST(DEFFORMAT) 126 | AC_SUBST(FSED_CPM) 127 | AC_SUBST(UPDATED) 128 | AC_OUTPUT(Makefile cpm.5 cpmchattr.1 cpmchmod.1 cpmcp.1 cpmls.1 cpmrm.1 fsck.cpm.1 fsed.cpm.1 mkfs.cpm.1 diskdefs.5) 129 | -------------------------------------------------------------------------------- /cpm.5: -------------------------------------------------------------------------------- 1 | .\" Believe it or not, reportedly there are nroffs which do not know \(en 2 | .if n .ds en - 3 | .if t .ds en \(en 4 | .TH CPM 5 "Jan 23, 2019" "CP/M tools" "File formats" 5 | .SH NAME \"{{{roff}}}\"{{{ 6 | cpm \- CP/M disk and file system format 7 | .\"}}} 8 | .SH DESCRIPTION \"{{{ 9 | .SS "Format descrptions" \"{{{ 10 | CP/M format may be specified by either the format name in a 11 | diskdefs file, or by including the entire "diskdef" format. 12 | For example: 13 | .RS 14 | .sp 15 | -f name 16 | .br 17 | -f `cat mydiskdef` 18 | .br 19 | -f "diskdef; seclen 512; tracks 512; ... end" 20 | .sp 21 | .RE 22 | A single diskdef must not begin with a comment, in other words it must 23 | begin with "diskdef". The name is not required. 24 | .\"}}} 25 | .SS "Characteristic sizes" \"{{{ 26 | Each CP/M disk format is described by the following specific sizes: 27 | .RS 28 | .sp 29 | Sector size in bytes 30 | .br 31 | Number of tracks 32 | .br 33 | Number of sectors 34 | .br 35 | Block size 36 | .br 37 | Number of directory entries 38 | .br 39 | Logical sector skew 40 | .br 41 | Number of reserved system tracks (optional) 42 | .br 43 | Offset to start of volume (optional and not covered by operating system, 44 | but disk driver specific) 45 | .sp 46 | .RE 47 | A block is the smallest allocatable storage unit. CP/M supports block 48 | sizes of 1024, 2048, 4096, 8192 and 16384 bytes. Unfortunately, this 49 | format specification is not stored on the disk and there are lots of 50 | formats. Accessing a block is performed by accessing its sectors, which 51 | are stored with the given software skew. \fBcpmtools\fP always counts 52 | sectors starting with 0, as it deals with logical sectors. CP/M uses physical 53 | sectors in the skew table, which often start with 1. 54 | .\"}}} 55 | .SS "Device areas" \"{{{ 56 | A CP/M disk contains four areas: 57 | .RS 58 | .sp 59 | Volume offset (optional and not covered by operating system, but disk driver specific) 60 | .br 61 | System tracks (optional) 62 | .br 63 | Directory 64 | .br 65 | Data 66 | .sp 67 | .RE 68 | The system tracks store the boot loader and CP/M itself. In order to save 69 | disk space, there are non-bootable formats which omit those system tracks. 70 | The term \fIdisk capacity\fP always excludes the space for system tracks. 71 | Note that there is no bitmap or list for free blocks. When accessing a 72 | drive for the first time, CP/M builds this bitmap in core from the directory. 73 | .LP 74 | A hard disk can have the additional notion of a \fIvolume offset\fP to 75 | locate the start of the drive image (which may or may not have system 76 | tracks associated with it). The base unit for volume offset is byte 77 | count from the beginning of the physical disk, but specifiers of 78 | \fIK\fP, \fIM\fP, \fIT\fP or \fIS\fP may be appended to denote 79 | kilobytes, megabytes, tracks or sectors. If provided, a specifier 80 | must immediately follow the numeric value with no whitespace. For 81 | convenience upper and lower case are both accepted and only the first 82 | letter is significant, thus 2KB, 8MB, 1000trk and 16sec are valid 83 | values. The \fBoffset\fP must appear subsequent to track, sector and sector 84 | length values for the sector and track units to work. 85 | .\"}}} 86 | .SS "Directory entries" \"{{{ 87 | The directory is a sequence of directory entries (also called extents), 88 | which contain 32 bytes of the following structure: 89 | .RS 90 | .sp 91 | .ta 3n 6n 9n 12n 15n 18n 21n 24n 27n 30n 33n 36n 39n 42n 45n 92 | St F0 F1 F2 F3 F4 F5 F6 F7 E0 E1 E2 Xl Bc Xh Rc 93 | .br 94 | Al Al Al Al Al Al Al Al Al Al Al Al Al Al Al Al 95 | .sp 96 | .RE 97 | .\"{{{ St = status 98 | \fBSt\fP is the status; possible values are: 99 | .RS 100 | .sp 101 | 0\*(en15: used for file, status is the user number 102 | .br 103 | 16\*(en31: used for file, status is the user number (P2DOS) 104 | or used for password extent (CP/M 3 or higher) 105 | .br 106 | 32: disc label 107 | .br 108 | 33: time stamp (P2DOS) 109 | .br 110 | 0xE5: unused 111 | .sp 112 | .RE 113 | .\"}}} 114 | .LP 115 | .\"{{{ F0-E2 = file name and extension 116 | \fBF0\*(enE2\fP are the file name and its extension. They may consist of 117 | any printable 7 bit ASCII character but: \fB< > . , ; : = ? * [ ]\fP. 118 | The file name must not be empty, the extension may be empty. Both are 119 | padded with blanks. The highest bit of each character of the file name 120 | and extension is used as attribute. The attributes have the following 121 | meaning: 122 | .RS 123 | .sp 124 | F0: requires set wheel byte (Backgrounder II) 125 | .br 126 | F1: public file (P2DOS, ZSDOS), forground-only command (Backgrounder II) 127 | .br 128 | F2: date stamp (ZSDOS), background-only commands (Backgrounder II) 129 | .br 130 | F7: wheel protect (ZSDOS) 131 | .br 132 | E0: read-only 133 | .br 134 | E1: system file 135 | .br 136 | E2: archived 137 | .sp 138 | .RE 139 | Public files (visible under each user number) are not supported by CP/M 140 | 2.2, but there is a patch and some free CP/M clones support them without 141 | any patches. 142 | .LP 143 | The wheel byte is (by default) the memory location at 0x4b. If it is 144 | zero, only non-privileged commands may be executed. 145 | .\"}}} 146 | .LP 147 | .\"{{{ Xl, Xh = extent number 148 | \fBXl\fP and \fBXh\fP store the extent number. A file may use more than 149 | one directory entry, if it contains more blocks than an extent can hold. 150 | In this case, more extents are allocated and each of them is numbered 151 | sequentially with an extent number. If a physical extent stores more than 152 | 16k, it is considered to contain multiple logical extents, each pointing 153 | to 16k data, and the extent number of the last used logical extent 154 | is stored. Note: Some formats decided to always store only one logical 155 | extent in a physical extent, thus wasting extent space. CP/M 2.2 allows 156 | 512 extents per file, CP/M 3 and higher allow up to 2048. Bit 5\*(en7 of 157 | Xl are 0, bit 0\*(en4 store the lower bits of the extent number. Bit 6 158 | and 7 of Xh are 0, bit 0\*(en5 store the higher bits of the extent number. 159 | .\"}}} 160 | .LP 161 | .\"{{{ Rc, Bc = record count, byte count 162 | \fBRc\fP and \fBBc\fP determine the length of the data used by this extent. The 163 | physical extent is divided into logical extents, each of them being 16k 164 | in size (a physical extent must hold at least one logical extent, e.g. a 165 | blocksize of 1024 byte with two-byte block pointers is not allowed). 166 | Rc stores the number of 128 byte records of the last used logical extent. 167 | Bc stores the number of bytes in the last used record. The value 0 means 168 | 128 for backward compatibility with CP/M 2.2, which did not support Bc. 169 | ISX records the number of unused instead of used bytes in Bc. 170 | This only applies to files with allocated blocks. For an empty file, no 171 | block is allocated and Bc 0 has no meaning. 172 | .\"}}} 173 | .LP 174 | .\"{{{ Al = allocated blocks 175 | \fBAl\fP stores block pointers. If the disk capacity minus boot 176 | tracks but including the directory area is less than 256 blocks, Al 177 | is interpreted as 16 byte-values, otherwise as 8 double-byte-values. 178 | Since the directory area is not subtracted, the directory area starts 179 | with block 0 and files can never allocate block 0, which is why this 180 | value can be given a new meaning: A block pointer of 0 marks a hole in 181 | the file. If a hole covers the range of a full extent, the extent will 182 | not be allocated. In particular, the first extent of a file does not 183 | neccessarily have extent number 0. A file may not share blocks with other 184 | files, as its blocks would be freed if the other files is erased without 185 | a following disk system reset. CP/M returns EOF when it reaches a hole, 186 | whereas UNIX returns zero-value bytes, which makes holes invisible. 187 | .\"}}} 188 | .\"}}} 189 | .SS "Native time stamps" \"{{{ 190 | P2DOS and CP/M Plus support time stamps, which are stored in each fourth 191 | directory entry. This entry contains the time stamps for 192 | the extents using the previous three directory entries. Note that you 193 | really have time stamps for each extent, no matter if it is the first 194 | extent of a file or not. The structure of time stamp entries is: 195 | .RS 196 | .sp 197 | 1 byte status 0x21 198 | .br 199 | 8 bytes time stamp for third-last directory entry 200 | .br 201 | 2 bytes unused 202 | .br 203 | 8 bytes time stamp for second-last directory entry 204 | .br 205 | 2 bytes unused 206 | .br 207 | 8 bytes time stamp for last directory entry 208 | .sp 209 | .RE 210 | A time stamp consists of two dates: Creation and modification date (the 211 | latter being recorded when the file is closed). CP/M Plus further 212 | allows optionally to record the access instead of creation date as first 213 | time stamp. 214 | .RS 215 | .sp 216 | 2 bytes (little-endian) days starting with 1 at 01-01-1978 217 | .br 218 | 1 byte hour in BCD format 219 | .br 220 | 1 byte minute in BCD format 221 | .sp 222 | .RE 223 | All time stamps are stored in local time. 224 | .\"}}} 225 | .SS "DateStamper time stamps" \"{{{ 226 | The DateStamper software added functions to the BDOS to manage 227 | time stamps by allocating a read only file with the name "!!!TIME&.DAT" 228 | in the very first directory entry, covering the very first data 229 | blocks. It contains one entry per directory entry with the 230 | following structure of 16 bytes: 231 | .RS 232 | .sp 233 | 5 bytes create datefield 234 | .br 235 | 5 bytes access datefield 236 | .br 237 | 5 bytes modify datefield 238 | .br 239 | 1 byte magic number/checksum 240 | .sp 241 | .RE 242 | The magic number is used for the first 7 entries of each 128-byte record 243 | and contains the characters \fB!\fP, \fB!\fP, \fB!\fP, \fBT\fP, \fBI\fP, 244 | \fBM\fP and \fBE\fP. The checksum is used on every 8th entry (last entry 245 | in 128-byte record) and is the sum of the first 127 bytes of the record. 246 | Each datefield has this structure: 247 | .RS 248 | .sp 249 | 1 byte BCD coded year (no century, so it is sane assuming any year < 70 250 | means 21st century) 251 | .br 252 | 1 byte BCD coded month 253 | .br 254 | 1 byte BCD coded day 255 | .br 256 | 1 byte BCD coded hour or, if the high bit is set, the high byte of a 257 | counter for systems without real time clock 258 | .br 259 | 1 byte BCD coded minute, or the low byte of the counter 260 | .sp 261 | .DE 262 | .\"}}} 263 | .SS "Disc labels" \"{{{ 264 | CP/M Plus support disc labels, which are stored in an arbitrary directory 265 | entry. 266 | The structure of disc labels is: 267 | .RS 268 | .sp 269 | 1 byte status 0x20 270 | .br 271 | \fBF0\*(enE2\fP are the disc label 272 | .br 273 | 1 byte mode: bit 7 activates password protection, bit 6 causes time stamps on 274 | access, but 5 causes time stamps on modifications, bit 4 causes time stamps on 275 | creation and bit 0 is set when a label exists. Bit 4 and 6 are exclusively set. 276 | .br 277 | 1 byte password decode byte: To decode the password, xor this byte with the password 278 | bytes in reverse order. To encode a password, add its characters to get the 279 | decode byte. 280 | .br 281 | 2 reserved bytes 282 | .br 283 | 8 password bytes 284 | .br 285 | 4 bytes label creation time stamp 286 | .br 287 | 4 bytes label modification time stamp 288 | .sp 289 | .RE 290 | .\"}}} 291 | .SS "Passwords" \"{{{ 292 | CP/M Plus supports passwords, which are stored in an arbitrary directory 293 | entry. 294 | The structure of these entries is: 295 | .RS 296 | .sp 297 | 1 byte status (user number plus 16) 298 | .br 299 | \fBF0\*(enE2\fP are the file name and its extension. 300 | .br 301 | 1 byte password mode: bit 7 means password required for reading, bit 6 for writing 302 | and bit 5 for deleting. 303 | .br 304 | 1 byte password decode byte: To decode the password, xor this byte with the password 305 | bytes in reverse order. To encode a password, add its characters to get the 306 | decode byte. 307 | .br 308 | 2 reserved bytes 309 | .br 310 | 8 password bytes 311 | .sp 312 | .RE 313 | .\"}}} 314 | .\"}}} 315 | .SH "SEE ALSO" \"{{{ 316 | .IR mkfs.cpm (1), 317 | .IR fsck.cpm (1), 318 | .IR fsed.cpm (1), 319 | .IR cpmls (1) 320 | .\"}}} 321 | -------------------------------------------------------------------------------- /cpm.5.in: -------------------------------------------------------------------------------- 1 | .\" Believe it or not, reportedly there are nroffs which do not know \(en 2 | .if n .ds en - 3 | .if t .ds en \(en 4 | .TH CPM 5 "@UPDATED@" "CP/M tools" "File formats" 5 | .SH NAME \"{{{roff}}}\"{{{ 6 | cpm \- CP/M disk and file system format 7 | .\"}}} 8 | .SH DESCRIPTION \"{{{ 9 | .SS "Format descrptions" \"{{{ 10 | CP/M format may be specified by either the format name in a 11 | diskdefs file, or by including the entire "diskdef" format. 12 | For example: 13 | .RS 14 | .sp 15 | -f name 16 | .br 17 | -f `cat mydiskdef` 18 | .br 19 | -f "diskdef; seclen 512; tracks 512; ... end" 20 | .sp 21 | .RE 22 | A single diskdef must not begin with a comment, in other words it must 23 | begin with "diskdef". The name is not required. 24 | .\"}}} 25 | .SS "Characteristic sizes" \"{{{ 26 | Each CP/M disk format is described by the following specific sizes: 27 | .RS 28 | .sp 29 | Sector size in bytes 30 | .br 31 | Number of tracks 32 | .br 33 | Number of sectors 34 | .br 35 | Block size 36 | .br 37 | Number of directory entries 38 | .br 39 | Logical sector skew 40 | .br 41 | Number of reserved system tracks (optional) 42 | .br 43 | Offset to start of volume (optional and not covered by operating system, 44 | but disk driver specific) 45 | .sp 46 | .RE 47 | A block is the smallest allocatable storage unit. CP/M supports block 48 | sizes of 1024, 2048, 4096, 8192 and 16384 bytes. Unfortunately, this 49 | format specification is not stored on the disk and there are lots of 50 | formats. Accessing a block is performed by accessing its sectors, which 51 | are stored with the given software skew. \fBcpmtools\fP always counts 52 | sectors starting with 0, as it deals with logical sectors. CP/M uses physical 53 | sectors in the skew table, which often start with 1. 54 | .\"}}} 55 | .SS "Device areas" \"{{{ 56 | A CP/M disk contains four areas: 57 | .RS 58 | .sp 59 | Volume offset (optional and not covered by operating system, but disk driver specific) 60 | .br 61 | System tracks (optional) 62 | .br 63 | Directory 64 | .br 65 | Data 66 | .sp 67 | .RE 68 | The system tracks store the boot loader and CP/M itself. In order to save 69 | disk space, there are non-bootable formats which omit those system tracks. 70 | The term \fIdisk capacity\fP always excludes the space for system tracks. 71 | Note that there is no bitmap or list for free blocks. When accessing a 72 | drive for the first time, CP/M builds this bitmap in core from the directory. 73 | .LP 74 | A hard disk can have the additional notion of a \fIvolume offset\fP to 75 | locate the start of the drive image (which may or may not have system 76 | tracks associated with it). The base unit for volume offset is byte 77 | count from the beginning of the physical disk, but specifiers of 78 | \fIK\fP, \fIM\fP, \fIT\fP or \fIS\fP may be appended to denote 79 | kilobytes, megabytes, tracks or sectors. If provided, a specifier 80 | must immediately follow the numeric value with no whitespace. For 81 | convenience upper and lower case are both accepted and only the first 82 | letter is significant, thus 2KB, 8MB, 1000trk and 16sec are valid 83 | values. The \fBoffset\fP must appear subsequent to track, sector and sector 84 | length values for the sector and track units to work. 85 | .\"}}} 86 | .SS "Directory entries" \"{{{ 87 | The directory is a sequence of directory entries (also called extents), 88 | which contain 32 bytes of the following structure: 89 | .RS 90 | .sp 91 | .ta 3n 6n 9n 12n 15n 18n 21n 24n 27n 30n 33n 36n 39n 42n 45n 92 | St F0 F1 F2 F3 F4 F5 F6 F7 E0 E1 E2 Xl Bc Xh Rc 93 | .br 94 | Al Al Al Al Al Al Al Al Al Al Al Al Al Al Al Al 95 | .sp 96 | .RE 97 | .\"{{{ St = status 98 | \fBSt\fP is the status; possible values are: 99 | .RS 100 | .sp 101 | 0\*(en15: used for file, status is the user number. CP/M 2.2 only documents 102 | 0\*(en15 and CCP and PIP only offer those, but the BDOS allows to use 0\*(en31. 103 | .br 104 | 16\*(en31: used for file, status is the user number (P2DOS, CP/M 2.2) 105 | or used for password extent (CP/M 3 or higher) 106 | .br 107 | 32: disc label 108 | .br 109 | 33: time stamp (P2DOS) 110 | .br 111 | 0xE5: unused 112 | .sp 113 | .RE 114 | .\"}}} 115 | .LP 116 | .\"{{{ F0-E2 = file name and extension 117 | \fBF0\*(enE2\fP are the file name and its extension. They may consist of 118 | any printable 7 bit ASCII character but: \fB< > . , ; : = ? * [ ]\fP. 119 | The file name must not be empty, the extension may be empty. Both are 120 | padded with blanks. The highest bit of each character of the file name 121 | and extension is used as attribute. The attributes have the following 122 | meaning: 123 | .RS 124 | .sp 125 | F0: requires set wheel byte (Backgrounder II) 126 | .br 127 | F1: public file (P2DOS, ZSDOS), forground-only command (Backgrounder II) 128 | .br 129 | F2: date stamp (ZSDOS), background-only commands (Backgrounder II) 130 | .br 131 | F7: wheel protect (ZSDOS) 132 | .br 133 | E0: read-only 134 | .br 135 | E1: system file 136 | .br 137 | E2: archived 138 | .sp 139 | .RE 140 | Public files (visible under each user number) are not supported by CP/M 141 | 2.2, but there is a patch and some free CP/M clones support them without 142 | any patches. 143 | .LP 144 | The wheel byte is (by default) the memory location at 0x4b. If it is 145 | zero, only non-privileged commands may be executed. 146 | .\"}}} 147 | .LP 148 | .\"{{{ Xl, Xh = extent number 149 | \fBXl\fP and \fBXh\fP store the extent number. A file may use more than 150 | one directory entry, if it contains more blocks than an extent can hold. 151 | In this case, more extents are allocated and each of them is numbered 152 | sequentially with an extent number. If a physical extent stores more than 153 | 16k, it is considered to contain multiple logical extents, each pointing 154 | to 16k data, and the extent number of the last used logical extent 155 | is stored. Note: Some formats decided to always store only one logical 156 | extent in a physical extent, thus wasting extent space. CP/M 2.2 allows 157 | 512 extents per file, CP/M 3 and higher allow up to 2048. Bit 5\*(en7 of 158 | Xl are 0, bit 0\*(en4 store the lower bits of the extent number. Bit 6 159 | and 7 of Xh are 0, bit 0\*(en5 store the higher bits of the extent number. 160 | .\"}}} 161 | .LP 162 | .\"{{{ Rc, Bc = record count, byte count 163 | \fBRc\fP and \fBBc\fP determine the length of the data used by this extent. The 164 | physical extent is divided into logical extents, each of them being 16k 165 | in size (a physical extent must hold at least one logical extent, e.g. a 166 | blocksize of 1024 byte with two-byte block pointers is not allowed). 167 | Rc stores the number of 128 byte records of the last used logical extent. 168 | Bc stores the number of bytes in the last used record. The value 0 means 169 | 128 for backward compatibility with CP/M 2.2, which did not support Bc. 170 | ISX records the number of unused instead of used bytes in Bc. 171 | This only applies to files with allocated blocks. For an empty file, no 172 | block is allocated and Bc 0 has no meaning. 173 | .\"}}} 174 | .LP 175 | .\"{{{ Al = allocated blocks 176 | \fBAl\fP stores block pointers. If the disk capacity minus boot 177 | tracks but including the directory area is less than 256 blocks, Al 178 | is interpreted as 16 byte-values, otherwise as 8 double-byte-values. 179 | Since the directory area is not subtracted, the directory area starts 180 | with block 0 and files can never allocate block 0, which is why this 181 | value can be given a new meaning: A block pointer of 0 marks a hole in 182 | the file. If a hole covers the range of a full extent, the extent will 183 | not be allocated. In particular, the first extent of a file does not 184 | neccessarily have extent number 0. A file may not share blocks with other 185 | files, as its blocks would be freed if the other files is erased without 186 | a following disk system reset. CP/M returns EOF when it reaches a hole, 187 | whereas UNIX returns zero-value bytes, which makes holes invisible. 188 | .\"}}} 189 | .\"}}} 190 | .SS "Native time stamps" \"{{{ 191 | P2DOS and CP/M Plus support time stamps, which are stored in each fourth 192 | directory entry. This entry contains the time stamps for 193 | the extents using the previous three directory entries. Note that you 194 | really have time stamps for each extent, no matter if it is the first 195 | extent of a file or not. The structure of time stamp entries is: 196 | .RS 197 | .sp 198 | 1 byte status 0x21 199 | .br 200 | 8 bytes time stamp for third-last directory entry 201 | .br 202 | 2 bytes unused 203 | .br 204 | 8 bytes time stamp for second-last directory entry 205 | .br 206 | 2 bytes unused 207 | .br 208 | 8 bytes time stamp for last directory entry 209 | .sp 210 | .RE 211 | A time stamp consists of two dates: Creation and modification date (the 212 | latter being recorded when the file is closed). CP/M Plus further 213 | allows optionally to record the access instead of creation date as first 214 | time stamp. 215 | .RS 216 | .sp 217 | 2 bytes (little-endian) days starting with 1 at 01-01-1978 218 | .br 219 | 1 byte hour in BCD format 220 | .br 221 | 1 byte minute in BCD format 222 | .sp 223 | .RE 224 | All time stamps are stored in local time. 225 | .\"}}} 226 | .SS "DateStamper time stamps" \"{{{ 227 | The DateStamper software added functions to the BDOS to manage 228 | time stamps by allocating a read only file with the name "!!!TIME&.DAT" 229 | in the very first directory entry, covering the very first data 230 | blocks. It contains one entry per directory entry with the 231 | following structure of 16 bytes: 232 | .RS 233 | .sp 234 | 5 bytes create datefield 235 | .br 236 | 5 bytes access datefield 237 | .br 238 | 5 bytes modify datefield 239 | .br 240 | 1 byte magic number/checksum 241 | .sp 242 | .RE 243 | The magic number is used for the first 7 entries of each 128-byte record 244 | and contains the characters \fB!\fP, \fB!\fP, \fB!\fP, \fBT\fP, \fBI\fP, 245 | \fBM\fP and \fBE\fP. The checksum is used on every 8th entry (last entry 246 | in 128-byte record) and is the sum of the first 127 bytes of the record. 247 | Each datefield has this structure: 248 | .RS 249 | .sp 250 | 1 byte BCD coded year (no century, so it is sane assuming any year < 70 251 | means 21st century) 252 | .br 253 | 1 byte BCD coded month 254 | .br 255 | 1 byte BCD coded day 256 | .br 257 | 1 byte BCD coded hour or, if the high bit is set, the high byte of a 258 | counter for systems without real time clock 259 | .br 260 | 1 byte BCD coded minute, or the low byte of the counter 261 | .sp 262 | .DE 263 | .\"}}} 264 | .SS "Disc labels" \"{{{ 265 | CP/M Plus support disc labels, which are stored in an arbitrary directory 266 | entry. 267 | The structure of disc labels is: 268 | .RS 269 | .sp 270 | 1 byte status 0x20 271 | .br 272 | \fBF0\*(enE2\fP are the disc label 273 | .br 274 | 1 byte mode: bit 7 activates password protection, bit 6 causes time stamps on 275 | access, but 5 causes time stamps on modifications, bit 4 causes time stamps on 276 | creation and bit 0 is set when a label exists. Bit 4 and 6 are exclusively set. 277 | .br 278 | 1 byte password decode byte: To decode the password, xor this byte with the password 279 | bytes in reverse order. To encode a password, add its characters to get the 280 | decode byte. 281 | .br 282 | 2 reserved bytes 283 | .br 284 | 8 password bytes 285 | .br 286 | 4 bytes label creation time stamp 287 | .br 288 | 4 bytes label modification time stamp 289 | .sp 290 | .RE 291 | .\"}}} 292 | .SS "Passwords" \"{{{ 293 | CP/M Plus supports passwords, which are stored in an arbitrary directory 294 | entry. 295 | The structure of these entries is: 296 | .RS 297 | .sp 298 | 1 byte status (user number plus 16) 299 | .br 300 | \fBF0\*(enE2\fP are the file name and its extension. 301 | .br 302 | 1 byte password mode: bit 7 means password required for reading, bit 6 for writing 303 | and bit 5 for deleting. 304 | .br 305 | 1 byte password decode byte: To decode the password, xor this byte with the password 306 | bytes in reverse order. To encode a password, add its characters to get the 307 | decode byte. 308 | .br 309 | 2 reserved bytes 310 | .br 311 | 8 password bytes 312 | .sp 313 | .RE 314 | .\"}}} 315 | .\"}}} 316 | .SH "SEE ALSO" \"{{{ 317 | .IR mkfs.cpm (1), 318 | .IR fsck.cpm (1), 319 | .IR fsed.cpm (1), 320 | .IR cpmls (1) 321 | .\"}}} 322 | -------------------------------------------------------------------------------- /cpmchattr.1: -------------------------------------------------------------------------------- 1 | .TH CPMCHATTR 1 "Jan 23, 2019" "CP/M tools" "User commands" 2 | .SH NAME \"{{{roff}}}\"{{{ 3 | cpmchattr \- change file attributes on CP/M files 4 | .\"}}} 5 | .SH SYNOPSIS \"{{{ 6 | .ad l 7 | .B cpmchattr 8 | .RB [ \-f 9 | .IR format ] 10 | .I image 11 | .I attrib 12 | .I file-pattern 13 | \&... 14 | .ad b 15 | .\"}}} 16 | .SH DESCRIPTION \"{{{ 17 | \fBCpmchattr\fP changes the file attributes for files on CP/M disks. 18 | .\"}}} 19 | .SH OPTIONS \"{{{ 20 | .IP "\fB\-f\fP \fIformat\fP" 21 | Use the given CP/M disk \fIformat\fP instead of the default format. 22 | .IP "\fB\-T\fP \fIlibdsk-type\fP" 23 | libdsk driver type, e.g. \fBtele\fP for Teledisk images or \fBraw\fP for raw images 24 | (requires building cpmtools with support for libdsk). 25 | .IP "\fIattrib\fP" 26 | Set the file attributes as given. 27 | .\"}}} 28 | .SH "FILE ATTRIBUTES" \"{{{ 29 | The file attribute string can contain the characters 30 | 1,2,3,4,r,s,a,n and m. 31 | The meanings of these are: 32 | .TP 33 | .B 1-4 34 | The CP/M "user attributes" F1-F4. CP/M does not assign any 35 | meaning to these attributes, though MP/M does. 36 | .TP 37 | .B r 38 | The file is read-only. This is the same as using 39 | .I cpmchmod(1) 40 | to revoke write permissions. 41 | .TP 42 | .B s 43 | The file is a system file. This attribute can also be set by 44 | .I cpmchmod(1). 45 | .TP 46 | .B a 47 | The file has been backed up. 48 | .TP 49 | .B n 50 | Reset all attributes to zero. So the string "n1r" resets all attributes and 51 | then sets F1 and Read-Only. 52 | .TP 53 | .B m 54 | Attributes after an m are unset rather than set. The string "12m34" sets 55 | atttributes F1 and F2, and unsets F3 and F4. 56 | .\"}}} 57 | .SH "RETURN VALUE" \"{{{ 58 | Upon successful completion, exit code 0 is returned. 59 | .\"}}} 60 | .SH ERRORS \"{{{ 61 | Any errors are indicated by exit code 1. 62 | .\"}}} 63 | .SH ENVIRONMENT \"{{{ 64 | CPMTOOLSFMT Default format 65 | .\"}}} 66 | .SH FILES \"{{{ 67 | ${prefix}/share/diskdefs CP/M disk format definitions 68 | .\"}}} 69 | .SH AUTHORS \"{{{ 70 | This program is copyright 1997\(en2012 Michael Haardt 71 | and copyright 2000, 2001, 2011 John Elliott 72 | . 73 | .PP 74 | This program is free software; you can redistribute it and/or modify 75 | it under the terms of the GNU General Public License as published by 76 | the Free Software Foundation; either version 3 of the License, or 77 | (at your option) any later version. 78 | .PP 79 | This program is distributed in the hope that it will be useful, 80 | but WITHOUT ANY WARRANTY; without even the implied warranty of 81 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 82 | GNU General Public License for more details. 83 | .PP 84 | You should have received a copy of the GNU General Public License along 85 | with this program. If not, write to the Free Software Foundation, Inc., 86 | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 87 | .\"}}} 88 | .SH "SEE ALSO" \"{{{ 89 | .IR cpmls (1), 90 | .IR cpmchmod (1), 91 | .IR cpm (5) 92 | .\"}}} 93 | -------------------------------------------------------------------------------- /cpmchattr.1.in: -------------------------------------------------------------------------------- 1 | .TH CPMCHATTR 1 "@UPDATED@" "CP/M tools" "User commands" 2 | .SH NAME \"{{{roff}}}\"{{{ 3 | cpmchattr \- change file attributes on CP/M files 4 | .\"}}} 5 | .SH SYNOPSIS \"{{{ 6 | .ad l 7 | .B cpmchattr 8 | .RB [ \-f 9 | .IR format ] 10 | .I image 11 | .I attrib 12 | .I file-pattern 13 | \&... 14 | .ad b 15 | .\"}}} 16 | .SH DESCRIPTION \"{{{ 17 | \fBCpmchattr\fP changes the file attributes for files on CP/M disks. 18 | .\"}}} 19 | .SH OPTIONS \"{{{ 20 | .IP "\fB\-f\fP \fIformat\fP" 21 | Use the given CP/M disk \fIformat\fP instead of the default format. 22 | .IP "\fB\-T\fP \fIlibdsk-type\fP" 23 | libdsk driver type, e.g. \fBtele\fP for Teledisk images or \fBraw\fP for raw images 24 | (requires building cpmtools with support for libdsk). 25 | .IP "\fIattrib\fP" 26 | Set the file attributes as given. 27 | .\"}}} 28 | .SH "FILE ATTRIBUTES" \"{{{ 29 | The file attribute string can contain the characters 30 | 1,2,3,4,r,s,a,n and m. 31 | The meanings of these are: 32 | .TP 33 | .B 1-4 34 | The CP/M "user attributes" F1-F4. CP/M does not assign any 35 | meaning to these attributes, though MP/M does. 36 | .TP 37 | .B r 38 | The file is read-only. This is the same as using 39 | .I cpmchmod(1) 40 | to revoke write permissions. 41 | .TP 42 | .B s 43 | The file is a system file. This attribute can also be set by 44 | .I cpmchmod(1). 45 | .TP 46 | .B a 47 | The file has been backed up. 48 | .TP 49 | .B n 50 | Reset all attributes to zero. So the string "n1r" resets all attributes and 51 | then sets F1 and Read-Only. 52 | .TP 53 | .B m 54 | Attributes after an m are unset rather than set. The string "12m34" sets 55 | atttributes F1 and F2, and unsets F3 and F4. 56 | .\"}}} 57 | .SH "RETURN VALUE" \"{{{ 58 | Upon successful completion, exit code 0 is returned. 59 | .\"}}} 60 | .SH ERRORS \"{{{ 61 | Any errors are indicated by exit code 1. 62 | .\"}}} 63 | .SH ENVIRONMENT \"{{{ 64 | CPMTOOLSFMT Default format 65 | .\"}}} 66 | .SH FILES \"{{{ 67 | @DATADIR@/diskdefs CP/M disk format definitions 68 | .\"}}} 69 | .SH AUTHORS \"{{{ 70 | This program is copyright 1997\(en2012 Michael Haardt 71 | and copyright 2000, 2001, 2011 John Elliott 72 | . 73 | .PP 74 | This program is free software; you can redistribute it and/or modify 75 | it under the terms of the GNU General Public License as published by 76 | the Free Software Foundation; either version 3 of the License, or 77 | (at your option) any later version. 78 | .PP 79 | This program is distributed in the hope that it will be useful, 80 | but WITHOUT ANY WARRANTY; without even the implied warranty of 81 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 82 | GNU General Public License for more details. 83 | .PP 84 | You should have received a copy of the GNU General Public License along 85 | with this program. If not, write to the Free Software Foundation, Inc., 86 | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 87 | .\"}}} 88 | .SH "SEE ALSO" \"{{{ 89 | .IR cpmls (1), 90 | .IR cpmchmod (1), 91 | .IR cpm (5) 92 | .\"}}} 93 | -------------------------------------------------------------------------------- /cpmchattr.c: -------------------------------------------------------------------------------- 1 | /* #includes */ /*{{{C}}}*//*{{{*/ 2 | #include "config.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "getopt_.h" 10 | #include "cpmfs.h" 11 | 12 | #ifdef USE_DMALLOC 13 | #include 14 | #endif 15 | /*}}}*/ 16 | 17 | const char cmd[]="cpmchattr"; 18 | 19 | int main(int argc, char *argv[]) /*{{{*/ 20 | { 21 | /* variables */ /*{{{*/ 22 | const char *err; 23 | const char *image; 24 | const char *format; 25 | const char *devopts=NULL; 26 | int c,i,usage=0,exitcode=0; 27 | struct cpmSuperBlock drive; 28 | struct cpmInode root; 29 | int gargc; 30 | char **gargv; 31 | const char *attrs; 32 | /*}}}*/ 33 | 34 | /* parse options */ /*{{{*/ 35 | if (!(format=getenv("CPMTOOLSFMT"))) format=FORMAT; 36 | while ((c=getopt(argc,argv,"T:f:h?"))!=EOF) switch(c) 37 | { 38 | case 'T': devopts=optarg; break; 39 | case 'f': format=optarg; break; 40 | case 'h': 41 | case '?': usage=1; break; 42 | } 43 | 44 | if (optind>=(argc-2)) usage=1; 45 | else 46 | { 47 | image=argv[optind++]; 48 | attrs = argv[optind++]; 49 | } 50 | 51 | if (usage) 52 | { 53 | fprintf(stderr,"Usage: %s [-f format] [-T dsktype] image [NMrsa1234] pattern ...\n",cmd); 54 | exit(1); 55 | } 56 | /*}}}*/ 57 | /* open image */ /*{{{*/ 58 | if ((err=Device_open(&drive.dev, image, O_RDWR, devopts))) 59 | { 60 | fprintf(stderr,"%s: cannot open %s (%s)\n",cmd,image,err); 61 | exit(1); 62 | } 63 | if (cpmReadSuper(&drive,&root,format)==-1) 64 | { 65 | fprintf(stderr,"%s: cannot read superblock (%s)\n",cmd,boo); 66 | exit(1); 67 | } 68 | /*}}}*/ 69 | cpmglob(optind,argc,argv,&root,&gargc,&gargv); 70 | for (i=0; i and copyright 2000, 2001, 2011 John Elliott 43 | . 44 | .PP 45 | This program is free software; you can redistribute it and/or modify 46 | it under the terms of the GNU General Public License as published by 47 | the Free Software Foundation; either version 3 of the License, or 48 | (at your option) any later version. 49 | .PP 50 | This program is distributed in the hope that it will be useful, 51 | but WITHOUT ANY WARRANTY; without even the implied warranty of 52 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 53 | GNU General Public License for more details. 54 | .PP 55 | You should have received a copy of the GNU General Public License along 56 | with this program. If not, write to the Free Software Foundation, Inc., 57 | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 58 | .\"}}} 59 | .SH "SEE ALSO" \"{{{ 60 | .IR cpmls (1), 61 | .IR chmod (1), 62 | .IR cpm (5) 63 | .\"}}} 64 | -------------------------------------------------------------------------------- /cpmchmod.1.in: -------------------------------------------------------------------------------- 1 | .TH CPMCHMOD 1 "@UPDATED@" "CP/M tools" "User commands" 2 | .SH NAME \"{{{roff}}}\"{{{ 3 | cpmchmod \- change file mode on CP/M files 4 | .\"}}} 5 | .SH SYNOPSIS \"{{{ 6 | .ad l 7 | .B cpmchmod 8 | .RB [ \-f 9 | .IR format ] 10 | .I image 11 | .I mode 12 | .I file-pattern 13 | \&... 14 | .ad b 15 | .\"}}} 16 | .SH DESCRIPTION \"{{{ 17 | \fBCpmchmod\fP changes the file mode for files on CP/M files. 18 | .\"}}} 19 | .SH OPTIONS \"{{{ 20 | .IP "\fB\-f\fP \fIformat\fP" 21 | Use the given CP/M disk \fIformat\fP instead of the default format. 22 | .IP "\fB\-T\fP \fIlibdsk-type\fP" 23 | libdsk driver type, e.g. \fBtele\fP for Teledisk images or \fBraw\fP for raw images 24 | (requires building cpmtools with support for libdsk). 25 | .IP "\fImode\fP" 26 | Octal file mode, as used in \fIchmod\fP(1). 27 | .\"}}} 28 | .SH "RETURN VALUE" \"{{{ 29 | Upon successful completion, exit code 0 is returned. 30 | .\"}}} 31 | .SH ERRORS \"{{{ 32 | Any errors are indicated by exit code 1. 33 | .\"}}} 34 | .SH ENVIRONMENT \"{{{ 35 | CPMTOOLSFMT Default format 36 | .\"}}} 37 | .SH FILES \"{{{ 38 | @DATADIR@/diskdefs CP/M disk format definitions 39 | .\"}}} 40 | .SH AUTHORS \"{{{ 41 | This program is copyright 1997\(en2012 Michael Haardt 42 | and copyright 2000, 2001, 2011 John Elliott 43 | . 44 | .PP 45 | This program is free software; you can redistribute it and/or modify 46 | it under the terms of the GNU General Public License as published by 47 | the Free Software Foundation; either version 3 of the License, or 48 | (at your option) any later version. 49 | .PP 50 | This program is distributed in the hope that it will be useful, 51 | but WITHOUT ANY WARRANTY; without even the implied warranty of 52 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 53 | GNU General Public License for more details. 54 | .PP 55 | You should have received a copy of the GNU General Public License along 56 | with this program. If not, write to the Free Software Foundation, Inc., 57 | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 58 | .\"}}} 59 | .SH "SEE ALSO" \"{{{ 60 | .IR cpmls (1), 61 | .IR chmod (1), 62 | .IR cpm (5) 63 | .\"}}} 64 | -------------------------------------------------------------------------------- /cpmchmod.c: -------------------------------------------------------------------------------- 1 | /* #includes */ /*{{{C}}}*//*{{{*/ 2 | #include "config.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "getopt_.h" 11 | #include "cpmfs.h" 12 | 13 | #ifdef USE_DMALLOC 14 | #include 15 | #endif 16 | /*}}}*/ 17 | 18 | const char cmd[]="cpmchmod"; 19 | 20 | int main(int argc, char *argv[]) /*{{{*/ 21 | { 22 | /* variables */ /*{{{*/ 23 | const char *err; 24 | const char *image; 25 | const char *format; 26 | const char *devopts=NULL; 27 | int c,i,usage=0,exitcode=0; 28 | struct cpmSuperBlock drive; 29 | struct cpmInode root; 30 | int gargc; 31 | char **gargv; 32 | unsigned int mode; 33 | /*}}}*/ 34 | 35 | /* parse options */ /*{{{*/ 36 | if (!(format=getenv("CPMTOOLSFMT"))) format=FORMAT; 37 | while ((c=getopt(argc,argv,"T:f:h?"))!=EOF) switch(c) 38 | { 39 | case 'T': devopts=optarg; break; 40 | case 'f': format=optarg; break; 41 | case 'h': 42 | case '?': usage=1; break; 43 | } 44 | 45 | if (optind>=(argc-2)) usage=1; 46 | else 47 | { 48 | image=argv[optind++]; 49 | if (!sscanf(argv[optind++], "%o", &mode)) usage=1; 50 | } 51 | 52 | if (usage) 53 | { 54 | fprintf(stderr,"Usage: %s [-f format] image mode pattern ...\n",cmd); 55 | exit(1); 56 | } 57 | /*}}}*/ 58 | /* open image */ /*{{{*/ 59 | if ((err=Device_open(&drive.dev, image, O_RDWR, devopts))) 60 | { 61 | fprintf(stderr,"%s: cannot open %s (%s)\n",cmd,image,err); 62 | exit(1); 63 | } 64 | if (cpmReadSuper(&drive,&root,format)==-1) 65 | { 66 | fprintf(stderr,"%s: cannot read superblock (%s)\n",cmd,boo); 67 | exit(1); 68 | } 69 | /*}}}*/ 70 | cpmglob(optind,argc,argv,&root,&gargc,&gargv); 71 | for (i=0; i. The Windows port is copyright 2000, 2001, 2011 John Elliott 79 | . 80 | .PP 81 | This program is free software; you can redistribute it and/or modify 82 | it under the terms of the GNU General Public License as published by 83 | the Free Software Foundation; either version 3 of the License, or 84 | (at your option) any later version. 85 | .PP 86 | This program is distributed in the hope that it will be useful, 87 | but WITHOUT ANY WARRANTY; without even the implied warranty of 88 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 89 | GNU General Public License for more details. 90 | .PP 91 | You should have received a copy of the GNU General Public License along 92 | with this program. If not, write to the Free Software Foundation, Inc., 93 | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 94 | .\"}}} 95 | .SH "SEE ALSO" \"{{{ 96 | .IR cpmls (1), 97 | .IR cpm (5) 98 | .\"}}} 99 | -------------------------------------------------------------------------------- /cpmcp.1.in: -------------------------------------------------------------------------------- 1 | .TH CPMCP 1 "@UPDATED@" "CP/M tools" "User commands" 2 | .SH NAME \"{{{roff}}}\"{{{ 3 | cpmcp \- copy files from and to CP/M disks 4 | .\"}}} 5 | .SH SYNOPSIS \"{{{ 6 | .ad l 7 | .B cpmcp 8 | .RB [ \-f 9 | .IR format ] 10 | .RB [ \-p ] 11 | .RB [ \-t ] 12 | .I image 13 | \fIuser\fP\fB:\fP\fIfile\fP \fIfile\fP 14 | .br 15 | .B cpmcp 16 | .RB [ \-f 17 | .IR format ] 18 | .RB [ \-p ] 19 | .RB [ \-t ] 20 | .I image 21 | \fIuser\fP\fB:\fP\fIfile\fP ... \fIdirectory\fP 22 | .br 23 | .B cpmcp 24 | .RB [ \-f 25 | .IR format ] 26 | .RB [ \-p ] 27 | .RB [ \-t ] 28 | .I image 29 | \fIfile\fP \fIuser\fP\fB:\fP\fIfile\fP 30 | .br 31 | .B cpmcp 32 | .RB [ \-f 33 | .IR format ] 34 | .RB [ \-p ] 35 | .RB [ \-t ] 36 | .I image 37 | \fIfile\fP ... \fIuser\fP\fB:\fP 38 | .ad b 39 | .\"}}} 40 | .SH DESCRIPTION \"{{{ 41 | \fBcpmcp\fP copies one or more files to or from a CP/M disk. When copying 42 | multiple files, the last argument must be a drive or directory. The drive 43 | letter does not matter because the device is specified by the image, it is 44 | only used to specify which direction you want to copy. The user number is 45 | specified after the drive letter, if omitted user 0 is used. 46 | .PP 47 | You can use \fB*\fP and \fB?\fP in CP/M file names, which have the same 48 | meaning in 49 | .IR sh (1) 50 | file name patterns. 51 | .\"}}} 52 | .SH OPTIONS \"{{{ 53 | .IP "\fB\-f\fP \fIformat\fP" 54 | Use the given CP/M disk \fIformat\fP instead of the default format. 55 | .IP "\fB\-T\fP \fIlibdsk-type\fP" 56 | libdsk driver type, e.g. \fBtele\fP for Teledisk images or \fBraw\fP for raw images 57 | (requires building cpmtools with support for libdsk). 58 | .IP \fB\-p\fP 59 | Preserve time stamps when copying files from CP/M to UNIX (not 60 | implemented for copying the other way so far). 61 | .IP \fB\-t\fP 62 | Convert text files between CP/M and UNIX conventions. 63 | .\"}}} 64 | .SH "RETURN VALUE" \"{{{ 65 | Upon successful completion, exit code 0 is returned. 66 | .\"}}} 67 | .SH ERRORS \"{{{ 68 | Any errors are indicated by exit code 1. 69 | .\"}}} 70 | .SH ENVIRONMENT \"{{{ 71 | CPMTOOLSFMT Default format 72 | .\"}}} 73 | .SH FILES \"{{{ 74 | @DATADIR@/diskdefs CP/M disk format definitions 75 | .\"}}} 76 | .SH AUTHORS \"{{{ 77 | This program is copyright 1997\(en2012 Michael Haardt 78 | . The Windows port is copyright 2000, 2001, 2011 John Elliott 79 | . 80 | .PP 81 | This program is free software; you can redistribute it and/or modify 82 | it under the terms of the GNU General Public License as published by 83 | the Free Software Foundation; either version 3 of the License, or 84 | (at your option) any later version. 85 | .PP 86 | This program is distributed in the hope that it will be useful, 87 | but WITHOUT ANY WARRANTY; without even the implied warranty of 88 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 89 | GNU General Public License for more details. 90 | .PP 91 | You should have received a copy of the GNU General Public License along 92 | with this program. If not, write to the Free Software Foundation, Inc., 93 | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 94 | .\"}}} 95 | .SH "SEE ALSO" \"{{{ 96 | .IR cpmls (1), 97 | .IR cpm (5) 98 | .\"}}} 99 | -------------------------------------------------------------------------------- /cpmcp.c: -------------------------------------------------------------------------------- 1 | /* #includes */ /*{{{C}}}*//*{{{*/ 2 | #include "config.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "getopt_.h" 15 | #include "cpmfs.h" 16 | 17 | #ifdef USE_DMALLOC 18 | #include 19 | #endif 20 | /*}}}*/ 21 | 22 | const char cmd[]="cpmcp"; 23 | static int text=0; 24 | static int preserve=0; 25 | 26 | /** 27 | * Return the user number. 28 | * @param s CP/M filename in 0[0]:aaaaaaaa.bbb format. 29 | * @returns The user number or -1 for no match. 30 | */ 31 | static int userNumber(const char *s) /*{{{*/ 32 | { 33 | if (isdigit(*s) && *(s+1)==':') return (*s-'0'); 34 | if (isdigit(*s) && isdigit(*(s+1)) && *(s+2)==':') return (10*(*s-'0')+(*(s+1)-'0')); 35 | return -1; 36 | } 37 | /*}}}*/ 38 | 39 | /** 40 | * Copy one file from CP/M to UNIX. 41 | * @param root The inode for the root directory. 42 | * @param src The CP/M filename in 00aaaaaaaabbb format. 43 | * @param dest The UNIX filename. 44 | * @returns 0 for success, 1 for error. 45 | */ 46 | static int cpmToUnix(const struct cpmInode *root, const char *src, const char *dest) /*{{{*/ 47 | { 48 | struct cpmInode ino; 49 | int exitcode=0; 50 | 51 | if (cpmNamei(root,src,&ino)==-1) { fprintf(stderr,"%s: can not open `%s': %s\n",cmd,src,boo); exitcode=1; } 52 | else 53 | { 54 | struct cpmFile file; 55 | FILE *ufp; 56 | 57 | cpmOpen(&ino,&file,O_RDONLY); 58 | if ((ufp=fopen(dest,text ? "w" : "wb"))==(FILE*)0) { fprintf(stderr,"%s: can not create %s: %s\n",cmd,dest,strerror(errno)); exitcode=1; } 59 | else 60 | { 61 | int crpending=0; 62 | int ohno=0; 63 | int res; 64 | char buf[4096]; 65 | 66 | while ((res=cpmRead(&file,buf,sizeof(buf)))>0) 67 | { 68 | int j; 69 | 70 | for (j=0; j=argc) usage(); 151 | image=argv[optind++]; 152 | 153 | if (userNumber(argv[optind])>=0) /* cpm -> unix? */ /*{{{*/ 154 | { 155 | int i; 156 | struct stat statbuf; 157 | 158 | for (i=optind; i<(argc-1); ++i) if (userNumber(argv[i])==-1) usage(); 159 | todir=((argc-optind)>2); 160 | if (stat(argv[argc-1],&statbuf)==-1) { if (todir) usage(); } 161 | else if (S_ISDIR(statbuf.st_mode)) todir=1; else if (todir) usage(); 162 | readcpm=1; 163 | } 164 | /*}}}*/ 165 | else if (userNumber(argv[argc-1])>=0) /* unix -> cpm */ /*{{{*/ 166 | { 167 | int i; 168 | 169 | todir=0; 170 | for (i=optind; i<(argc-1); ++i) if (userNumber(argv[i])>=0) usage(); 171 | if ((argc-optind)>2 && *(strchr(argv[argc-1],':')+1)!='\0') usage(); 172 | if (*(strchr(argv[argc-1],':')+1)=='\0') todir=1; 173 | readcpm=0; 174 | } 175 | /*}}}*/ 176 | else usage(); 177 | /*}}}*/ 178 | /* open image file */ /*{{{*/ 179 | if ((err=Device_open(&super.dev,image,readcpm ? O_RDONLY : O_RDWR, devopts))) 180 | { 181 | fprintf(stderr,"%s: cannot open %s (%s)\n",cmd,image,err); 182 | exit(1); 183 | } 184 | if (cpmReadSuper(&super,&root,format)==-1) 185 | { 186 | fprintf(stderr,"%s: cannot read superblock (%s)\n",cmd,boo); 187 | exit(1); 188 | } 189 | /*}}}*/ 190 | if (readcpm) /* copy from CP/M to UNIX */ /*{{{*/ 191 | { 192 | int i; 193 | char *last=argv[argc-1]; 194 | 195 | cpmglob(optind,argc-1,argv,&root,&gargc,&gargv); 196 | /* trying to copy multiple files to a file? */ 197 | if (gargc>1 && !todir) usage(); 198 | for (i=0; i=' ' && !((c)&~0x7f) && (c)!='<' && (c)!='>' && (c)!='.' && (c)!=',' && (c)!=';' && (c)!=':' && (c)!='=' && (c)!='?' && (c)!='*' && (c)!= '[' && (c)!=']') 17 | #define EXTENT(low,high) (((low)&0x1f)|(((high)&0x3f)<<5)) 18 | #define EXTENTL(extent) ((extent)&0x1f) 19 | #define EXTENTH(extent) (((extent>>5))&0x3f) 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /cpmfs.h: -------------------------------------------------------------------------------- 1 | #ifndef CPMFS_H 2 | #define CPMFS_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #ifdef _WIN32 9 | #include 10 | #include 11 | /* To make it compile on NT: extracts from Linux 2.0 * 12 | * and */ 13 | #define __S_IFMT 0170000 /* These bits determine file type. */ 14 | #define __S_IFDIR 0040000 /* Directory. */ 15 | #define __S_IFREG 0100000 /* Regular file. */ 16 | #define __S_IWUSR 0000200 /* Writable for user. */ 17 | #define __S_IWGRP 0000200 /* Writable for group. */ 18 | #define __S_IWOTH 0000200 /* Writable for others. */ 19 | 20 | #define __S_ISTYPE(mode, mask) (((mode) & __S_IFMT) == (mask)) 21 | #define __S_ISTYPE(mode, mask) (((mode) & __S_IFMT) == (mask)) 22 | /* These bits are defined in Borland C++ 5 but not in MS Visual C++ */ 23 | #ifndef S_ISDIR 24 | # define S_ISDIR(mode) __S_ISTYPE((mode), __S_IFDIR) 25 | #endif 26 | #ifndef S_ISREG 27 | # define S_ISREG(mode) __S_ISTYPE((mode), __S_IFREG) 28 | #endif 29 | #ifndef S_IWUSR 30 | #define S_IWUSR __S_IWUSR 31 | #endif 32 | #ifndef S_IWGRP 33 | #define S_IWGRP __S_IWGRP 34 | #endif 35 | #ifndef S_IWOTH 36 | #define S_IWOTH __S_IWOTH 37 | #endif 38 | 39 | #include /* For open(), lseek() etc. */ 40 | #ifndef HAVE_MODE_T 41 | typedef int mode_t; 42 | #endif 43 | #endif 44 | 45 | #ifdef __cplusplus 46 | extern "C" { 47 | #endif 48 | 49 | #include "device.h" 50 | 51 | /* CP/M file attributes */ 52 | #define CPM_ATTR_F1 1 53 | #define CPM_ATTR_F2 2 54 | #define CPM_ATTR_F3 4 55 | #define CPM_ATTR_F4 8 56 | /* F5-F8 are banned in CP/M 2 & 3, F7 is used by ZSDOS */ 57 | #define CPM_ATTR_RO 256 /* Read-only */ 58 | #define CPM_ATTR_SYS 512 /* System */ 59 | #define CPM_ATTR_ARCV 1024 /* Archive */ 60 | #define CPM_ATTR_PWDEL 2048 /* Password required to delete */ 61 | #define CPM_ATTR_PWWRITE 4096 /* Password required to write */ 62 | #define CPM_ATTR_PWREAD 8192 /* Password required to read */ 63 | 64 | typedef int cpm_attr_t; 65 | 66 | struct cpmInode 67 | { 68 | ino_t ino; 69 | mode_t mode; 70 | off_t size; 71 | cpm_attr_t attr; 72 | time_t atime; 73 | time_t mtime; 74 | time_t ctime; 75 | struct cpmSuperBlock *sb; 76 | }; 77 | 78 | struct cpmFile 79 | { 80 | mode_t mode; 81 | off_t pos; 82 | struct cpmInode *ino; 83 | }; 84 | 85 | struct cpmDirent 86 | { 87 | ino_t ino; 88 | off_t off; 89 | size_t reclen; 90 | char name[2+8+1+3+1]; /* 00foobarxy.zzy\0 */ 91 | }; 92 | 93 | struct cpmStat 94 | { 95 | ino_t ino; 96 | mode_t mode; 97 | off_t size; 98 | time_t atime; 99 | time_t mtime; 100 | time_t ctime; 101 | }; 102 | 103 | /* Note: CPMFS_HI_USER should be split for systems with user numbers 104 | * up to 31 and CP/M 3, which uses them, but for password entries and 105 | * not for files. 106 | */ 107 | #define CPMFS_HI_USER (0x1<<0) /* has user numbers up to 31 */ 108 | #define CPMFS_CPM3_DATES (0x1<<1) /* has CP/M+ style time stamps */ 109 | #define CPMFS_CPM3_OTHER (0x1<<2) /* has passwords and disc label */ 110 | #define CPMFS_DS_DATES (0x1<<3) /* has datestamper timestamps */ 111 | #define CPMFS_EXACT_SIZE (0x1<<4) /* has reverse exact file size */ 112 | 113 | #define CPMFS_DR22 (CPMFS_HI_USER) 114 | #define CPMFS_P2DOS (CPMFS_CPM3_DATES|CPMFS_HI_USER) 115 | #define CPMFS_DR3 (CPMFS_CPM3_DATES|CPMFS_CPM3_OTHER|CPMFS_HI_USER) 116 | #define CPMFS_ISX (CPMFS_EXACT_SIZE) 117 | #define CPMFS_ZSYS (CPMFS_HI_USER) 118 | 119 | struct dsEntry 120 | { 121 | char year; 122 | char month; 123 | char day; 124 | char hour; 125 | char minute; 126 | }; 127 | 128 | struct dsDate 129 | { 130 | struct dsEntry create; 131 | struct dsEntry access; 132 | struct dsEntry modify; 133 | char checksum; 134 | }; 135 | 136 | struct cpmSuperBlock 137 | { 138 | struct Device dev; 139 | 140 | int secLength; 141 | int tracks; 142 | int sectrk; 143 | int blksiz; 144 | int maxdir; 145 | int skew; 146 | int boottrk; 147 | off_t offset; 148 | int type; 149 | int size; 150 | int extents; /* logical extents per physical extent */ 151 | int *skewtab; 152 | char libdskGeometry[256]; 153 | 154 | struct PhysDirectoryEntry *dir; 155 | int alvSize; 156 | int *alv; 157 | int cnotatime; 158 | char *label; 159 | size_t labelLength; 160 | char *passwd; 161 | size_t passwdLength; 162 | struct cpmInode *root; 163 | int dirtyDirectory; 164 | struct dsDate *ds; 165 | int dirtyDs; 166 | }; 167 | 168 | struct cpmStatFS 169 | { 170 | long f_bsize; 171 | long f_blocks; 172 | long f_bfree; 173 | long f_bused; 174 | long f_bavail; 175 | long f_files; 176 | long f_ffree; 177 | long f_namelen; 178 | }; 179 | 180 | extern const char cmd[]; 181 | extern const char *boo; 182 | 183 | int match(const char *a, const char *pattern); 184 | void cpmglob(int opti, int argc, char * const argv[], struct cpmInode *root, int *gargc, char ***gargv); 185 | 186 | int cpmReadSuper(struct cpmSuperBlock *drive, struct cpmInode *root, const char *format); 187 | int cpmNamei(const struct cpmInode *dir, const char *filename, struct cpmInode *i); 188 | void cpmStatFS(const struct cpmInode *ino, struct cpmStatFS *buf); 189 | int cpmUnlink(const struct cpmInode *dir, const char *fname); 190 | int cpmRename(const struct cpmInode *dir, const char *old, const char *newname); 191 | int cpmOpendir(struct cpmInode *dir, struct cpmFile *dirp); 192 | int cpmReaddir(struct cpmFile *dir, struct cpmDirent *ent); 193 | void cpmStat(const struct cpmInode *ino, struct cpmStat *buf); 194 | int cpmAttrGet(struct cpmInode *ino, cpm_attr_t *attrib); 195 | int cpmAttrSet(struct cpmInode *ino, cpm_attr_t attrib); 196 | int cpmChmod(struct cpmInode *ino, mode_t mode); 197 | int cpmOpen(struct cpmInode *ino, struct cpmFile *file, mode_t mode); 198 | int cpmRead(struct cpmFile *file, char *buf, int count); 199 | int cpmWrite(struct cpmFile *file, const char *buf, int count); 200 | int cpmClose(struct cpmFile *file); 201 | int cpmCreat(struct cpmInode *dir, const char *fname, struct cpmInode *ino, mode_t mode); 202 | void cpmUtime(struct cpmInode *ino, struct utimbuf *times); 203 | int cpmSync(struct cpmSuperBlock *sb); 204 | void cpmUmount(struct cpmSuperBlock *sb); 205 | int cpmCheckDs(struct cpmSuperBlock *sb); 206 | 207 | #ifdef __cplusplus 208 | } 209 | #endif 210 | 211 | #endif 212 | -------------------------------------------------------------------------------- /cpmls.1: -------------------------------------------------------------------------------- 1 | .TH CPMLS 1 "Jan 23, 2019" "CP/M tools" "User commands" 2 | .SH NAME \"{{{roff}}}\"{{{ 3 | cpmls \- list sorted contents of directory 4 | .\"}}} 5 | .SH SYNOPSIS \"{{{ 6 | .ad l 7 | .B cpmls 8 | .RB [ \-f 9 | .IR format ] 10 | .RB [ \-T 11 | .IR libdsk-type ] 12 | .RB [ \-d | \-D | \-F | \-A | \-l [ \-c ][ \-i ]] 13 | .I image 14 | .RI [ file-pattern "...]" 15 | .ad b 16 | .\"}}} 17 | .SH DESCRIPTION \"{{{ 18 | \fBCpmls\fP lists the sorted contents of the directory. 19 | .\"}}} 20 | .SH OPTIONS \"{{{ 21 | .IP "\fB\-f\fP \fIformat\fP" 22 | Use the given CP/M disk \fIformat\fP instead of the default format. 23 | .IP "\fB\-T\fP \fIlibdsk-type\fP" 24 | libdsk driver type, e.g. \fBtele\fP for Teledisk images or \fBraw\fP for raw images 25 | (requires building cpmtools with support for libdsk). 26 | .IP \fB\-d\fP 27 | Old CP/M 2.2 dir output. 28 | .IP \fB\-D\fP 29 | P2DOS 2.3 ddir-like output. 30 | .IP \fB\-F\fp 31 | CP/M 3.x dir output. 32 | .IP \fB\-A\fp 33 | E2fs lsattr-like output. 34 | .IP \fB\-l\fP 35 | Long UNIX-style directory listing including size, time stamp and user number. 36 | .IP \fB\-c\fP 37 | Output the creation time, not the modification time. 38 | .IP \fB\-i\fP 39 | Print index number of each file. 40 | .\"}}} 41 | .SH "RETURN VALUE" \"{{{ 42 | Upon successful completion, exit code 0 is returned. 43 | .\"}}} 44 | .SH ERRORS \"{{{ 45 | Any errors are indicated by exit code 1. 46 | .\"}}} 47 | .SH ENVIRONMENT \"{{{ 48 | CPMTOOLSFMT Default format 49 | .\"}}} 50 | .SH FILES \"{{{ 51 | ${prefix}/share/diskdefs CP/M disk format definitions 52 | .\"}}} 53 | .SH AUTHORS \"{{{ 54 | This program is copyright 1997\(en2012 Michael Haardt 55 | . The Windows port is copyright 2000, 2001, 2011 John Elliott 56 | . 57 | .PP 58 | This program is free software; you can redistribute it and/or modify 59 | it under the terms of the GNU General Public License as published by 60 | the Free Software Foundation; either version 3 of the License, or 61 | (at your option) any later version. 62 | .PP 63 | This program is distributed in the hope that it will be useful, 64 | but WITHOUT ANY WARRANTY; without even the implied warranty of 65 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 66 | GNU General Public License for more details. 67 | .PP 68 | You should have received a copy of the GNU General Public License along 69 | with this program. If not, write to the Free Software Foundation, Inc., 70 | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 71 | .\"}}} 72 | .SH "SEE ALSO" \"{{{ 73 | .IR cpmcp (1), 74 | .IR cpm (5) 75 | .\"}}} 76 | -------------------------------------------------------------------------------- /cpmls.1.in: -------------------------------------------------------------------------------- 1 | .TH CPMLS 1 "@UPDATED@" "CP/M tools" "User commands" 2 | .SH NAME \"{{{roff}}}\"{{{ 3 | cpmls \- list sorted contents of directory 4 | .\"}}} 5 | .SH SYNOPSIS \"{{{ 6 | .ad l 7 | .B cpmls 8 | .RB [ \-f 9 | .IR format ] 10 | .RB [ \-T 11 | .IR libdsk-type ] 12 | .RB [ \-d | \-D | \-F | \-A | \-l [ \-c ][ \-i ]] 13 | .I image 14 | .RI [ file-pattern "...]" 15 | .ad b 16 | .\"}}} 17 | .SH DESCRIPTION \"{{{ 18 | \fBCpmls\fP lists the sorted contents of the directory. 19 | .\"}}} 20 | .SH OPTIONS \"{{{ 21 | .IP "\fB\-f\fP \fIformat\fP" 22 | Use the given CP/M disk \fIformat\fP instead of the default format. 23 | .IP "\fB\-T\fP \fIlibdsk-type\fP" 24 | libdsk driver type, e.g. \fBtele\fP for Teledisk images or \fBraw\fP for raw images 25 | (requires building cpmtools with support for libdsk). 26 | .IP \fB\-d\fP 27 | Old CP/M 2.2 dir output. 28 | .IP \fB\-D\fP 29 | P2DOS 2.3 ddir-like output. 30 | .IP \fB\-F\fp 31 | CP/M 3.x dir output. 32 | .IP \fB\-A\fp 33 | E2fs lsattr-like output. 34 | .IP \fB\-l\fP 35 | Long UNIX-style directory listing including size, time stamp and user number. 36 | .IP \fB\-c\fP 37 | Output the creation time, not the modification time. 38 | .IP \fB\-i\fP 39 | Print index number of each file. 40 | .\"}}} 41 | .SH "RETURN VALUE" \"{{{ 42 | Upon successful completion, exit code 0 is returned. 43 | .\"}}} 44 | .SH ERRORS \"{{{ 45 | Any errors are indicated by exit code 1. 46 | .\"}}} 47 | .SH ENVIRONMENT \"{{{ 48 | CPMTOOLSFMT Default format 49 | .\"}}} 50 | .SH FILES \"{{{ 51 | @DATADIR@/diskdefs CP/M disk format definitions 52 | .\"}}} 53 | .SH AUTHORS \"{{{ 54 | This program is copyright 1997\(en2012 Michael Haardt 55 | . The Windows port is copyright 2000, 2001, 2011 John Elliott 56 | . 57 | .PP 58 | This program is free software; you can redistribute it and/or modify 59 | it under the terms of the GNU General Public License as published by 60 | the Free Software Foundation; either version 3 of the License, or 61 | (at your option) any later version. 62 | .PP 63 | This program is distributed in the hope that it will be useful, 64 | but WITHOUT ANY WARRANTY; without even the implied warranty of 65 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 66 | GNU General Public License for more details. 67 | .PP 68 | You should have received a copy of the GNU General Public License along 69 | with this program. If not, write to the Free Software Foundation, Inc., 70 | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 71 | .\"}}} 72 | .SH "SEE ALSO" \"{{{ 73 | .IR cpmcp (1), 74 | .IR cpm (5) 75 | .\"}}} 76 | -------------------------------------------------------------------------------- /cpmls.c: -------------------------------------------------------------------------------- 1 | /* #includes */ /*{{{C}}}*//*{{{*/ 2 | #include "config.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "getopt_.h" 12 | #include "cpmfs.h" 13 | 14 | #ifdef USE_DMALLOC 15 | #include 16 | #endif 17 | /*}}}*/ 18 | 19 | /* variables */ /*{{{*/ 20 | static const char * const month[12]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" }; 21 | /*}}}*/ 22 | 23 | /* namecmp -- compare two entries */ /*{{{*/ 24 | static int namecmp(const void *a, const void *b) 25 | { 26 | if (**((const char * const *)a)=='[') return -1; 27 | return strcmp(*((const char * const *)a),*((const char * const *)b)); 28 | } 29 | /*}}}*/ 30 | /* olddir -- old style output */ /*{{{*/ 31 | static void olddir(char **dirent, int entries) 32 | { 33 | int i,j,k,l,user,announce; 34 | 35 | announce=0; 36 | for (user=0; user<32; ++user) 37 | { 38 | for (i=l=0; itm_mday,month[tmp->tm_mon],tmp->tm_year+1900,tmp->tm_hour,tmp->tm_min); 120 | } 121 | else if (statbuf.ctime) printf(" "); 122 | if (statbuf.ctime) 123 | { 124 | tmp=localtime(&statbuf.ctime); 125 | printf(" %02d-%s-%04d %02d:%02d",tmp->tm_mday,month[tmp->tm_mon],tmp->tm_year+1900,tmp->tm_hour,tmp->tm_min); 126 | } 127 | putchar('\n'); 128 | ++l; 129 | } 130 | } 131 | if (announce==2) announce=1; 132 | } 133 | printf("%5.1d Files occupying %6.1ldK",l,(buf.f_bused*buf.f_bsize)/1024); 134 | printf(", %7.1ldK Free.\n",(buf.f_bfree*buf.f_bsize)/1024); 135 | } 136 | else printf("No files found\n"); 137 | } 138 | /*}}}*/ 139 | /* old3dir -- old CP/M Plus style long output */ /*{{{*/ 140 | static void old3dir(char **dirent, int entries, struct cpmInode *ino) 141 | { 142 | struct cpmStatFS buf; 143 | struct cpmStat statbuf; 144 | struct cpmInode file; 145 | 146 | if (entries) 147 | { 148 | int i,j,k,l,announce,user, attrib; 149 | int totalBytes=0,totalRecs=0; 150 | 151 | qsort(dirent,entries,sizeof(char*),namecmp); 152 | cpmStatFS(ino,&buf); 153 | announce=1; 154 | for (l=0,user=0; user<32; ++user) 155 | { 156 | for (i=0; isb->cnotatime ? "Create" : "Access"); 171 | printf("------------ ------ ------ ------------ ------ -------------- --------------\n\n"); 172 | } 173 | announce=2; 174 | for (j=2; dirent[i][j] && dirent[i][j]!='.'; ++j) putchar(toupper(dirent[i][j])); 175 | k=j; while (k<10) { putchar(' '); ++k; } 176 | putchar(' '); 177 | if (dirent[i][j]=='.') ++j; 178 | for (k=0; dirent[i][j]; ++j,++k) putchar(toupper(dirent[i][j])); 179 | for (; k<3; ++k) putchar(' '); 180 | 181 | totalBytes+=statbuf.size; 182 | totalRecs+=(statbuf.size+127)/128; 183 | printf(" %5.1ldk",(long) (statbuf.size+buf.f_bsize-1) / 184 | buf.f_bsize*(buf.f_bsize/1024)); 185 | printf(" %6.1ld ",(long)(statbuf.size/128)); 186 | putchar((attrib & CPM_ATTR_F1) ? '1' : ' '); 187 | putchar((attrib & CPM_ATTR_F2) ? '2' : ' '); 188 | putchar((attrib & CPM_ATTR_F3) ? '3' : ' '); 189 | putchar((attrib & CPM_ATTR_F4) ? '4' : ' '); 190 | putchar((statbuf.mode&(S_IWUSR|S_IWGRP|S_IWOTH)) ? ' ' : 'R'); 191 | putchar((attrib & CPM_ATTR_SYS) ? 'S' : ' '); 192 | putchar((attrib & CPM_ATTR_ARCV) ? 'A' : ' '); 193 | printf(" "); 194 | if (attrib & CPM_ATTR_PWREAD) printf("Read "); 195 | else if (attrib & CPM_ATTR_PWWRITE) printf("Write "); 196 | else if (attrib & CPM_ATTR_PWDEL) printf("Delete "); 197 | else printf("None "); 198 | if (statbuf.mtime) 199 | { 200 | tmp=localtime(&statbuf.mtime); 201 | printf("%02d/%02d/%02d %02d:%02d ",tmp->tm_mon+1,tmp->tm_mday,tmp->tm_year%100,tmp->tm_hour,tmp->tm_min); 202 | } 203 | else printf(" "); 204 | if (ino->sb->cnotatime && statbuf.ctime) 205 | { 206 | tmp=localtime(&statbuf.ctime); 207 | printf("%02d/%02d/%02d %02d:%02d",tmp->tm_mon+1,tmp->tm_mday,tmp->tm_year%100,tmp->tm_hour,tmp->tm_min); 208 | } 209 | else if (!ino->sb->cnotatime && statbuf.atime) 210 | { 211 | tmp=localtime(&statbuf.atime); 212 | printf("%02d/%02d/%02d %02d:%02d",tmp->tm_mon+1,tmp->tm_mday,tmp->tm_year%100,tmp->tm_hour,tmp->tm_min); 213 | } 214 | putchar('\n'); 215 | ++l; 216 | } 217 | } 218 | if (announce==2) announce=1; 219 | } 220 | printf("\nTotal Bytes = %6.1dk ",(totalBytes+1023)/1024); 221 | printf("Total Records = %7.1d ",totalRecs); 222 | printf("Files Found = %4.1d\n",l); 223 | printf("Total 1k Blocks = %6.1ld ",(buf.f_bused*buf.f_bsize)/1024); 224 | printf("Used/Max Dir Entries For Drive A: %4.1ld/%4.1ld\n",buf.f_files-buf.f_ffree,buf.f_files); 225 | } 226 | else printf("No files found\n"); 227 | } 228 | /*}}}*/ 229 | /* ls -- UNIX style output */ /*{{{*/ 230 | static void ls(char **dirent, int entries, struct cpmInode *ino, int l, int c, int iflag) 231 | { 232 | int i,user,announce,any; 233 | time_t now; 234 | struct cpmStat statbuf; 235 | struct cpmInode file; 236 | 237 | time(&now); 238 | qsort(dirent,entries,sizeof(char*),namecmp); 239 | announce=0; 240 | any=0; 241 | for (user=0; user<32; ++user) 242 | { 243 | announce=0; 244 | for (i=0; itm_mon],tmp->tm_mday); 283 | if ((c ? statbuf.ctime : statbuf.mtime)<(now-182*24*3600)) printf("%04d ",tmp->tm_year+1900); 284 | else printf("%02d:%02d ",tmp->tm_hour,tmp->tm_min); 285 | } 286 | printf("%s\n",dirent[i]+2); 287 | } 288 | } 289 | } 290 | } 291 | /*}}}*/ 292 | /* lsattr -- output something like e2fs lsattr */ /*{{{*/ 293 | static void lsattr(char **dirent, int entries, struct cpmInode *ino) 294 | { 295 | int i,user,announce,any; 296 | struct cpmStat statbuf; 297 | struct cpmInode file; 298 | cpm_attr_t attrib; 299 | 300 | qsort(dirent,entries,sizeof(char*),namecmp); 301 | announce=0; 302 | any=0; 303 | for (user=0; user<32; ++user) 304 | { 305 | announce=0; 306 | for (i=0; i. The Windows port is copyright 2000, 2001, 2011 John Elliott 40 | . 41 | .PP 42 | This program is free software; you can redistribute it and/or modify 43 | it under the terms of the GNU General Public License as published by 44 | the Free Software Foundation; either version 3 of the License, or 45 | (at your option) any later version. 46 | .PP 47 | This program is distributed in the hope that it will be useful, 48 | but WITHOUT ANY WARRANTY; without even the implied warranty of 49 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 50 | GNU General Public License for more details. 51 | .PP 52 | You should have received a copy of the GNU General Public License along 53 | with this program. If not, write to the Free Software Foundation, Inc., 54 | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 55 | .\"}}} 56 | .SH "SEE ALSO" \"{{{ 57 | .IR cpmls (1), 58 | .IR cpm (5) 59 | .\"}}} 60 | -------------------------------------------------------------------------------- /cpmrm.1.in: -------------------------------------------------------------------------------- 1 | .TH CPMRM 1 "@UPDATED@" "CP/M tools" "User commands" 2 | .SH NAME \"{{{roff}}}\"{{{ 3 | cpmrm \- remove files on CP/M disks 4 | .\"}}} 5 | .SH SYNOPSIS \"{{{ 6 | .ad l 7 | .B cpmrm 8 | .RB [ \-f 9 | .IR format ] 10 | .I image 11 | .I file-pattern 12 | \&... 13 | .ad b 14 | .\"}}} 15 | .SH DESCRIPTION \"{{{ 16 | \fBcpmrm\fP removes files from CP/M disks. 17 | .\"}}} 18 | .SH OPTIONS \"{{{ 19 | .IP "\fB\-f\fP \fIformat\fP" 20 | Use the given CP/M disk \fIformat\fP instead of the default format. 21 | .IP "\fB\-T\fP \fIlibdsk-type\fP" 22 | libdsk driver type, e.g. \fBtele\fP for Teledisk images or \fBraw\fP for raw images 23 | (requires building cpmtools with support for libdsk). 24 | .\"}}} 25 | .SH "RETURN VALUE" \"{{{ 26 | Upon successful completion, exit code 0 is returned. 27 | .\"}}} 28 | .SH ERRORS \"{{{ 29 | Any errors are indicated by exit code 1. 30 | .\"}}} 31 | .SH ENVIRONMENT \"{{{ 32 | CPMTOOLSFMT Default format 33 | .\"}}} 34 | .SH FILES \"{{{ 35 | @DATADIR@/diskdefs CP/M disk format definitions 36 | .\"}}} 37 | .SH AUTHORS \"{{{ 38 | This program is copyright 1997\(en2012 Michael Haardt 39 | . The Windows port is copyright 2000, 2001, 2011 John Elliott 40 | . 41 | .PP 42 | This program is free software; you can redistribute it and/or modify 43 | it under the terms of the GNU General Public License as published by 44 | the Free Software Foundation; either version 3 of the License, or 45 | (at your option) any later version. 46 | .PP 47 | This program is distributed in the hope that it will be useful, 48 | but WITHOUT ANY WARRANTY; without even the implied warranty of 49 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 50 | GNU General Public License for more details. 51 | .PP 52 | You should have received a copy of the GNU General Public License along 53 | with this program. If not, write to the Free Software Foundation, Inc., 54 | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 55 | .\"}}} 56 | .SH "SEE ALSO" \"{{{ 57 | .IR cpmls (1), 58 | .IR cpm (5) 59 | .\"}}} 60 | -------------------------------------------------------------------------------- /cpmrm.c: -------------------------------------------------------------------------------- 1 | /* #includes */ /*{{{C}}}*//*{{{*/ 2 | #include "config.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "getopt_.h" 11 | #include "cpmfs.h" 12 | 13 | #ifdef USE_DMALLOC 14 | #include 15 | #endif 16 | /*}}}*/ 17 | 18 | const char cmd[]="cpmrm"; 19 | 20 | int main(int argc, char *argv[]) /*{{{*/ 21 | { 22 | /* variables */ /*{{{*/ 23 | const char *err; 24 | const char *image; 25 | const char *format; 26 | const char *devopts=NULL; 27 | int c,i,usage=0,exitcode=0; 28 | struct cpmSuperBlock drive; 29 | struct cpmInode root; 30 | int gargc; 31 | char **gargv; 32 | /*}}}*/ 33 | 34 | /* parse options */ /*{{{*/ 35 | if (!(format=getenv("CPMTOOLSFMT"))) format=FORMAT; 36 | while ((c=getopt(argc,argv,"T:f:h?"))!=EOF) switch(c) 37 | { 38 | case 'T': devopts=optarg; break; 39 | case 'f': format=optarg; break; 40 | case 'h': 41 | case '?': usage=1; break; 42 | } 43 | 44 | if (optind>=(argc-1)) usage=1; 45 | else image=argv[optind++]; 46 | 47 | if (usage) 48 | { 49 | fprintf(stderr,"Usage: %s [-f format] [-T dsktype] image pattern ...\n",cmd); 50 | exit(1); 51 | } 52 | /*}}}*/ 53 | /* open image */ /*{{{*/ 54 | if ((err=Device_open(&drive.dev, image, O_RDWR, devopts))) 55 | { 56 | fprintf(stderr,"%s: cannot open %s (%s)\n",cmd,image,err); 57 | exit(1); 58 | } 59 | if (cpmReadSuper(&drive,&root,format)==-1) 60 | { 61 | fprintf(stderr,"%s: cannot read superblock (%s)\n",cmd,boo); 62 | exit(1); 63 | } 64 | /*}}}*/ 65 | cpmglob(optind,argc,argv,&root,&gargc,&gargv); 66 | for (i=0; i 5 | #include 6 | #include 7 | #include 8 | 9 | #include "device.h" 10 | 11 | #ifdef USE_DMALLOC 12 | #include 13 | #endif 14 | /*}}}*/ 15 | 16 | static const char *lookupFormat(DSK_GEOMETRY *geom, const char *name) 17 | { 18 | dsk_format_t fmt = FMT_180K; 19 | const char *fname; 20 | 21 | while (dg_stdformat(NULL, fmt, &fname, NULL) == DSK_ERR_OK) 22 | { 23 | if (!strcmp(name, fname)) 24 | { 25 | dg_stdformat(geom, fmt, &fname, NULL); 26 | return NULL; 27 | } 28 | ++fmt; 29 | } 30 | return "Unrecognised LibDsk geometry specification"; 31 | } 32 | 33 | /* Device_open -- Open an image file */ /*{{{*/ 34 | const char *Device_open(struct Device *this, const char *filename, int mode, const char *deviceOpts) 35 | { 36 | char *format; 37 | char driverName[80]; 38 | const char *boo; 39 | dsk_err_t e; 40 | 41 | /* Assume driver name & format name both fit in 80 characters, rather than 42 | * malloccing the exact size */ 43 | if (deviceOpts == NULL) 44 | { 45 | e = dsk_open(&this->dev, filename, NULL, NULL); 46 | format = NULL; 47 | } 48 | else 49 | { 50 | strncpy(driverName, deviceOpts, 79); 51 | driverName[79] = 0; 52 | format = strchr(driverName, ','); 53 | if (format) 54 | { 55 | *format = 0; 56 | ++format; 57 | } 58 | e = dsk_open(&this->dev, filename, driverName, NULL); 59 | } 60 | this->opened = 0; 61 | if (e) return dsk_strerror(e); 62 | this->opened = 1; 63 | if (format) 64 | { 65 | boo = lookupFormat(&this->geom, format); 66 | if (boo) return boo; 67 | } 68 | else 69 | { 70 | dsk_getgeom(this->dev, &this->geom); 71 | } 72 | return NULL; 73 | } 74 | /*}}}*/ 75 | /* Device_setGeometry -- Set disk geometry */ /*{{{*/ 76 | const char *Device_setGeometry(struct Device *this, int secLength, int sectrk, int tracks, off_t offset, const char *libdskGeometry) 77 | { 78 | char *boo; 79 | 80 | this->secLength=secLength; 81 | this->sectrk=sectrk; 82 | this->tracks=tracks; 83 | /* Must be an even multiple of sector size */ 84 | assert(offset%secLength==0); 85 | this->offset=offset; 86 | /* If a geometry is named in diskdefs, use it */ 87 | if (libdskGeometry && libdskGeometry[0]) 88 | { 89 | return lookupFormat(&this->geom, libdskGeometry); 90 | } 91 | 92 | this->geom.dg_secsize = secLength; 93 | this->geom.dg_sectors = sectrk; 94 | /* Did the autoprobe guess right about the number of sectors & cylinders? */ 95 | if (this->geom.dg_cylinders * this->geom.dg_heads == tracks) return NULL; 96 | /* Otherwise we guess: <= 43 tracks: single-sided. Else double. This 97 | * fails for 80-track single-sided if there are any such beasts */ 98 | if (tracks <= 43) 99 | { 100 | this->geom.dg_cylinders = tracks; 101 | this->geom.dg_heads = 1; 102 | } 103 | else 104 | { 105 | this->geom.dg_cylinders = tracks/2; 106 | this->geom.dg_heads = 2; 107 | } 108 | return NULL; 109 | } 110 | /*}}}*/ 111 | /* Device_close -- Close an image file */ /*{{{*/ 112 | const char *Device_close(struct Device *this) 113 | { 114 | dsk_err_t e; 115 | this->opened=0; 116 | e = dsk_close(&this->dev); 117 | return (e?dsk_strerror(e):(const char*)0); 118 | } 119 | /*}}}*/ 120 | /* Device_readSector -- read a physical sector */ /*{{{*/ 121 | const char *Device_readSector(const struct Device *this, int track, int sector, char *buf) 122 | { 123 | dsk_err_t e; 124 | e = dsk_lread(this->dev, &this->geom, buf, (track * this->sectrk) + sector + this->offset/this->secLength); 125 | return (e?dsk_strerror(e):(const char*)0); 126 | } 127 | /*}}}*/ 128 | /* Device_writeSector -- write physical sector */ /*{{{*/ 129 | const char *Device_writeSector(const struct Device *this, int track, int sector, const char *buf) 130 | { 131 | dsk_err_t e; 132 | e = dsk_lwrite(this->dev, &this->geom, buf, (track * this->sectrk) + sector + this->offset/this->secLength); 133 | return (e?dsk_strerror(e):(const char*)0); 134 | } 135 | /*}}}*/ 136 | -------------------------------------------------------------------------------- /device_posix.c: -------------------------------------------------------------------------------- 1 | /* #includes */ /*{{{C}}}*//*{{{*/ 2 | #include "config.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "device.h" 11 | 12 | #ifdef USE_DMALLOC 13 | #include 14 | #endif 15 | /*}}}*/ 16 | 17 | /* Device_open -- Open an image file */ /*{{{*/ 18 | const char *Device_open(struct Device *this, const char *filename, int mode, const char *deviceOpts) 19 | { 20 | this->fd=open(filename,mode); 21 | this->opened=(this->fd==-1?0:1); 22 | return ((this->fd==-1)?strerror(errno):(const char*)0); 23 | } 24 | /*}}}*/ 25 | /* Device_setGeometry -- Set disk geometry */ /*{{{*/ 26 | const char *Device_setGeometry(struct Device *this, int secLength, int sectrk, int tracks, off_t offset, const char *libdskGeometry) 27 | { 28 | this->secLength=secLength; 29 | this->sectrk=sectrk; 30 | this->tracks=tracks; 31 | this->offset=offset; 32 | return NULL; 33 | } 34 | /*}}}*/ 35 | /* Device_close -- Close an image file */ /*{{{*/ 36 | const char *Device_close(struct Device *this) 37 | { 38 | this->opened=0; 39 | return ((close(this->fd)==-1)?strerror(errno):(const char*)0); 40 | } 41 | /*}}}*/ 42 | /* Device_readSector -- read a physical sector */ /*{{{*/ 43 | const char *Device_readSector(const struct Device *this, int track, int sector, char *buf) 44 | { 45 | int res; 46 | 47 | assert(this); 48 | assert(sector>=0); 49 | assert(sectorsectrk); 50 | assert(track>=0); 51 | assert(tracktracks); 52 | assert(buf); 53 | if (lseek(this->fd,(off_t)(((sector+track*this->sectrk)*this->secLength)+this->offset),SEEK_SET)==-1) 54 | { 55 | return strerror(errno); 56 | } 57 | if ((res=read(this->fd, buf, this->secLength)) != this->secLength) 58 | { 59 | if (res==-1) 60 | { 61 | return strerror(errno); 62 | } 63 | else memset(buf+res,0,this->secLength-res); /* hit end of disk image */ 64 | } 65 | return (const char*)0; 66 | } 67 | /*}}}*/ 68 | /* Device_writeSector -- write physical sector */ /*{{{*/ 69 | const char *Device_writeSector(const struct Device *this, int track, int sector, const char *buf) 70 | { 71 | assert(sector>=0); 72 | assert(sectorsectrk); 73 | assert(track>=0); 74 | assert(tracktracks); 75 | if (lseek(this->fd,(off_t)(((sector+track*this->sectrk)*this->secLength)+this->offset),SEEK_SET)==-1) 76 | { 77 | return strerror(errno); 78 | } 79 | if (write(this->fd, buf, this->secLength) == this->secLength) return (const char*)0; 80 | return strerror(errno); 81 | } 82 | /*}}}*/ 83 | -------------------------------------------------------------------------------- /diskdefs.5: -------------------------------------------------------------------------------- 1 | .\" Believe it or not, reportedly there are nroffs which do not know \(en 2 | .if n .ds en - 3 | .if t .ds en \(en 4 | .TH DISKDEFS 5 "Jan 23, 2019" "CP/M tools" "File formats" 5 | .SH NAME \"{{{roff}}}\"{{{ 6 | diskdefs \- CP/M disk and file system format definitions 7 | .\"}}} 8 | .SH DESCRIPTION \"{{{ 9 | The diskdefs file contains CP/M format descriptions, 10 | because CP/M in general does not store those in the file system and there are 11 | no standards of any kind. 12 | .PP 13 | A diskdefs file consists of one or more entries of the format: 14 | .PP 15 | .nf 16 | .RS 17 | \fBdiskdef\fP \fIname\fP 18 | \fBseclen\fP \fIsize\fP 19 | \fBtracks\fP \fIcount\fP 20 | \fBsectrk\fP \fIcount\fP 21 | \fBblocksize\fP \fIsize\fP 22 | \fBmaxdir\fP \fIcount\fP 23 | \fBboottrk\fP \fInumber\fP 24 | [\fBskew\fP \fInumber\fP] 25 | [\fBskewtab\fP \fIsector\fP[\fB,\fP\fIsector\fP]...] 26 | [\fBos\fP \fB2.2\fP|\fB3\fP|\fBisx\fP|\fBp2dos\fP|\fBzsys\fP] 27 | [\fBoffset\fP \fIsize\fP] 28 | [\fBlogicalextents\fP \fIcount\fP] 29 | [\fBlibdsk:format\fP \fIname\fP] 30 | \fBend\fP 31 | .RE 32 | .fi 33 | .PP 34 | \fBskew\fP and \fBskewtab\fP must only be used exclusively. 35 | .PP 36 | Comments are marked with a leading hash or semicolon and extend to the end of the line. 37 | .\"}}} 38 | .SH "SEE ALSO" \"{{{ 39 | .IR cpm (5) 40 | .\"}}} 41 | -------------------------------------------------------------------------------- /diskdefs.5.in: -------------------------------------------------------------------------------- 1 | .\" Believe it or not, reportedly there are nroffs which do not know \(en 2 | .if n .ds en - 3 | .if t .ds en \(en 4 | .TH DISKDEFS 5 "@UPDATED@" "CP/M tools" "File formats" 5 | .SH NAME \"{{{roff}}}\"{{{ 6 | diskdefs \- CP/M disk and file system format definitions 7 | .\"}}} 8 | .SH DESCRIPTION \"{{{ 9 | The diskdefs file contains CP/M format descriptions, 10 | because CP/M in general does not store those in the file system and there are 11 | no standards of any kind. 12 | .PP 13 | A diskdefs file consists of one or more entries of the format: 14 | .PP 15 | .nf 16 | .RS 17 | \fBdiskdef\fP \fIname\fP 18 | \fBseclen\fP \fIsize\fP 19 | \fBtracks\fP \fIcount\fP 20 | \fBsectrk\fP \fIcount\fP 21 | \fBblocksize\fP \fIsize\fP 22 | \fBmaxdir\fP \fIcount\fP 23 | \fBboottrk\fP \fInumber\fP 24 | [\fBskew\fP \fInumber\fP] 25 | [\fBskewtab\fP \fIsector\fP[\fB,\fP\fIsector\fP]...] 26 | [\fBos\fP \fB2.2\fP|\fB3\fP|\fBisx\fP|\fBp2dos\fP|\fBzsys\fP] 27 | [\fBoffset\fP \fIsize\fP] 28 | [\fBlogicalextents\fP \fIcount\fP] 29 | [\fBlibdsk:format\fP \fIname\fP] 30 | \fBend\fP 31 | .RE 32 | .fi 33 | .PP 34 | \fBskew\fP and \fBskewtab\fP must only be used exclusively. 35 | .PP 36 | Comments are marked with a leading hash or semicolon and extend to the end of the line. 37 | .\"}}} 38 | .SH "SEE ALSO" \"{{{ 39 | .IR cpm (5) 40 | .\"}}} 41 | -------------------------------------------------------------------------------- /fsck.cpm.1: -------------------------------------------------------------------------------- 1 | .TH FSCK.CPM 1 "Jan 23, 2019" "CP/M tools" "User commands" 2 | .SH NAME ..\"{{{roff}}}\"{{{ 3 | fsck.cpm \- check a CP/M file system 4 | .\"}}} 5 | .SH SYNOPSIS .\"{{{ 6 | .ad l 7 | .B fsck.cpm 8 | .RB [ \-f 9 | .IR format ] 10 | .RB [ \-n ] 11 | .I image 12 | .ad b 13 | .\"}}} 14 | .SH DESCRIPTION .\"{{{ 15 | \fBfsck.cpm\fP is used to check and repair a CP/M file system. After 16 | reading the directory, it makes two passes. The first pass checks extent 17 | fields for range and format violations (bad status, extent number, last 18 | record byte count, file name, extension, block number, record count, 19 | size of \&.COM files, time stamp format, invalid password characters, 20 | invalid time stamp mode). The second pass checks extent connectivity 21 | (multiple allocated blocks and duplicate directory entries). 22 | .P 23 | \fBfsck.cpm\fP can not yet repair all errors. 24 | .\"}}} 25 | .SH OPTIONS .\"{{{ 26 | .IP "\fB\-f\fP \fIformat\fP" 27 | Use the given CP/M disk \fIformat\fP instead of the default format. 28 | .IP "\fB\-T\fP \fIlibdsk-type\fP" 29 | libdsk driver type, e.g. \fBtele\fP for Teledisk images or \fBraw\fP for raw images 30 | (requires building cpmtools with support for libdsk). 31 | .IP "\fB\-n\fP" 32 | Open the file system read-only and do not repair any errors. 33 | .\"}}} 34 | .SH "RETURN VALUE" .\"{{{ 35 | Upon successful completion, exit code 0 is returned. 36 | .\"}}} 37 | .SH ERRORS .\"{{{ 38 | Any errors are indicated by exit code 1. 39 | .\"}}} 40 | .SH FILES .\"{{{ 41 | ${prefix}/share/diskdefs CP/M disk format definitions 42 | .\"}}} 43 | .SH ENVIRONMENT \"{{{ 44 | CPMTOOLSFMT Default format 45 | .\"}}} 46 | .SH DIAGNOSTICS .\"{{{ 47 | .IP "\fIimage\fP: \fIused\fP/\fItotal\fP files (\fIn\fP.\fIn\fP% non-contiguos), \fIused\fP/\fItotal\fP blocks" 48 | No inconsistencies could be found. The number of used files actually 49 | is the number of used extents. Since a file may use more than 50 | one extent, this may be greather than the actual number of files, but a 51 | correct measure would not reflect how many files could still be created 52 | at most. A file is considered fragmented, if sequential data blocks 53 | pointed to by the same extent do not have sequential block numbers. 54 | The number of used blocks includes the blocks used for system tracks 55 | and the directory. 56 | .\"}}} 57 | .SH AUTHORS .\"{{{ 58 | This program is copyright 1997\(en2012 Michael Haardt 59 | . The Windows port is copyright 2000, 2001, 2011 John Elliott 60 | . 61 | .PP 62 | This program is free software; you can redistribute it and/or modify 63 | it under the terms of the GNU General Public License as published by 64 | the Free Software Foundation; either version 3 of the License, or 65 | (at your option) any later version. 66 | .PP 67 | This program is distributed in the hope that it will be useful, 68 | but WITHOUT ANY WARRANTY; without even the implied warranty of 69 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 70 | GNU General Public License for more details. 71 | .PP 72 | You should have received a copy of the GNU General Public License along 73 | with this program. If not, write to the Free Software Foundation, Inc., 74 | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 75 | .\"}}} 76 | .SH "SEE ALSO" .\"{{{ 77 | .IR fsck (8), 78 | .IR mkfs.cpm (1), 79 | .IR cpm (5) 80 | .\"}}} 81 | -------------------------------------------------------------------------------- /fsck.cpm.1.in: -------------------------------------------------------------------------------- 1 | .TH FSCK.CPM 1 "@UPDATED@" "CP/M tools" "User commands" 2 | .SH NAME \"{{{roff}}}\"{{{ 3 | fsck.cpm \- check a CP/M file system 4 | .\"}}} 5 | .SH SYNOPSIS \"{{{ 6 | .ad l 7 | .B fsck.cpm 8 | .RB [ \-f 9 | .IR format ] 10 | .RB [ \-n ] 11 | .I image 12 | .ad b 13 | .\"}}} 14 | .SH DESCRIPTION \"{{{ 15 | \fBfsck.cpm\fP is used to check and repair a CP/M file system. After 16 | reading the directory, it makes two passes. The first pass checks extent 17 | fields for range and format violations (bad status, extent number, last 18 | record byte count, file name, extension, block number, record count, 19 | size of \&.COM files, time stamp format, invalid password characters, 20 | invalid time stamp mode). The second pass checks extent connectivity 21 | (multiple allocated blocks and duplicate directory entries). 22 | .P 23 | \fBfsck.cpm\fP can not yet repair all errors. 24 | .\"}}} 25 | .SH OPTIONS \"{{{ 26 | .IP "\fB\-f\fP \fIformat\fP" 27 | Use the given CP/M disk \fIformat\fP instead of the default format. 28 | .IP "\fB\-T\fP \fIlibdsk-type\fP" 29 | libdsk driver type, e.g. \fBtele\fP for Teledisk images or \fBraw\fP for raw images 30 | (requires building cpmtools with support for libdsk). 31 | .IP "\fB\-n\fP" 32 | Open the file system read-only and do not repair any errors. 33 | .\"}}} 34 | .SH "RETURN VALUE" \"{{{ 35 | Upon successful completion, exit code 0 is returned. 36 | .\"}}} 37 | .SH ERRORS \"{{{ 38 | Any errors are indicated by exit code 1. 39 | .\"}}} 40 | .SH FILES \"{{{ 41 | @DATADIR@/diskdefs CP/M disk format definitions 42 | .\"}}} 43 | .SH ENVIRONMENT \"{{{ 44 | CPMTOOLSFMT Default format 45 | .\"}}} 46 | .SH DIAGNOSTICS \"{{{ 47 | .IP "\fIimage\fP: \fIused\fP/\fItotal\fP files (\fIn\fP.\fIn\fP% non-contiguos), \fIused\fP/\fItotal\fP blocks" 48 | No inconsistencies could be found. The number of used files actually 49 | is the number of used extents. Since a file may use more than 50 | one extent, this may be greather than the actual number of files, but a 51 | correct measure would not reflect how many files could still be created 52 | at most. A file is considered fragmented, if sequential data blocks 53 | pointed to by the same extent do not have sequential block numbers. 54 | The number of used blocks includes the blocks used for system tracks 55 | and the directory. 56 | .\"}}} 57 | .SH AUTHORS \"{{{ 58 | This program is copyright 1997\(en2012 Michael Haardt 59 | . The Windows port is copyright 2000, 2001, 2011 John Elliott 60 | . 61 | .PP 62 | This program is free software; you can redistribute it and/or modify 63 | it under the terms of the GNU General Public License as published by 64 | the Free Software Foundation; either version 3 of the License, or 65 | (at your option) any later version. 66 | .PP 67 | This program is distributed in the hope that it will be useful, 68 | but WITHOUT ANY WARRANTY; without even the implied warranty of 69 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 70 | GNU General Public License for more details. 71 | .PP 72 | You should have received a copy of the GNU General Public License along 73 | with this program. If not, write to the Free Software Foundation, Inc., 74 | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 75 | .\"}}} 76 | .SH "SEE ALSO" \"{{{ 77 | .IR fsck (8), 78 | .IR mkfs.cpm (1), 79 | .IR cpm (5) 80 | .\"}}} 81 | -------------------------------------------------------------------------------- /fsck.cpm.c: -------------------------------------------------------------------------------- 1 | /* #includes */ /*{{{C}}}*//*{{{*/ 2 | #include "config.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "getopt_.h" 12 | #include "cpmdir.h" 13 | #include "cpmfs.h" 14 | 15 | #ifdef USE_DMALLOC 16 | #include 17 | #endif 18 | /*}}}*/ 19 | /* #defines */ /*{{{*/ 20 | /* your favourite password *:-) */ 21 | 22 | #define T0 'G' 23 | #define T1 'E' 24 | #define T2 'H' 25 | #define T3 'E' 26 | #define T4 'I' 27 | #define T5 'M' 28 | #define T6 ' ' 29 | #define T7 ' ' 30 | 31 | #define PB ((char)(T0+T1+T2+T3+T4+T5+T6+T7)) 32 | #define P0 ((char)(T7^PB)) 33 | #define P1 ((char)(T6^PB)) 34 | #define P2 ((char)(T5^PB)) 35 | #define P3 ((char)(T4^PB)) 36 | #define P4 ((char)(T3^PB)) 37 | #define P5 ((char)(T2^PB)) 38 | #define P6 ((char)(T1^PB)) 39 | #define P7 ((char)(T0^PB)) 40 | /*}}}*/ 41 | 42 | /* types */ /*{{{*/ 43 | enum Result { OK=0, MODIFIED=1, BROKEN=2 }; 44 | /*}}}*/ 45 | /* variables */ /*{{{*/ 46 | static int norepair=0; 47 | /*}}}*/ 48 | 49 | /* bcdCheck -- check format and range of BCD digit */ /*{{{*/ 50 | static int bcdCheck(int n, int max, const char *msg, const char *unit, int extent1, int extent2) 51 | { 52 | if (((n>>4)&0xf)>10 || (n&0xf)>10 || (((n>>4)&0xf)*10+(n&0xf))>=max) 53 | { 54 | printf("Error: Bad %s %s (extent=%d/%d, %s=%02x)\n",msg,unit,extent1,extent2,unit,n&0xff); 55 | return -1; 56 | } 57 | else return 0; 58 | } 59 | /*}}}*/ 60 | /* pwdCheck -- check password */ /*{{{*/ 61 | static int pwdCheck(int extent, const char *pwd, char decode) 62 | { 63 | char c; 64 | int i; 65 | 66 | for (i=0; i<8; ++i) if ((c=((char)(pwd[7-i]^decode)))<' ' || c&0x80) 67 | { 68 | printf("Error: non-printable character in password (extent=%d, password=",extent); 69 | for (i=0; i<8; ++i) 70 | { 71 | c=pwd[7-i]^decode; 72 | if (c<' ' || c&0x80) 73 | { 74 | putchar('\\'); putchar('0'+((c>>6)&0x01)); 75 | putchar('0'+((c>>3)&0x03)); 76 | putchar('0'+(c&0x03)); 77 | } 78 | else putchar(c); 79 | } 80 | printf(")\n"); 81 | return -1; 82 | } 83 | return 0; 84 | } 85 | /*}}}*/ 86 | /* ask -- ask user and return answer */ /*{{{*/ 87 | static int ask(const char *msg) 88 | { 89 | while (1) 90 | { 91 | char buf[80]; 92 | 93 | if (norepair) return 0; 94 | printf("%s [Y]? ",msg); fflush(stdout); 95 | if (fgets(buf,sizeof(buf),stdin)==(char*)0) exit(1); 96 | switch (toupper(buf[0])) 97 | { 98 | case '\n': 99 | case 'Y': return 1; 100 | case 'N': return 0; 101 | } 102 | } 103 | } 104 | /*}}}*/ 105 | /* prfile -- print file name */ /*{{{*/ 106 | static char *prfile(struct cpmSuperBlock *sb, int extent) 107 | { 108 | struct PhysDirectoryEntry *dir; 109 | static char name[80]; 110 | char *s=name; 111 | int i; 112 | char c; 113 | 114 | dir=sb->dir+extent; 115 | for (i=0; i<8; ++i) 116 | { 117 | c=dir->name[i]; 118 | if ((c&0x7f)<' ') 119 | { 120 | *s++='\\'; *s++=('0'+((c>>6)&0x01)); 121 | *s++=('0'+((c>>3)&0x03)); 122 | *s++=('0'+(c&0x03)); 123 | } 124 | else *s++=(c&0x7f); 125 | } 126 | *s++='.'; 127 | for (i=0; i<3; ++i) 128 | { 129 | c=dir->ext[i]; 130 | if ((c&0x7f)<' ') 131 | { 132 | *s++='\\'; *s++=('0'+((c>>6)&0x01)); 133 | *s++=('0'+((c>>3)&0x03)); 134 | *s++=('0'+(c&0x03)); 135 | } 136 | else *s++=(c&0x7f); 137 | } 138 | *s='\0'; 139 | return name; 140 | } 141 | /*}}}*/ 142 | /* fsck -- file system check */ /*{{{*/ 143 | static int fsck(struct cpmInode *root, const char *image) 144 | { 145 | /* variables */ /*{{{*/ 146 | enum Result ret=OK; 147 | int extent,extent2; 148 | struct PhysDirectoryEntry *dir,*dir2; 149 | struct cpmSuperBlock *sb=root->sb; 150 | /*}}}*/ 151 | 152 | /* Phase 1: check extent fields */ /*{{{*/ 153 | printf("Phase 1: check extent fields\n"); 154 | for (extent=0; extentmaxdir; ++extent) 155 | { 156 | char *status; 157 | int usedBlocks=0; 158 | 159 | dir=sb->dir+extent; 160 | status=&dir->status; 161 | if (*status>=0 && *status<=(sb->type==CPMFS_P2DOS ? 31 : 15)) /* directory entry */ /*{{{*/ 162 | { 163 | /* check name and extension */ /*{{{*/ 164 | { 165 | int i; 166 | char *c; 167 | 168 | for (i=0; i<8; ++i) 169 | { 170 | c=&(dir->name[i]); 171 | if (!ISFILECHAR(i,*c&0x7f) || islower(*c&0x7f)) 172 | { 173 | printf("Error: Bad name (extent=%d, name=\"%s\", position=%d)\n",extent,prfile(sb,extent),i); 174 | if (ask("Remove file")) 175 | { 176 | *status=(char)0xE5; 177 | ret|=MODIFIED; 178 | break; 179 | } 180 | else ret|=BROKEN; 181 | } 182 | } 183 | if (*status==(char)0xe5) continue; 184 | for (i=0; i<3; ++i) 185 | { 186 | c=&(dir->ext[i]); 187 | if (!ISFILECHAR(1,*c&0x7f) || islower(*c&0x7f)) 188 | { 189 | printf("Error: Bad name (extent=%d, name=\"%s\", position=%d)\n",extent,prfile(sb,extent),i); 190 | if (ask("Remove file")) 191 | { 192 | *status=(char)0xE5; 193 | ret|=MODIFIED; 194 | break; 195 | } 196 | else ret|=BROKEN; 197 | } 198 | } 199 | if (*status==(char)0xe5) continue; 200 | } 201 | /*}}}*/ 202 | /* check extent number */ /*{{{*/ 203 | if ((dir->extnol&0xff)>0x1f) 204 | { 205 | printf("Error: Bad lower bits of extent number (extent=%d, name=\"%s\", low bits=%d)\n",extent,prfile(sb,extent),dir->extnol&0xff); 206 | if (ask("Remove file")) 207 | { 208 | *status=(char)0xE5; 209 | ret|=MODIFIED; 210 | } 211 | else ret|=BROKEN; 212 | } 213 | if (*status==(char)0xe5) continue; 214 | if ((dir->extnoh&0xff)>0x3f) 215 | { 216 | printf("Error: Bad higher bits of extent number (extent=%d, name=\"%s\", high bits=%d)\n",extent,prfile(sb,extent),dir->extnoh&0xff); 217 | if (ask("Remove file")) 218 | { 219 | *status=(char)0xE5; 220 | ret|=MODIFIED; 221 | } 222 | else ret|=BROKEN; 223 | } 224 | if (*status==(char)0xe5) continue; 225 | /*}}}*/ 226 | /* check last record byte count */ /*{{{*/ 227 | if ((dir->lrc&0xff)>128) 228 | { 229 | printf("Error: Bad last record byte count (extent=%d, name=\"%s\", lrc=%d)\n",extent,prfile(sb,extent),dir->lrc&0xff); 230 | if (ask("Clear last record byte count")) 231 | { 232 | dir->lrc=(char)0; 233 | ret|=MODIFIED; 234 | } 235 | else ret|=BROKEN; 236 | } 237 | if (*status==(char)0xe5) continue; 238 | /*}}}*/ 239 | /* check block number range */ /*{{{*/ 240 | { 241 | int block,min,max,i; 242 | 243 | min=(sb->maxdir*32+sb->blksiz-1)/sb->blksiz; 244 | max=sb->size; 245 | for (i=0; i<16; ++i) 246 | { 247 | block=dir->pointers[i]&0xff; 248 | if (sb->size>=256) block+=(dir->pointers[++i]&0xff)<<8; 249 | if (block>0) 250 | { 251 | ++usedBlocks; 252 | if (block=max) 253 | { 254 | printf("Error: Bad block number (extent=%d, name=\"%s\", block=%d)\n",extent,prfile(sb,extent),block); 255 | if (ask("Remove file")) 256 | { 257 | *status=(char)0xE5; 258 | ret|=MODIFIED; 259 | break; 260 | } 261 | else ret|=BROKEN; 262 | } 263 | } 264 | } 265 | if (*status==(char)0xe5) continue; 266 | } 267 | /*}}}*/ 268 | /* check number of used blocks ? */ /*{{{*/ 269 | /*}}}*/ 270 | /* check record count */ /*{{{*/ 271 | { 272 | int i,min,max,recordsInBlocks,used=0; 273 | 274 | min=(dir->extnol%sb->extents)*16/sb->extents; 275 | max=((dir->extnol%sb->extents)+1)*16/sb->extents; 276 | assert(minpointers[i] || (sb->size>=256 && dir->pointers[i+1])) ++used; 282 | if (sb->size >= 256) ++i; 283 | } 284 | recordsInBlocks=(((unsigned char)dir->blkcnt)*128+sb->blksiz-1)/sb->blksiz; 285 | if (recordsInBlocks!=used) 286 | { 287 | printf("Error: Bad record count (extent=%d, name=\"%s\", record count=%d)\n",extent,prfile(sb,extent),dir->blkcnt&0xff); 288 | if (ask("Remove file")) 289 | { 290 | *status=(char)0xE5; 291 | ret|=MODIFIED; 292 | } 293 | else ret|=BROKEN; 294 | } 295 | if (*status==(char)0xe5) continue; 296 | } 297 | /*}}}*/ 298 | /* check for too large .com files */ /*{{{*/ 299 | if (((EXTENT(dir->extnol,dir->extnoh)==3 && dir->blkcnt>=126) || EXTENT(dir->extnol,dir->extnoh)>=4) && (dir->ext[0]&0x7f)=='C' && (dir->ext[1]&0x7f)=='O' && (dir->ext[2]&0x7f)=='M') 300 | { 301 | printf("Warning: Oversized .COM file (extent=%d, name=\"%s\")\n",extent,prfile(sb,extent)); 302 | } 303 | /*}}}*/ 304 | } 305 | /*}}}*/ 306 | else if ((sb->type==CPMFS_P2DOS || sb->type==CPMFS_DR3) && *status==33) /* check time stamps ? */ /*{{{*/ 307 | { 308 | unsigned long created,modified; 309 | char s; 310 | 311 | if ((s=sb->dir[extent2=(extent&~3)].status)>=0 && s<=(sb->type==CPMFS_P2DOS ? 31 : 15)) /* time stamps for first of the three extents */ /*{{{*/ 312 | { 313 | bcdCheck(dir->name[2],24,sb->cnotatime ? "creation date" : "access date","hour",extent,extent2); 314 | bcdCheck(dir->name[3],60,sb->cnotatime ? "creation date" : "access date","minute",extent,extent2); 315 | bcdCheck(dir->name[6],24,"modification date","hour",extent,extent2); 316 | bcdCheck(dir->name[7],60,"modification date","minute",extent,extent2); 317 | created=(dir->name[4]+(dir->name[1]<<8))*(0x60*0x60)+dir->name[2]*0x60+dir->name[3]; 318 | modified=(dir->name[0]+(dir->name[5]<<8))*(0x60*0x60)+dir->name[6]*0x60+dir->name[7]; 319 | if (sb->cnotatime && modifieddir[extent2=(extent&~3)+1].status)>=0 && s<=(sb->type==CPMFS_P2DOS ? 31 : 15)) /* time stamps for second */ /*{{{*/ 326 | { 327 | bcdCheck(dir->lrc,24,sb->cnotatime ? "creation date" : "access date","hour",extent,extent2); 328 | bcdCheck(dir->extnoh,60,sb->cnotatime ? "creation date" : "access date","minute",extent,extent2); 329 | bcdCheck(dir->pointers[1],24,"modification date","hour",extent,extent2); 330 | bcdCheck(dir->pointers[2],60,"modification date","minute",extent,extent2); 331 | created=(dir->ext[2]+(dir->extnol<<8))*(0x60*0x60)+dir->lrc*0x60+dir->extnoh; 332 | modified=(dir->blkcnt+(dir->pointers[0]<<8))*(0x60*0x60)+dir->pointers[1]*0x60+dir->pointers[2]; 333 | if (sb->cnotatime && modifieddir[extent2=(extent&~3)+2].status)>=0 && s<=(sb->type==CPMFS_P2DOS ? 31 : 15)) /* time stamps for third */ /*{{{*/ 340 | { 341 | bcdCheck(dir->pointers[7],24,sb->cnotatime ? "creation date" : "access date","hour",extent,extent2); 342 | bcdCheck(dir->pointers[8],60,sb->cnotatime ? "creation date" : "access date","minute",extent,extent2); 343 | bcdCheck(dir->pointers[11],24,"modification date","hour",extent,extent2); 344 | bcdCheck(dir->pointers[12],60,"modification date","minute",extent,extent2); 345 | created=(dir->pointers[5]+(dir->pointers[6]<<8))*(0x60*0x60)+dir->pointers[7]*0x60+dir->pointers[8]; 346 | modified=(dir->pointers[9]+(dir->pointers[10]<<8))*(0x60*0x60)+dir->pointers[11]*0x60+dir->pointers[12]; 347 | if (sb->cnotatime && modifiedtype==CPMFS_DR3 && *status==32) /* disc label */ /*{{{*/ 356 | { 357 | unsigned long created,modified; 358 | 359 | bcdCheck(dir->pointers[10],24,sb->cnotatime ? "creation date" : "access date","hour",extent,extent); 360 | bcdCheck(dir->pointers[11],60,sb->cnotatime ? "creation date" : "access date","minute",extent,extent); 361 | bcdCheck(dir->pointers[14],24,"modification date","hour",extent,extent); 362 | bcdCheck(dir->pointers[15],60,"modification date","minute",extent,extent); 363 | created=(dir->pointers[8]+(dir->pointers[9]<<8))*(0x60*0x60)+dir->pointers[10]*0x60+dir->pointers[11]; 364 | modified=(dir->pointers[12]+(dir->pointers[13]<<8))*(0x60*0x60)+dir->pointers[14]*0x60+dir->pointers[15]; 365 | if (sb->cnotatime && modifiedextnol&0x40 && dir->extnol&0x10) 370 | { 371 | printf("Error: Bit 4 and 6 can only be exclusively be set (extent=%d, label byte=0x%02x)\n",extent,(unsigned char)dir->extnol); 372 | if (ask("Time stamp on creation")) 373 | { 374 | dir->extnol&=~0x40; 375 | ret|=MODIFIED; 376 | } 377 | else if (ask("Time stamp on access")) 378 | { 379 | dir->extnol&=~0x10; 380 | ret|=MODIFIED; 381 | } 382 | else ret|=BROKEN; 383 | } 384 | if (dir->extnol&0x80 && pwdCheck(extent,dir->pointers,dir->lrc)) 385 | { 386 | char msg[80]; 387 | 388 | sprintf(msg,"Set password to %c%c%c%c%c%c%c%c",T0,T1,T2,T3,T4,T5,T6,T7); 389 | if (ask(msg)) 390 | { 391 | dir->pointers[0]=P0; 392 | dir->pointers[1]=P1; 393 | dir->pointers[2]=P2; 394 | dir->pointers[3]=P3; 395 | dir->pointers[4]=P4; 396 | dir->pointers[5]=P5; 397 | dir->pointers[6]=P6; 398 | dir->pointers[7]=P7; 399 | dir->lrc=PB; 400 | ret|=MODIFIED; 401 | } 402 | else ret|=BROKEN; 403 | } 404 | } 405 | /*}}}*/ 406 | else if (sb->type==CPMFS_DR3 && *status>=16 && *status<=31) /* password */ /*{{{*/ 407 | { 408 | /* check name and extension */ /*{{{*/ 409 | { 410 | int i; 411 | char *c; 412 | 413 | for (i=0; i<8; ++i) 414 | { 415 | c=&(dir->name[i]); 416 | if (!ISFILECHAR(i,*c&0x7f) || islower(*c&0x7f)) 417 | { 418 | printf("Error: Bad name (extent=%d, name=\"%s\", position=%d)\n",extent,prfile(sb,extent),i); 419 | if (ask("Clear password entry")) 420 | { 421 | *status=(char)0xE5; 422 | ret|=MODIFIED; 423 | break; 424 | } 425 | else ret|=BROKEN; 426 | } 427 | } 428 | if (*status==(char)0xe5) continue; 429 | for (i=0; i<3; ++i) 430 | { 431 | c=&(dir->ext[i]); 432 | if (!ISFILECHAR(1,*c&0x7f) || islower(*c&0x7f)) 433 | { 434 | printf("Error: Bad name (extent=%d, name=\"%s\", position=%d)\n",extent,prfile(sb,extent),i); 435 | if (ask("Clear password entry")) 436 | { 437 | *status=(char)0xE5; 438 | ret|=MODIFIED; 439 | break; 440 | } 441 | else ret|=BROKEN; 442 | } 443 | } 444 | if (*status==(char)0xe5) continue; 445 | } 446 | /*}}}*/ 447 | /* check password */ /*{{{*/ 448 | if (dir->extnol&(0x80|0x40|0x20) && pwdCheck(extent,dir->pointers,dir->lrc)) 449 | { 450 | char msg[80]; 451 | 452 | sprintf(msg,"Set password to %c%c%c%c%c%c%c%c",T0,T1,T2,T3,T4,T5,T6,T7); 453 | if (ask(msg)) 454 | { 455 | dir->pointers[0]=P0; 456 | dir->pointers[1]=P1; 457 | dir->pointers[2]=P2; 458 | dir->pointers[3]=P3; 459 | dir->pointers[4]=P4; 460 | dir->pointers[5]=P5; 461 | dir->pointers[6]=P6; 462 | dir->pointers[7]=P7; 463 | dir->lrc=PB; 464 | ret|=MODIFIED; 465 | } 466 | else ret|=BROKEN; 467 | } 468 | /*}}}*/ 469 | } 470 | /*}}}*/ 471 | else if (*status!=(char)0xe5) /* bad status */ /*{{{*/ 472 | { 473 | printf("Error: Bad status (extent=%d, name=\"%s\", status=0x%02x)\n",extent,prfile(sb,extent),*status&0xff); 474 | if (ask("Clear entry")) 475 | { 476 | *status=(char)0xE5; 477 | ret|=MODIFIED; 478 | } 479 | else ret|=BROKEN; 480 | continue; 481 | } 482 | /*}}}*/ 483 | } 484 | /*}}}*/ 485 | /* Phase 2: check extent connectivity */ /*{{{*/ 486 | printf("Phase 2: check extent connectivity\n"); 487 | /* check multiple allocated blocks */ /*{{{*/ 488 | for (extent=0; extentmaxdir; ++extent) if ((dir=sb->dir+extent)->status>=0 && dir->status<=(sb->type==CPMFS_P2DOS ? 31 : 15)) 489 | { 490 | int i,j,block,block2; 491 | 492 | for (i=0; i<16; ++i) 493 | { 494 | block=dir->pointers[i]&0xff; 495 | if (sb->size>=256) block+=(dir->pointers[++i]&0xff)<<8; 496 | for (extent2=0; extent2maxdir; ++extent2) if ((dir2=sb->dir+extent2)->status>=0 && dir2->status<=(sb->type==CPMFS_P2DOS ? 31 : 15)) 497 | { 498 | for (j=0; j<16; ++j) 499 | { 500 | block2=dir2->pointers[j]&0xff; 501 | if (sb->size>=256) block2+=(dir2->pointers[++j]&0xff)<<8; 502 | if (block!=0 && block2!=0 && block==block2 && !(extent==extent2 && i==j)) 503 | { 504 | printf("Error: Multiple allocated block (extent=%d,%d, name=\"%s\"",extent,extent2,prfile(sb,extent)); 505 | printf(",\"%s\" block=%d)\n",prfile(sb,extent2),block); 506 | ret|=BROKEN; 507 | } 508 | } 509 | } 510 | } 511 | } 512 | /*}}}*/ 513 | /* check multiple extents */ /*{{{*/ 514 | for (extent=0; extentmaxdir; ++extent) if ((dir=sb->dir+extent)->status>=0 && dir->status<=(sb->type==CPMFS_P2DOS ? 31 : 15)) 515 | { 516 | for (extent2=0; extent2maxdir; ++extent2) if ((dir2=sb->dir+extent2)->status>=0 && dir2->status<=(sb->type==CPMFS_P2DOS ? 31 : 15)) 517 | { 518 | if (extent!=extent2 && EXTENT(dir->extnol,dir->extnoh)==EXTENT(dir2->extnol,dir2->extnoh) && dir->status==dir2->status) 519 | { 520 | int i; 521 | 522 | for (i=0; i<8 && (dir->name[i]&0x7f)==(dir2->name[i]&0x7f); ++i); 523 | if (i==8) 524 | { 525 | for (i=0; i<3 && (dir->ext[i]&0x7f)==(dir2->ext[i]&0x7f); ++i); 526 | if (i==3) 527 | { 528 | printf("Error: Duplicate extent (extent=%d,%d)\n",extent,extent2); 529 | ret|=BROKEN; 530 | } 531 | } 532 | } 533 | } 534 | } 535 | /*}}}*/ 536 | /*}}}*/ 537 | if (ret==0) /* print statistics */ /*{{{*/ 538 | { 539 | struct cpmStatFS statfsbuf; 540 | int fragmented=0,borders=0; 541 | 542 | cpmStatFS(root,&statfsbuf); 543 | for (extent=0; extentmaxdir; ++extent) if ((dir=sb->dir+extent)->status>=0 && dir->status<=(sb->type==CPMFS_P2DOS ? 31 : 15)) 544 | { 545 | int i,block,previous=-1; 546 | 547 | for (i=0; i<16; ++i) 548 | { 549 | block=dir->pointers[i]&0xff; 550 | if (sb->size>=256) block+=(dir->pointers[++i]&0xff)<<8; 551 | if (previous!=-1) 552 | { 553 | if (block!=0 && block!=(previous+1)) ++fragmented; 554 | ++borders; 555 | } 556 | previous=block; 557 | } 558 | } 559 | fragmented=(borders ? (1000*fragmented)/borders : 0); 560 | printf("%s: %ld/%ld files (%d.%d%% non-contigous), %ld/%ld blocks\n",image,statfsbuf.f_files-statfsbuf.f_ffree,statfsbuf.f_files,fragmented/10,fragmented%10,statfsbuf.f_blocks-statfsbuf.f_bfree,statfsbuf.f_blocks); 561 | } 562 | /*}}}*/ 563 | return ret; 564 | } 565 | /*}}}*/ 566 | 567 | const char cmd[]="fsck.cpm"; 568 | 569 | /* main */ /*{{{*/ 570 | int main(int argc, char *argv[]) 571 | { 572 | const char *err; 573 | const char *image; 574 | const char *format; 575 | const char *devopts=NULL; 576 | int c,usage=0; 577 | struct cpmSuperBlock sb; 578 | struct cpmInode root; 579 | enum Result ret; 580 | 581 | if (!(format=getenv("CPMTOOLSFMT"))) format=FORMAT; 582 | while ((c=getopt(argc,argv,"T:f:nh?"))!=EOF) switch(c) 583 | { 584 | case 'f': format=optarg; break; 585 | case 'T': devopts=optarg; break; 586 | case 'n': norepair=1; break; 587 | case 'h': 588 | case '?': usage=1; break; 589 | } 590 | 591 | if (optind!=(argc-1)) usage=1; 592 | else image=argv[optind++]; 593 | 594 | if (usage) 595 | { 596 | fprintf(stderr,"Usage: %s [-f format] [-n] image\n",cmd); 597 | exit(1); 598 | } 599 | if ((err=Device_open(&sb.dev, image, (norepair ? O_RDONLY : O_RDWR), devopts))) 600 | { 601 | if ((err=Device_open(&sb.dev, image,O_RDONLY, devopts))) 602 | { 603 | fprintf(stderr,"%s: cannot open %s: %s\n",cmd,image,err); 604 | exit(1); 605 | } 606 | else 607 | { 608 | fprintf(stderr,"%s: cannot open %s for writing, no repair possible\n",cmd,image); 609 | } 610 | } 611 | if (cpmReadSuper(&sb,&root,format)==-1) 612 | { 613 | fprintf(stderr,"%s: cannot read superblock (%s)\n",cmd,boo); 614 | exit(1); 615 | } 616 | ret=fsck(&root,image); 617 | if (ret&MODIFIED) 618 | { 619 | if (cpmSync(&sb)==-1) 620 | { 621 | fprintf(stderr,"%s: write error on %s: %s\n",cmd,image,strerror(errno)); 622 | ret|=BROKEN; 623 | } 624 | fprintf(stderr,"%s: FILE SYSTEM ON %s MODIFIED",cmd,image); 625 | if (ret&BROKEN) fprintf(stderr,", PLEASE CHECK AGAIN"); 626 | fprintf(stderr,"\n"); 627 | } 628 | cpmUmount(&sb); 629 | if (ret&BROKEN) return 2; 630 | else return 0; 631 | } 632 | /*}}}*/ 633 | -------------------------------------------------------------------------------- /fsed.cpm.1: -------------------------------------------------------------------------------- 1 | .TH FSED.CPM 1 "Jan 23, 2019" "CP/M tools" "User commands" 2 | .SH NAME ..\"{{{roff}}}\"{{{ 3 | fsed.cpm \- edit a CP/M file system 4 | .\"}}} 5 | .SH SYNOPSIS .\"{{{ 6 | .ad l 7 | .B fsed.cpm 8 | .RB [ \-f 9 | .IR format ] 10 | .I image 11 | .ad b 12 | .\"}}} 13 | .SH DESCRIPTION .\"{{{ 14 | \fBfsed.cpm\fP edits a CP/M file system on an image file or device. 15 | It knows about the system, directory and data area, using sector skew on 16 | the last two. Directory entries are decoded. The interactive usage is 17 | self-explanatory. 18 | .\"}}} 19 | .SH OPTIONS .\"{{{ 20 | .IP "\fB\-f\fP \fIformat\fP" 21 | Use the given CP/M disk \fIformat\fP instead of the default format. 22 | .IP "\fB\-T\fP \fIlibdsk-type\fP" 23 | libdsk driver type, e.g. \fBtele\fP for Teledisk images or \fBraw\fP for raw images 24 | (requires building cpmtools with support for libdsk). 25 | .\"}}} 26 | .SH "RETURN VALUE" .\"{{{ 27 | Upon successful completion, exit code 0 is returned. 28 | .\"}}} 29 | .SH ERRORS .\"{{{ 30 | Any errors are indicated by exit code 1. 31 | .\"}}} 32 | .SH ENVIRONMENT \"{{{ 33 | CPMTOOLSFMT Default format 34 | .\"}}} 35 | .SH FILES .\"{{{ 36 | ${prefix}/share/diskdefs CP/M disk format definitions 37 | .\"}}} 38 | .SH AUTHORS \"{{{ 39 | This program is copyright 1997\(en2012 Michael Haardt 40 | . The Windows port is copyright 2000, 2001, 2011 John Elliott 41 | . 42 | .PP 43 | This program is free software; you can redistribute it and/or modify 44 | it under the terms of the GNU General Public License as published by 45 | the Free Software Foundation; either version 3 of the License, or 46 | (at your option) any later version. 47 | .PP 48 | This program is distributed in the hope that it will be useful, 49 | but WITHOUT ANY WARRANTY; without even the implied warranty of 50 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 51 | GNU General Public License for more details. 52 | .PP 53 | You should have received a copy of the GNU General Public License along 54 | with this program. If not, write to the Free Software Foundation, Inc., 55 | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 56 | .\"}}} 57 | .SH "SEE ALSO" .\"{{{ 58 | .IR fsck.cpm (1), 59 | .IR mkfs.cpm (1), 60 | .IR cpmls (1), 61 | .IR cpm (5) 62 | .\"}}} 63 | -------------------------------------------------------------------------------- /fsed.cpm.1.in: -------------------------------------------------------------------------------- 1 | .TH FSED.CPM 1 "@UPDATED@" "CP/M tools" "User commands" 2 | .SH NAME \"{{{roff}}}\"{{{ 3 | fsed.cpm \- edit a CP/M file system 4 | .\"}}} 5 | .SH SYNOPSIS \"{{{ 6 | .ad l 7 | .B fsed.cpm 8 | .RB [ \-f 9 | .IR format ] 10 | .I image 11 | .ad b 12 | .\"}}} 13 | .SH DESCRIPTION \"{{{ 14 | \fBfsed.cpm\fP edits a CP/M file system on an image file or device. 15 | It knows about the system, directory and data area, using sector skew on 16 | the last two. Directory entries are decoded. The interactive usage is 17 | self-explanatory. 18 | .\"}}} 19 | .SH OPTIONS \"{{{ 20 | .IP "\fB\-f\fP \fIformat\fP" 21 | Use the given CP/M disk \fIformat\fP instead of the default format. 22 | .IP "\fB\-T\fP \fIlibdsk-type\fP" 23 | libdsk driver type, e.g. \fBtele\fP for Teledisk images or \fBraw\fP for raw images 24 | (requires building cpmtools with support for libdsk). 25 | .\"}}} 26 | .SH "RETURN VALUE" \"{{{ 27 | Upon successful completion, exit code 0 is returned. 28 | .\"}}} 29 | .SH ERRORS \"{{{ 30 | Any errors are indicated by exit code 1. 31 | .\"}}} 32 | .SH ENVIRONMENT \"{{{ 33 | CPMTOOLSFMT Default format 34 | .\"}}} 35 | .SH FILES \"{{{ 36 | @DATADIR@/diskdefs CP/M disk format definitions 37 | .\"}}} 38 | .SH AUTHORS \"{{{ 39 | This program is copyright 1997\(en2012 Michael Haardt 40 | . The Windows port is copyright 2000, 2001, 2011 John Elliott 41 | . 42 | .PP 43 | This program is free software; you can redistribute it and/or modify 44 | it under the terms of the GNU General Public License as published by 45 | the Free Software Foundation; either version 3 of the License, or 46 | (at your option) any later version. 47 | .PP 48 | This program is distributed in the hope that it will be useful, 49 | but WITHOUT ANY WARRANTY; without even the implied warranty of 50 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 51 | GNU General Public License for more details. 52 | .PP 53 | You should have received a copy of the GNU General Public License along 54 | with this program. If not, write to the Free Software Foundation, Inc., 55 | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 56 | .\"}}} 57 | .SH "SEE ALSO" \"{{{ 58 | .IR fsck.cpm (1), 59 | .IR mkfs.cpm (1), 60 | .IR cpmls (1), 61 | .IR cpm (5) 62 | .\"}}} 63 | -------------------------------------------------------------------------------- /getopt1.c: -------------------------------------------------------------------------------- 1 | /* getopt_long and getopt_long_only entry points for GNU getopt. 2 | Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98,2004,2006 3 | Free Software Foundation, Inc. 4 | This file is part of the GNU C Library. 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2, or (at your option) 9 | any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License along 17 | with this program; if not, write to the Free Software Foundation, 18 | Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ 19 | 20 | #ifdef _LIBC 21 | # include 22 | #else 23 | # include "config.h" 24 | # include "getopt_.h" 25 | #endif 26 | #include "getopt_int.h" 27 | 28 | #include 29 | 30 | /* This needs to come after some library #include 31 | to get __GNU_LIBRARY__ defined. */ 32 | #ifdef __GNU_LIBRARY__ 33 | #include 34 | #endif 35 | 36 | #ifndef NULL 37 | #define NULL 0 38 | #endif 39 | 40 | int 41 | getopt_long (int argc, char *__getopt_argv_const *argv, const char *options, 42 | const struct option *long_options, int *opt_index) 43 | { 44 | return _getopt_internal (argc, (char **) argv, options, long_options, 45 | opt_index, 0, 0); 46 | } 47 | 48 | int 49 | _getopt_long_r (int argc, char **argv, const char *options, 50 | const struct option *long_options, int *opt_index, 51 | struct _getopt_data *d) 52 | { 53 | return _getopt_internal_r (argc, argv, options, long_options, opt_index, 54 | 0, 0, d); 55 | } 56 | 57 | /* Like getopt_long, but '-' as well as '--' can indicate a long option. 58 | If an option that starts with '-' (not '--') doesn't match a long option, 59 | but does match a short option, it is parsed as a short option 60 | instead. */ 61 | 62 | int 63 | getopt_long_only (int argc, char *__getopt_argv_const *argv, 64 | const char *options, 65 | const struct option *long_options, int *opt_index) 66 | { 67 | return _getopt_internal (argc, (char **) argv, options, long_options, 68 | opt_index, 1, 0); 69 | } 70 | 71 | int 72 | _getopt_long_only_r (int argc, char **argv, const char *options, 73 | const struct option *long_options, int *opt_index, 74 | struct _getopt_data *d) 75 | { 76 | return _getopt_internal_r (argc, argv, options, long_options, opt_index, 77 | 1, 0, d); 78 | } 79 | 80 | 81 | #ifdef TEST 82 | 83 | #include 84 | 85 | int 86 | main (int argc, char **argv) 87 | { 88 | int c; 89 | int digit_optind = 0; 90 | 91 | while (1) 92 | { 93 | int this_option_optind = optind ? optind : 1; 94 | int option_index = 0; 95 | static struct option long_options[] = 96 | { 97 | {"add", 1, 0, 0}, 98 | {"append", 0, 0, 0}, 99 | {"delete", 1, 0, 0}, 100 | {"verbose", 0, 0, 0}, 101 | {"create", 0, 0, 0}, 102 | {"file", 1, 0, 0}, 103 | {0, 0, 0, 0} 104 | }; 105 | 106 | c = getopt_long (argc, argv, "abc:d:0123456789", 107 | long_options, &option_index); 108 | if (c == -1) 109 | break; 110 | 111 | switch (c) 112 | { 113 | case 0: 114 | printf ("option %s", long_options[option_index].name); 115 | if (optarg) 116 | printf (" with arg %s", optarg); 117 | printf ("\n"); 118 | break; 119 | 120 | case '0': 121 | case '1': 122 | case '2': 123 | case '3': 124 | case '4': 125 | case '5': 126 | case '6': 127 | case '7': 128 | case '8': 129 | case '9': 130 | if (digit_optind != 0 && digit_optind != this_option_optind) 131 | printf ("digits occur in two different argv-elements.\n"); 132 | digit_optind = this_option_optind; 133 | printf ("option %c\n", c); 134 | break; 135 | 136 | case 'a': 137 | printf ("option a\n"); 138 | break; 139 | 140 | case 'b': 141 | printf ("option b\n"); 142 | break; 143 | 144 | case 'c': 145 | printf ("option c with value `%s'\n", optarg); 146 | break; 147 | 148 | case 'd': 149 | printf ("option d with value `%s'\n", optarg); 150 | break; 151 | 152 | case '?': 153 | break; 154 | 155 | default: 156 | printf ("?? getopt returned character code 0%o ??\n", c); 157 | } 158 | } 159 | 160 | if (optind < argc) 161 | { 162 | printf ("non-option ARGV-elements: "); 163 | while (optind < argc) 164 | printf ("%s ", argv[optind++]); 165 | printf ("\n"); 166 | } 167 | 168 | exit (0); 169 | } 170 | 171 | #endif /* TEST */ 172 | -------------------------------------------------------------------------------- /getopt_.h: -------------------------------------------------------------------------------- 1 | /* Declarations for getopt. 2 | Copyright (C) 1989-1994,1996-1999,2001,2003,2004,2005,2006,2007 3 | Free Software Foundation, Inc. 4 | This file is part of the GNU C Library. 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2, or (at your option) 9 | any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License along 17 | with this program; if not, write to the Free Software Foundation, 18 | Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ 19 | 20 | #ifndef _GETOPT_H 21 | 22 | #ifndef __need_getopt 23 | # define _GETOPT_H 1 24 | #endif 25 | 26 | /* Standalone applications should #define __GETOPT_PREFIX to an 27 | identifier that prefixes the external functions and variables 28 | defined in this header. When this happens, include the 29 | headers that might declare getopt so that they will not cause 30 | confusion if included after this file. Then systematically rename 31 | identifiers so that they do not collide with the system functions 32 | and variables. Renaming avoids problems with some compilers and 33 | linkers. */ 34 | #if defined __GETOPT_PREFIX && !defined __need_getopt 35 | # include 36 | # include 37 | # include 38 | # undef __need_getopt 39 | # undef getopt 40 | # undef getopt_long 41 | # undef getopt_long_only 42 | # undef optarg 43 | # undef opterr 44 | # undef optind 45 | # undef optopt 46 | # define __GETOPT_CONCAT(x, y) x ## y 47 | # define __GETOPT_XCONCAT(x, y) __GETOPT_CONCAT (x, y) 48 | # define __GETOPT_ID(y) __GETOPT_XCONCAT (__GETOPT_PREFIX, y) 49 | # define getopt __GETOPT_ID (getopt) 50 | # define getopt_long __GETOPT_ID (getopt_long) 51 | # define getopt_long_only __GETOPT_ID (getopt_long_only) 52 | # define optarg __GETOPT_ID (optarg) 53 | # define opterr __GETOPT_ID (opterr) 54 | # define optind __GETOPT_ID (optind) 55 | # define optopt __GETOPT_ID (optopt) 56 | #endif 57 | 58 | /* Standalone applications get correct prototypes for getopt_long and 59 | getopt_long_only; they declare "char **argv". libc uses prototypes 60 | with "char *const *argv" that are incorrect because getopt_long and 61 | getopt_long_only can permute argv; this is required for backward 62 | compatibility (e.g., for LSB 2.0.1). 63 | 64 | This used to be `#if defined __GETOPT_PREFIX && !defined __need_getopt', 65 | but it caused redefinition warnings if both unistd.h and getopt.h were 66 | included, since unistd.h includes getopt.h having previously defined 67 | __need_getopt. 68 | 69 | The only place where __getopt_argv_const is used is in definitions 70 | of getopt_long and getopt_long_only below, but these are visible 71 | only if __need_getopt is not defined, so it is quite safe to rewrite 72 | the conditional as follows: 73 | */ 74 | #if !defined __need_getopt 75 | # if defined __GETOPT_PREFIX 76 | # define __getopt_argv_const /* empty */ 77 | # else 78 | # define __getopt_argv_const const 79 | # endif 80 | #endif 81 | 82 | /* If __GNU_LIBRARY__ is not already defined, either we are being used 83 | standalone, or this is the first header included in the source file. 84 | If we are being used with glibc, we need to include , but 85 | that does not exist if we are standalone. So: if __GNU_LIBRARY__ is 86 | not defined, include , which will pull in for us 87 | if it's from glibc. (Why ctype.h? It's guaranteed to exist and it 88 | doesn't flood the namespace with stuff the way some other headers do.) */ 89 | #if !defined __GNU_LIBRARY__ 90 | # include 91 | #endif 92 | 93 | #ifndef __THROW 94 | # ifndef __GNUC_PREREQ 95 | # define __GNUC_PREREQ(maj, min) (0) 96 | # endif 97 | # if defined __cplusplus && __GNUC_PREREQ (2,8) 98 | # define __THROW throw () 99 | # else 100 | # define __THROW 101 | # endif 102 | #endif 103 | 104 | #ifdef __cplusplus 105 | extern "C" { 106 | #endif 107 | 108 | /* For communication from `getopt' to the caller. 109 | When `getopt' finds an option that takes an argument, 110 | the argument value is returned here. 111 | Also, when `ordering' is RETURN_IN_ORDER, 112 | each non-option ARGV-element is returned here. */ 113 | 114 | extern char *optarg; 115 | 116 | /* Index in ARGV of the next element to be scanned. 117 | This is used for communication to and from the caller 118 | and for communication between successive calls to `getopt'. 119 | 120 | On entry to `getopt', zero means this is the first call; initialize. 121 | 122 | When `getopt' returns -1, this is the index of the first of the 123 | non-option elements that the caller should itself scan. 124 | 125 | Otherwise, `optind' communicates from one call to the next 126 | how much of ARGV has been scanned so far. */ 127 | 128 | extern int optind; 129 | 130 | /* Callers store zero here to inhibit the error message `getopt' prints 131 | for unrecognized options. */ 132 | 133 | extern int opterr; 134 | 135 | /* Set to an option character which was unrecognized. */ 136 | 137 | extern int optopt; 138 | 139 | #ifndef __need_getopt 140 | /* Describe the long-named options requested by the application. 141 | The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector 142 | of `struct option' terminated by an element containing a name which is 143 | zero. 144 | 145 | The field `has_arg' is: 146 | no_argument (or 0) if the option does not take an argument, 147 | required_argument (or 1) if the option requires an argument, 148 | optional_argument (or 2) if the option takes an optional argument. 149 | 150 | If the field `flag' is not NULL, it points to a variable that is set 151 | to the value given in the field `val' when the option is found, but 152 | left unchanged if the option is not found. 153 | 154 | To have a long-named option do something other than set an `int' to 155 | a compiled-in constant, such as set a value from `optarg', set the 156 | option's `flag' field to zero and its `val' field to a nonzero 157 | value (the equivalent single-letter option character, if there is 158 | one). For long options that have a zero `flag' field, `getopt' 159 | returns the contents of the `val' field. */ 160 | 161 | struct option 162 | { 163 | const char *name; 164 | /* has_arg can't be an enum because some compilers complain about 165 | type mismatches in all the code that assumes it is an int. */ 166 | int has_arg; 167 | int *flag; 168 | int val; 169 | }; 170 | 171 | /* Names for the values of the `has_arg' field of `struct option'. */ 172 | 173 | # define no_argument 0 174 | # define required_argument 1 175 | # define optional_argument 2 176 | #endif /* need getopt */ 177 | 178 | 179 | /* Get definitions and prototypes for functions to process the 180 | arguments in ARGV (ARGC of them, minus the program name) for 181 | options given in OPTS. 182 | 183 | Return the option character from OPTS just read. Return -1 when 184 | there are no more options. For unrecognized options, or options 185 | missing arguments, `optopt' is set to the option letter, and '?' is 186 | returned. 187 | 188 | The OPTS string is a list of characters which are recognized option 189 | letters, optionally followed by colons, specifying that that letter 190 | takes an argument, to be placed in `optarg'. 191 | 192 | If a letter in OPTS is followed by two colons, its argument is 193 | optional. This behavior is specific to the GNU `getopt'. 194 | 195 | The argument `--' causes premature termination of argument 196 | scanning, explicitly telling `getopt' that there are no more 197 | options. 198 | 199 | If OPTS begins with `-', then non-option arguments are treated as 200 | arguments to the option '\1'. This behavior is specific to the GNU 201 | `getopt'. If OPTS begins with `+', or POSIXLY_CORRECT is set in 202 | the environment, then do not permute arguments. */ 203 | 204 | extern int getopt (int ___argc, char *const *___argv, const char *__shortopts) 205 | __THROW; 206 | 207 | #ifndef __need_getopt 208 | extern int getopt_long (int ___argc, char *__getopt_argv_const *___argv, 209 | const char *__shortopts, 210 | const struct option *__longopts, int *__longind) 211 | __THROW; 212 | extern int getopt_long_only (int ___argc, char *__getopt_argv_const *___argv, 213 | const char *__shortopts, 214 | const struct option *__longopts, int *__longind) 215 | __THROW; 216 | 217 | #endif 218 | 219 | #ifdef __cplusplus 220 | } 221 | #endif 222 | 223 | /* Make sure we later can get all the definitions and declarations. */ 224 | #undef __need_getopt 225 | 226 | #endif /* getopt.h */ 227 | -------------------------------------------------------------------------------- /getopt_int.h: -------------------------------------------------------------------------------- 1 | /* Internal declarations for getopt. 2 | Copyright (C) 1989-1994,1996-1999,2001,2003,2004 3 | Free Software Foundation, Inc. 4 | This file is part of the GNU C Library. 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2, or (at your option) 9 | any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License along 17 | with this program; if not, write to the Free Software Foundation, 18 | Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ 19 | 20 | #ifndef _GETOPT_INT_H 21 | #define _GETOPT_INT_H 1 22 | 23 | extern int _getopt_internal (int ___argc, char **___argv, 24 | const char *__shortopts, 25 | const struct option *__longopts, int *__longind, 26 | int __long_only, int __posixly_correct); 27 | 28 | 29 | /* Reentrant versions which can handle parsing multiple argument 30 | vectors at the same time. */ 31 | 32 | /* Data type for reentrant functions. */ 33 | struct _getopt_data 34 | { 35 | /* These have exactly the same meaning as the corresponding global 36 | variables, except that they are used for the reentrant 37 | versions of getopt. */ 38 | int optind; 39 | int opterr; 40 | int optopt; 41 | char *optarg; 42 | 43 | /* Internal members. */ 44 | 45 | /* True if the internal members have been initialized. */ 46 | int __initialized; 47 | 48 | /* The next char to be scanned in the option-element 49 | in which the last option character we returned was found. 50 | This allows us to pick up the scan where we left off. 51 | 52 | If this is zero, or a null string, it means resume the scan 53 | by advancing to the next ARGV-element. */ 54 | char *__nextchar; 55 | 56 | /* Describe how to deal with options that follow non-option ARGV-elements. 57 | 58 | If the caller did not specify anything, 59 | the default is REQUIRE_ORDER if the environment variable 60 | POSIXLY_CORRECT is defined, PERMUTE otherwise. 61 | 62 | REQUIRE_ORDER means don't recognize them as options; 63 | stop option processing when the first non-option is seen. 64 | This is what Unix does. 65 | This mode of operation is selected by either setting the environment 66 | variable POSIXLY_CORRECT, or using `+' as the first character 67 | of the list of option characters, or by calling getopt. 68 | 69 | PERMUTE is the default. We permute the contents of ARGV as we 70 | scan, so that eventually all the non-options are at the end. 71 | This allows options to be given in any order, even with programs 72 | that were not written to expect this. 73 | 74 | RETURN_IN_ORDER is an option available to programs that were 75 | written to expect options and other ARGV-elements in any order 76 | and that care about the ordering of the two. We describe each 77 | non-option ARGV-element as if it were the argument of an option 78 | with character code 1. Using `-' as the first character of the 79 | list of option characters selects this mode of operation. 80 | 81 | The special argument `--' forces an end of option-scanning regardless 82 | of the value of `ordering'. In the case of RETURN_IN_ORDER, only 83 | `--' can cause `getopt' to return -1 with `optind' != ARGC. */ 84 | 85 | enum 86 | { 87 | REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER 88 | } __ordering; 89 | 90 | /* If the POSIXLY_CORRECT environment variable is set 91 | or getopt was called. */ 92 | int __posixly_correct; 93 | 94 | 95 | /* Handle permutation of arguments. */ 96 | 97 | /* Describe the part of ARGV that contains non-options that have 98 | been skipped. `first_nonopt' is the index in ARGV of the first 99 | of them; `last_nonopt' is the index after the last of them. */ 100 | 101 | int __first_nonopt; 102 | int __last_nonopt; 103 | 104 | #if defined _LIBC && defined USE_NONOPTION_FLAGS 105 | int __nonoption_flags_max_len; 106 | int __nonoption_flags_len; 107 | # endif 108 | }; 109 | 110 | /* The initializer is necessary to set OPTIND and OPTERR to their 111 | default values and to clear the initialization flag. */ 112 | #define _GETOPT_DATA_INITIALIZER { 1, 1 } 113 | 114 | extern int _getopt_internal_r (int ___argc, char **___argv, 115 | const char *__shortopts, 116 | const struct option *__longopts, int *__longind, 117 | int __long_only, int __posixly_correct, 118 | struct _getopt_data *__data); 119 | 120 | extern int _getopt_long_r (int ___argc, char **___argv, 121 | const char *__shortopts, 122 | const struct option *__longopts, int *__longind, 123 | struct _getopt_data *__data); 124 | 125 | extern int _getopt_long_only_r (int ___argc, char **___argv, 126 | const char *__shortopts, 127 | const struct option *__longopts, 128 | int *__longind, 129 | struct _getopt_data *__data); 130 | 131 | #endif /* getopt_int.h */ 132 | -------------------------------------------------------------------------------- /install-sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # install - install a program, script, or datafile 3 | 4 | scriptversion=2011-11-20.07; # UTC 5 | 6 | # This originates from X11R5 (mit/util/scripts/install.sh), which was 7 | # later released in X11R6 (xc/config/util/install.sh) with the 8 | # following copyright and license. 9 | # 10 | # Copyright (C) 1994 X Consortium 11 | # 12 | # Permission is hereby granted, free of charge, to any person obtaining a copy 13 | # of this software and associated documentation files (the "Software"), to 14 | # deal in the Software without restriction, including without limitation the 15 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 16 | # sell copies of the Software, and to permit persons to whom the Software is 17 | # furnished to do so, subject to the following conditions: 18 | # 19 | # The above copyright notice and this permission notice shall be included in 20 | # all copies or substantial portions of the Software. 21 | # 22 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 26 | # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- 27 | # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 | # 29 | # Except as contained in this notice, the name of the X Consortium shall not 30 | # be used in advertising or otherwise to promote the sale, use or other deal- 31 | # ings in this Software without prior written authorization from the X Consor- 32 | # tium. 33 | # 34 | # 35 | # FSF changes to this file are in the public domain. 36 | # 37 | # Calling this script install-sh is preferred over install.sh, to prevent 38 | # 'make' implicit rules from creating a file called install from it 39 | # when there is no Makefile. 40 | # 41 | # This script is compatible with the BSD install script, but was written 42 | # from scratch. 43 | 44 | nl=' 45 | ' 46 | IFS=" "" $nl" 47 | 48 | # set DOITPROG to echo to test this script 49 | 50 | # Don't use :- since 4.3BSD and earlier shells don't like it. 51 | doit=${DOITPROG-} 52 | if test -z "$doit"; then 53 | doit_exec=exec 54 | else 55 | doit_exec=$doit 56 | fi 57 | 58 | # Put in absolute file names if you don't have them in your path; 59 | # or use environment vars. 60 | 61 | chgrpprog=${CHGRPPROG-chgrp} 62 | chmodprog=${CHMODPROG-chmod} 63 | chownprog=${CHOWNPROG-chown} 64 | cmpprog=${CMPPROG-cmp} 65 | cpprog=${CPPROG-cp} 66 | mkdirprog=${MKDIRPROG-mkdir} 67 | mvprog=${MVPROG-mv} 68 | rmprog=${RMPROG-rm} 69 | stripprog=${STRIPPROG-strip} 70 | 71 | posix_glob='?' 72 | initialize_posix_glob=' 73 | test "$posix_glob" != "?" || { 74 | if (set -f) 2>/dev/null; then 75 | posix_glob= 76 | else 77 | posix_glob=: 78 | fi 79 | } 80 | ' 81 | 82 | posix_mkdir= 83 | 84 | # Desired mode of installed file. 85 | mode=0755 86 | 87 | chgrpcmd= 88 | chmodcmd=$chmodprog 89 | chowncmd= 90 | mvcmd=$mvprog 91 | rmcmd="$rmprog -f" 92 | stripcmd= 93 | 94 | src= 95 | dst= 96 | dir_arg= 97 | dst_arg= 98 | 99 | copy_on_change=false 100 | no_target_directory= 101 | 102 | usage="\ 103 | Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE 104 | or: $0 [OPTION]... SRCFILES... DIRECTORY 105 | or: $0 [OPTION]... -t DIRECTORY SRCFILES... 106 | or: $0 [OPTION]... -d DIRECTORIES... 107 | 108 | In the 1st form, copy SRCFILE to DSTFILE. 109 | In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. 110 | In the 4th, create DIRECTORIES. 111 | 112 | Options: 113 | --help display this help and exit. 114 | --version display version info and exit. 115 | 116 | -c (ignored) 117 | -C install only if different (preserve the last data modification time) 118 | -d create directories instead of installing files. 119 | -g GROUP $chgrpprog installed files to GROUP. 120 | -m MODE $chmodprog installed files to MODE. 121 | -o USER $chownprog installed files to USER. 122 | -s $stripprog installed files. 123 | -t DIRECTORY install into DIRECTORY. 124 | -T report an error if DSTFILE is a directory. 125 | 126 | Environment variables override the default commands: 127 | CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG 128 | RMPROG STRIPPROG 129 | " 130 | 131 | while test $# -ne 0; do 132 | case $1 in 133 | -c) ;; 134 | 135 | -C) copy_on_change=true;; 136 | 137 | -d) dir_arg=true;; 138 | 139 | -g) chgrpcmd="$chgrpprog $2" 140 | shift;; 141 | 142 | --help) echo "$usage"; exit $?;; 143 | 144 | -m) mode=$2 145 | case $mode in 146 | *' '* | *' '* | *' 147 | '* | *'*'* | *'?'* | *'['*) 148 | echo "$0: invalid mode: $mode" >&2 149 | exit 1;; 150 | esac 151 | shift;; 152 | 153 | -o) chowncmd="$chownprog $2" 154 | shift;; 155 | 156 | -s) stripcmd=$stripprog;; 157 | 158 | -t) dst_arg=$2 159 | # Protect names problematic for 'test' and other utilities. 160 | case $dst_arg in 161 | -* | [=\(\)!]) dst_arg=./$dst_arg;; 162 | esac 163 | shift;; 164 | 165 | -T) no_target_directory=true;; 166 | 167 | --version) echo "$0 $scriptversion"; exit $?;; 168 | 169 | --) shift 170 | break;; 171 | 172 | -*) echo "$0: invalid option: $1" >&2 173 | exit 1;; 174 | 175 | *) break;; 176 | esac 177 | shift 178 | done 179 | 180 | if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then 181 | # When -d is used, all remaining arguments are directories to create. 182 | # When -t is used, the destination is already specified. 183 | # Otherwise, the last argument is the destination. Remove it from $@. 184 | for arg 185 | do 186 | if test -n "$dst_arg"; then 187 | # $@ is not empty: it contains at least $arg. 188 | set fnord "$@" "$dst_arg" 189 | shift # fnord 190 | fi 191 | shift # arg 192 | dst_arg=$arg 193 | # Protect names problematic for 'test' and other utilities. 194 | case $dst_arg in 195 | -* | [=\(\)!]) dst_arg=./$dst_arg;; 196 | esac 197 | done 198 | fi 199 | 200 | if test $# -eq 0; then 201 | if test -z "$dir_arg"; then 202 | echo "$0: no input file specified." >&2 203 | exit 1 204 | fi 205 | # It's OK to call 'install-sh -d' without argument. 206 | # This can happen when creating conditional directories. 207 | exit 0 208 | fi 209 | 210 | if test -z "$dir_arg"; then 211 | do_exit='(exit $ret); exit $ret' 212 | trap "ret=129; $do_exit" 1 213 | trap "ret=130; $do_exit" 2 214 | trap "ret=141; $do_exit" 13 215 | trap "ret=143; $do_exit" 15 216 | 217 | # Set umask so as not to create temps with too-generous modes. 218 | # However, 'strip' requires both read and write access to temps. 219 | case $mode in 220 | # Optimize common cases. 221 | *644) cp_umask=133;; 222 | *755) cp_umask=22;; 223 | 224 | *[0-7]) 225 | if test -z "$stripcmd"; then 226 | u_plus_rw= 227 | else 228 | u_plus_rw='% 200' 229 | fi 230 | cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; 231 | *) 232 | if test -z "$stripcmd"; then 233 | u_plus_rw= 234 | else 235 | u_plus_rw=,u+rw 236 | fi 237 | cp_umask=$mode$u_plus_rw;; 238 | esac 239 | fi 240 | 241 | for src 242 | do 243 | # Protect names problematic for 'test' and other utilities. 244 | case $src in 245 | -* | [=\(\)!]) src=./$src;; 246 | esac 247 | 248 | if test -n "$dir_arg"; then 249 | dst=$src 250 | dstdir=$dst 251 | test -d "$dstdir" 252 | dstdir_status=$? 253 | else 254 | 255 | # Waiting for this to be detected by the "$cpprog $src $dsttmp" command 256 | # might cause directories to be created, which would be especially bad 257 | # if $src (and thus $dsttmp) contains '*'. 258 | if test ! -f "$src" && test ! -d "$src"; then 259 | echo "$0: $src does not exist." >&2 260 | exit 1 261 | fi 262 | 263 | if test -z "$dst_arg"; then 264 | echo "$0: no destination specified." >&2 265 | exit 1 266 | fi 267 | dst=$dst_arg 268 | 269 | # If destination is a directory, append the input filename; won't work 270 | # if double slashes aren't ignored. 271 | if test -d "$dst"; then 272 | if test -n "$no_target_directory"; then 273 | echo "$0: $dst_arg: Is a directory" >&2 274 | exit 1 275 | fi 276 | dstdir=$dst 277 | dst=$dstdir/`basename "$src"` 278 | dstdir_status=0 279 | else 280 | # Prefer dirname, but fall back on a substitute if dirname fails. 281 | dstdir=` 282 | (dirname "$dst") 2>/dev/null || 283 | expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ 284 | X"$dst" : 'X\(//\)[^/]' \| \ 285 | X"$dst" : 'X\(//\)$' \| \ 286 | X"$dst" : 'X\(/\)' \| . 2>/dev/null || 287 | echo X"$dst" | 288 | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ 289 | s//\1/ 290 | q 291 | } 292 | /^X\(\/\/\)[^/].*/{ 293 | s//\1/ 294 | q 295 | } 296 | /^X\(\/\/\)$/{ 297 | s//\1/ 298 | q 299 | } 300 | /^X\(\/\).*/{ 301 | s//\1/ 302 | q 303 | } 304 | s/.*/./; q' 305 | ` 306 | 307 | test -d "$dstdir" 308 | dstdir_status=$? 309 | fi 310 | fi 311 | 312 | obsolete_mkdir_used=false 313 | 314 | if test $dstdir_status != 0; then 315 | case $posix_mkdir in 316 | '') 317 | # Create intermediate dirs using mode 755 as modified by the umask. 318 | # This is like FreeBSD 'install' as of 1997-10-28. 319 | umask=`umask` 320 | case $stripcmd.$umask in 321 | # Optimize common cases. 322 | *[2367][2367]) mkdir_umask=$umask;; 323 | .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; 324 | 325 | *[0-7]) 326 | mkdir_umask=`expr $umask + 22 \ 327 | - $umask % 100 % 40 + $umask % 20 \ 328 | - $umask % 10 % 4 + $umask % 2 329 | `;; 330 | *) mkdir_umask=$umask,go-w;; 331 | esac 332 | 333 | # With -d, create the new directory with the user-specified mode. 334 | # Otherwise, rely on $mkdir_umask. 335 | if test -n "$dir_arg"; then 336 | mkdir_mode=-m$mode 337 | else 338 | mkdir_mode= 339 | fi 340 | 341 | posix_mkdir=false 342 | case $umask in 343 | *[123567][0-7][0-7]) 344 | # POSIX mkdir -p sets u+wx bits regardless of umask, which 345 | # is incompatible with FreeBSD 'install' when (umask & 300) != 0. 346 | ;; 347 | *) 348 | tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ 349 | trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 350 | 351 | if (umask $mkdir_umask && 352 | exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 353 | then 354 | if test -z "$dir_arg" || { 355 | # Check for POSIX incompatibilities with -m. 356 | # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or 357 | # other-writable bit of parent directory when it shouldn't. 358 | # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. 359 | ls_ld_tmpdir=`ls -ld "$tmpdir"` 360 | case $ls_ld_tmpdir in 361 | d????-?r-*) different_mode=700;; 362 | d????-?--*) different_mode=755;; 363 | *) false;; 364 | esac && 365 | $mkdirprog -m$different_mode -p -- "$tmpdir" && { 366 | ls_ld_tmpdir_1=`ls -ld "$tmpdir"` 367 | test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" 368 | } 369 | } 370 | then posix_mkdir=: 371 | fi 372 | rmdir "$tmpdir/d" "$tmpdir" 373 | else 374 | # Remove any dirs left behind by ancient mkdir implementations. 375 | rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null 376 | fi 377 | trap '' 0;; 378 | esac;; 379 | esac 380 | 381 | if 382 | $posix_mkdir && ( 383 | umask $mkdir_umask && 384 | $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" 385 | ) 386 | then : 387 | else 388 | 389 | # The umask is ridiculous, or mkdir does not conform to POSIX, 390 | # or it failed possibly due to a race condition. Create the 391 | # directory the slow way, step by step, checking for races as we go. 392 | 393 | case $dstdir in 394 | /*) prefix='/';; 395 | [-=\(\)!]*) prefix='./';; 396 | *) prefix='';; 397 | esac 398 | 399 | eval "$initialize_posix_glob" 400 | 401 | oIFS=$IFS 402 | IFS=/ 403 | $posix_glob set -f 404 | set fnord $dstdir 405 | shift 406 | $posix_glob set +f 407 | IFS=$oIFS 408 | 409 | prefixes= 410 | 411 | for d 412 | do 413 | test X"$d" = X && continue 414 | 415 | prefix=$prefix$d 416 | if test -d "$prefix"; then 417 | prefixes= 418 | else 419 | if $posix_mkdir; then 420 | (umask=$mkdir_umask && 421 | $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break 422 | # Don't fail if two instances are running concurrently. 423 | test -d "$prefix" || exit 1 424 | else 425 | case $prefix in 426 | *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; 427 | *) qprefix=$prefix;; 428 | esac 429 | prefixes="$prefixes '$qprefix'" 430 | fi 431 | fi 432 | prefix=$prefix/ 433 | done 434 | 435 | if test -n "$prefixes"; then 436 | # Don't fail if two instances are running concurrently. 437 | (umask $mkdir_umask && 438 | eval "\$doit_exec \$mkdirprog $prefixes") || 439 | test -d "$dstdir" || exit 1 440 | obsolete_mkdir_used=true 441 | fi 442 | fi 443 | fi 444 | 445 | if test -n "$dir_arg"; then 446 | { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && 447 | { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && 448 | { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || 449 | test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 450 | else 451 | 452 | # Make a couple of temp file names in the proper directory. 453 | dsttmp=$dstdir/_inst.$$_ 454 | rmtmp=$dstdir/_rm.$$_ 455 | 456 | # Trap to clean up those temp files at exit. 457 | trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 458 | 459 | # Copy the file name to the temp name. 460 | (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && 461 | 462 | # and set any options; do chmod last to preserve setuid bits. 463 | # 464 | # If any of these fail, we abort the whole thing. If we want to 465 | # ignore errors from any of these, just make sure not to ignore 466 | # errors from the above "$doit $cpprog $src $dsttmp" command. 467 | # 468 | { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && 469 | { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && 470 | { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && 471 | { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && 472 | 473 | # If -C, don't bother to copy if it wouldn't change the file. 474 | if $copy_on_change && 475 | old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && 476 | new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && 477 | 478 | eval "$initialize_posix_glob" && 479 | $posix_glob set -f && 480 | set X $old && old=:$2:$4:$5:$6 && 481 | set X $new && new=:$2:$4:$5:$6 && 482 | $posix_glob set +f && 483 | 484 | test "$old" = "$new" && 485 | $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 486 | then 487 | rm -f "$dsttmp" 488 | else 489 | # Rename the file to the real destination. 490 | $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || 491 | 492 | # The rename failed, perhaps because mv can't rename something else 493 | # to itself, or perhaps because mv is so ancient that it does not 494 | # support -f. 495 | { 496 | # Now remove or move aside any old file at destination location. 497 | # We try this two ways since rm can't unlink itself on some 498 | # systems and the destination file might be busy for other 499 | # reasons. In this case, the final cleanup might fail but the new 500 | # file should still install successfully. 501 | { 502 | test ! -f "$dst" || 503 | $doit $rmcmd -f "$dst" 2>/dev/null || 504 | { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && 505 | { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } 506 | } || 507 | { echo "$0: cannot unlink or rename $dst" >&2 508 | (exit 1); exit 1 509 | } 510 | } && 511 | 512 | # Now rename the file to the real destination. 513 | $doit $mvcmd "$dsttmp" "$dst" 514 | } 515 | fi || exit 1 516 | 517 | trap '' 0 518 | fi 519 | done 520 | 521 | # Local variables: 522 | # eval: (add-hook 'write-file-hooks 'time-stamp) 523 | # time-stamp-start: "scriptversion=" 524 | # time-stamp-format: "%:y-%02m-%02d.%02H" 525 | # time-stamp-time-zone: "UTC" 526 | # time-stamp-end: "; # UTC" 527 | # End: 528 | -------------------------------------------------------------------------------- /makefile.nt: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile for cpmtools, using MS Visual C++ 6.x command-line tools 3 | # 4 | CC= cl 5 | DISKDEFS= c:/cpm/etc/diskdefs 6 | CPPFLAGS= /DDISKDEFS=\"$(DISKDEFS)\" /DFORMAT=\"ibm-3740\" 7 | # 8 | # Note: The "/Zp1" (pack structures on 1-byte boundaries) is important. 9 | # 10 | CFLAGS= /Zp1 /WX /W3 $(CPPFLAGS) 11 | LDFLAGS= 12 | #LDFLAGS= -g -L/usr/dmalloc/lib 13 | LIBCURSES= -lcurses 14 | LIBS= getopt.obj 15 | #LIBS= -ldmalloc 16 | MAKEDEPEND= mkdep -d 17 | #MAKEDEPEND= gcc -MM 18 | #MAKEDEPEND= makedepend -f- 19 | BINDIR= /usr/cpm/bin 20 | MANDIR= /usr/cpm/man/en 21 | OSDEPEND= os_win32.obj 22 | #OSDEPEND= os_unix.obj # Can be used if you don't want direct 23 | # floppy support 24 | 25 | ALL= cpmls.exe cpmrm.exe cpmcp.exe mkfs.cpm.exe fsck.cpm.exe \ 26 | # fsed.cpm.exe 27 | 28 | all: $(ALL) 29 | 30 | cpmls.exe: cpmls.obj cpmfs.obj $(OSDEPEND) getopt.obj 31 | $(CC) $(LDFLAGS) -o $@ cpmls.obj cpmfs.obj $(OSDEPEND) $(LIBS) 32 | 33 | cpmrm.exe: cpmrm.obj cpmfs.obj $(OSDEPEND) getopt.obj 34 | $(CC) $(LDFLAGS) -o $@ cpmrm.obj cpmfs.obj $(OSDEPEND) $(LIBS) 35 | 36 | cpmcp.exe: cpmcp.obj cpmfs.obj $(OSDEPEND) getopt.obj 37 | $(CC) $(LDFLAGS) -o $@ cpmcp.obj cpmfs.obj $(OSDEPEND) $(LIBS) 38 | 39 | mkfs.cpm.exe: mkfs.cpm.obj cpmfs.obj $(OSDEPEND) getopt.obj 40 | $(CC) $(LDFLAGS) -o $@ mkfs.cpm.obj cpmfs.obj $(OSDEPEND) $(LIBS) 41 | 42 | fsck.cpm.exe: fsck.cpm.obj cpmfs.obj $(OSDEPEND) getopt.obj 43 | $(CC) $(LDFLAGS) -o $@ fsck.cpm.obj cpmfs.obj $(OSDEPEND) $(LIBS) 44 | 45 | fsed.cpm.exe: fsed.cpm.obj getopt.obj 46 | $(CC) $(LDFLAGS) -o $@ fsed.cpm.obj $(LIBCURSES) $(LIBS) 47 | 48 | fsck.test: fsck.cpm.exe 49 | -.\\fsck.cpm.exe -n badfs/status 50 | -.\\fsck.cpm.exe -n badfs/extno 51 | -.\\fsck.cpm.exe -n badfs/lcr 52 | -.\\fsck.cpm.exe -n badfs/name 53 | -.\\fsck.cpm.exe -n badfs/extension 54 | -.\\fsck.cpm.exe -n badfs/blocknumber 55 | -.\\fsck.cpm.exe -n badfs/recordcount 56 | -.\\fsck.cpm.exe -n badfs/hugecom 57 | -.\\fsck.cpm.exe -n badfs/timestamps 58 | -.\\fsck.cpm.exe -n badfs/multipleblocks 59 | -.\\fsck.cpm.exe -n badfs/doubleext 60 | -.\\fsck.cpm.exe -f pcw -n badfs/label 61 | 62 | install: all 63 | install -c -s -m 755 cpmls $(BINDIR)/cpmls 64 | install -c -s -m 755 cpmcp $(BINDIR)/cpmcp 65 | install -c -s -m 755 cpmrm $(BINDIR)/cpmrm 66 | install -c -s -m 755 mkfs.cpm $(BINDIR)/mkfs.cpm 67 | install -c -s -m 755 fsck.cpm $(BINDIR)/fsck.cpm 68 | install -c -s -m 755 fsed.cpm $(BINDIR)/fsed.cpm 69 | install -c -m 644 diskdefs $(DISKDEFS) 70 | install -c -m 644 cpmls.1 $(MANDIR)/man1/cpmls.1 71 | install -c -m 644 cpmcp.1 $(MANDIR)/man1/cpmcp.1 72 | install -c -m 644 cpmrm.1 $(MANDIR)/man1/cpmrm.1 73 | install -c -m 644 mkfs.cpm.1 $(MANDIR)/man1/mkfs.cpm.1 74 | install -c -m 644 fsck.cpm.1 $(MANDIR)/man1/fsck.cpm.1 75 | install -c -m 644 fsed.cpm.1 $(MANDIR)/man1/fsed.cpm.1 76 | 77 | clean: 78 | rm -f *.obj 79 | 80 | clobber: clean 81 | rm -f $(ALL) *.out 82 | 83 | tar: clobber 84 | (b=`pwd`; b=`basename $$b`; cd ..; tar zcvf $$b.tar.gz $$b) 85 | 86 | depend: 87 | $(MAKEDEPEND) $(CPPFLAGS) *.c >.depend 88 | 89 | include .depend 90 | -------------------------------------------------------------------------------- /mkfs.cpm.1: -------------------------------------------------------------------------------- 1 | .TH MKFS.CPM 1 "Jan 23, 2019" "CP/M tools" "User commands" 2 | .SH NAME \"{{{roff}}}\"{{{ 3 | mkfs.cpm \- make a CP/M file system 4 | .\"}}} 5 | .SH SYNOPSIS \"{{{ 6 | .ad l 7 | .B mkfs.cpm 8 | .RB [ \-f 9 | .IR format ] 10 | .RB [ \-b 11 | .IR boot ] 12 | .RB [ \-L 13 | .IR label ] 14 | .RB [ \-t ] 15 | .I image 16 | .ad b 17 | .\"}}} 18 | .SH DESCRIPTION \"{{{ 19 | \fBmkfs.cpm\fP makes a CP/M file system on an image file or device. 20 | .\"}}} 21 | .SH OPTIONS \"{{{ 22 | .IP "\fB\-f\fP \fIformat\fP" 23 | Use the given CP/M disk \fIformat\fP instead of the default format. 24 | .IP "\fB\-b\fP \fIbootblock\fP" 25 | Write the contents of the file \fIbootblock\fP to the system tracks 26 | instead of filling them with 0xe5. This option can be used up to four 27 | times. The file contents (typically boot block, CCP, BDOS and BIOS) 28 | are written to sequential sectors, padding with 0xe5 if needed. 29 | .IP "\fB\-L\fP \fIlabel\fP" 30 | Label the file system. This is only supported by CP/M Plus. 31 | .IP "\fB\-t\fP" 32 | Create time stamps. 33 | .\"}}} 34 | .SH "RETURN VALUE" \"{{{ 35 | Upon successful completion, exit code 0 is returned. 36 | .\"}}} 37 | .SH ERRORS \"{{{ 38 | Any errors are indicated by exit code 1. 39 | .\"}}} 40 | .SH ENVIRONMENT \"{{{ 41 | CPMTOOLSFMT Default format 42 | .\"}}} 43 | .SH FILES \"{{{ 44 | ${prefix}/share/diskdefs CP/M disk format definitions 45 | .\"}}} 46 | .SH AUTHORS \"{{{ 47 | This program is copyright 1997\(en2012 Michael Haardt 48 | . The Windows port is copyright 2000, 2001, 2011 John Elliott 49 | . 50 | .PP 51 | This program is free software; you can redistribute it and/or modify 52 | it under the terms of the GNU General Public License as published by 53 | the Free Software Foundation; either version 3 of the License, or 54 | (at your option) any later version. 55 | .PP 56 | This program is distributed in the hope that it will be useful, 57 | but WITHOUT ANY WARRANTY; without even the implied warranty of 58 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 59 | GNU General Public License for more details. 60 | .PP 61 | You should have received a copy of the GNU General Public License along 62 | with this program. If not, write to the Free Software Foundation, Inc., 63 | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 64 | .\"}}} 65 | .SH "SEE ALSO" \"{{{ 66 | .IR fsck.cpm (1), 67 | .IR cpmls (1), 68 | .IR mkfs (1), 69 | .IR cpm (5) 70 | .\"}}} 71 | -------------------------------------------------------------------------------- /mkfs.cpm.1.in: -------------------------------------------------------------------------------- 1 | .TH MKFS.CPM 1 "@UPDATED@" "CP/M tools" "User commands" 2 | .SH NAME \"{{{roff}}}\"{{{ 3 | mkfs.cpm \- make a CP/M file system 4 | .\"}}} 5 | .SH SYNOPSIS \"{{{ 6 | .ad l 7 | .B mkfs.cpm 8 | .RB [ \-f 9 | .IR format ] 10 | .RB [ \-b 11 | .IR boot ] 12 | .RB [ \-L 13 | .IR label ] 14 | .RB [ \-t ] 15 | .I image 16 | .ad b 17 | .\"}}} 18 | .SH DESCRIPTION \"{{{ 19 | \fBmkfs.cpm\fP makes a CP/M file system on an image file or device. 20 | .\"}}} 21 | .SH OPTIONS \"{{{ 22 | .IP "\fB\-f\fP \fIformat\fP" 23 | Use the given CP/M disk \fIformat\fP instead of the default format. 24 | .IP "\fB\-b\fP \fIbootblock\fP" 25 | Write the contents of the file \fIbootblock\fP to the system tracks 26 | instead of filling them with 0xe5. This option can be used up to four 27 | times. The file contents (typically boot block, CCP, BDOS and BIOS) 28 | are written to sequential sectors, padding with 0xe5 if needed. 29 | .IP "\fB\-L\fP \fIlabel\fP" 30 | Label the file system. This is only supported by CP/M Plus. 31 | .IP "\fB\-t\fP" 32 | Create time stamps. 33 | .\"}}} 34 | .SH "RETURN VALUE" \"{{{ 35 | Upon successful completion, exit code 0 is returned. 36 | .\"}}} 37 | .SH ERRORS \"{{{ 38 | Any errors are indicated by exit code 1. 39 | .\"}}} 40 | .SH ENVIRONMENT \"{{{ 41 | CPMTOOLSFMT Default format 42 | .\"}}} 43 | .SH FILES \"{{{ 44 | @DATADIR@/diskdefs CP/M disk format definitions 45 | .\"}}} 46 | .SH AUTHORS \"{{{ 47 | This program is copyright 1997\(en2012 Michael Haardt 48 | . The Windows port is copyright 2000, 2001, 2011 John Elliott 49 | . 50 | .PP 51 | This program is free software; you can redistribute it and/or modify 52 | it under the terms of the GNU General Public License as published by 53 | the Free Software Foundation; either version 3 of the License, or 54 | (at your option) any later version. 55 | .PP 56 | This program is distributed in the hope that it will be useful, 57 | but WITHOUT ANY WARRANTY; without even the implied warranty of 58 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 59 | GNU General Public License for more details. 60 | .PP 61 | You should have received a copy of the GNU General Public License along 62 | with this program. If not, write to the Free Software Foundation, Inc., 63 | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 64 | .\"}}} 65 | .SH "SEE ALSO" \"{{{ 66 | .IR fsck.cpm (1), 67 | .IR cpmls (1), 68 | .IR mkfs (1), 69 | .IR cpm (5) 70 | .\"}}} 71 | -------------------------------------------------------------------------------- /mkfs.cpm.c: -------------------------------------------------------------------------------- 1 | /* #includes */ /*{{{C}}}*//*{{{*/ 2 | #include "config.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "getopt_.h" 12 | #include "cpmfs.h" 13 | 14 | #ifdef USE_DMALLOC 15 | #include 16 | #endif 17 | /*}}}*/ 18 | /* #defines */ /*{{{*/ 19 | #ifndef O_BINARY 20 | #define O_BINARY 0 21 | #endif 22 | /*}}}*/ 23 | 24 | /* mkfs -- make file system */ /*{{{*/ 25 | static int mkfs(struct cpmSuperBlock *drive, const char *name, const char *format, const char *label, char *bootTracks, int timeStamps) 26 | { 27 | /* variables */ /*{{{*/ 28 | unsigned int i; 29 | char buf[128]; 30 | char firstbuf[128]; 31 | int fd; 32 | unsigned int bytes; 33 | unsigned int trkbytes; 34 | /*}}}*/ 35 | 36 | /* open image file */ /*{{{*/ 37 | if ((fd = open(name, O_BINARY|O_CREAT|O_WRONLY, 0666)) < 0) 38 | { 39 | boo=strerror(errno); 40 | return -1; 41 | } 42 | /*}}}*/ 43 | /* write system tracks */ /*{{{*/ 44 | /* this initialises only whole tracks, so it skew is not an issue */ 45 | trkbytes=drive->secLength*drive->sectrk; 46 | for (i=0; iboottrk; i+=drive->secLength) if (write(fd, bootTracks+i, drive->secLength)!=(ssize_t)drive->secLength) 47 | { 48 | boo=strerror(errno); 49 | close(fd); 50 | return -1; 51 | } 52 | /*}}}*/ 53 | /* write directory */ /*{{{*/ 54 | memset(buf,0xe5,128); 55 | bytes=drive->maxdir*32; 56 | if (bytes%trkbytes) bytes=((bytes+trkbytes)/trkbytes)*trkbytes; 57 | if (timeStamps && (drive->type==CPMFS_P2DOS || drive->type==CPMFS_DR3)) buf[3*32]=0x21; 58 | memcpy(firstbuf,buf,128); 59 | if (drive->type==CPMFS_DR3) 60 | { 61 | time_t now; 62 | struct tm *t; 63 | int min,hour,days; 64 | 65 | firstbuf[0]=0x20; 66 | for (i=0; i<11 && *label; ++i,++label) firstbuf[1+i]=toupper(*label&0x7f); 67 | while (i<11) firstbuf[1+i++]=' '; 68 | firstbuf[12]=timeStamps ? 0x11 : 0x01; /* label set and first time stamp is creation date */ 69 | memset(&firstbuf[13],0,1+2+8); 70 | if (timeStamps) 71 | { 72 | int year; 73 | 74 | /* Stamp label. */ 75 | time(&now); 76 | t=localtime(&now); 77 | min=((t->tm_min/10)<<4)|(t->tm_min%10); 78 | hour=((t->tm_hour/10)<<4)|(t->tm_hour%10); 79 | for (year=1978,days=0; year<1900+t->tm_year; ++year) 80 | { 81 | days+=365; 82 | if (year%4==0 && (year%100!=0 || year%400==0)) ++days; 83 | } 84 | days += t->tm_yday + 1; 85 | firstbuf[24]=firstbuf[28]=days&0xff; firstbuf[25]=firstbuf[29]=days>>8; 86 | firstbuf[26]=firstbuf[30]=hour; 87 | firstbuf[27]=firstbuf[31]=min; 88 | } 89 | } 90 | for (i=0; itype==CPMFS_P2DOS || drive->type==CPMFS_DR3)) /*{{{*/ 105 | { 106 | int offset,j; 107 | struct cpmInode ino, root; 108 | static const char sig[] = "!!!TIME"; 109 | unsigned int records; 110 | struct dsDate *ds; 111 | struct cpmSuperBlock super; 112 | const char *err; 113 | 114 | if ((err=Device_open(&super.dev,name,O_RDWR,NULL))) 115 | { 116 | fprintf(stderr,"%s: can not open %s (%s)\n",cmd,name,err); 117 | exit(1); 118 | } 119 | cpmReadSuper(&super,&root,format); 120 | 121 | records=root.sb->maxdir/8; 122 | if (!(ds=malloc(records*128))) 123 | { 124 | cpmUmount(&super); 125 | return -1; 126 | } 127 | memset(ds,0,records*128); 128 | offset=15; 129 | for (i=0; ids=ds; 148 | root.sb->dirtyDs=1; 149 | cpmUmount(&super); 150 | } 151 | /*}}}*/ 152 | 153 | return 0; 154 | } 155 | /*}}}*/ 156 | 157 | const char cmd[]="mkfs.cpm"; 158 | 159 | int main(int argc, char *argv[]) /*{{{*/ 160 | { 161 | char *image; 162 | const char *format; 163 | int c,usage=0; 164 | struct cpmSuperBlock drive; 165 | struct cpmInode root; 166 | const char *label="unlabeled"; 167 | int timeStamps=0; 168 | size_t bootTrackSize,used; 169 | char *bootTracks; 170 | const char *boot[4]={(const char*)0,(const char*)0,(const char*)0,(const char*)0}; 171 | 172 | if (!(format=getenv("CPMTOOLSFMT"))) format=FORMAT; 173 | while ((c=getopt(argc,argv,"b:f:L:th?"))!=EOF) switch(c) 174 | { 175 | case 'b': 176 | { 177 | if (boot[0]==(const char*)0) boot[0]=optarg; 178 | else if (boot[1]==(const char*)0) boot[1]=optarg; 179 | else if (boot[2]==(const char*)0) boot[2]=optarg; 180 | else if (boot[3]==(const char*)0) boot[3]=optarg; 181 | else usage=1; 182 | break; 183 | } 184 | case 'f': format=optarg; break; 185 | case 'L': label=optarg; break; 186 | case 't': timeStamps=1; break; 187 | case 'h': 188 | case '?': usage=1; break; 189 | } 190 | 191 | if (optind!=(argc-1)) usage=1; 192 | else image=argv[optind++]; 193 | 194 | if (usage) 195 | { 196 | fprintf(stderr,"Usage: %s [-f format] [-b boot] [-L label] [-t] image\n",cmd); 197 | exit(1); 198 | } 199 | drive.dev.opened=0; 200 | cpmReadSuper(&drive,&root,format); 201 | bootTrackSize=drive.boottrk*drive.secLength*drive.sectrk; 202 | if ((bootTracks=malloc(bootTrackSize))==(void*)0) 203 | { 204 | fprintf(stderr,"%s: can not allocate boot track buffer: %s\n",cmd,strerror(errno)); 205 | exit(1); 206 | } 207 | memset(bootTracks,0xe5,bootTrackSize); 208 | used=0; 209 | for (c=0; c<4 && boot[c]; ++c) 210 | { 211 | int fd; 212 | size_t size; 213 | 214 | if ((fd=open(boot[c],O_BINARY|O_RDONLY))==-1) 215 | { 216 | fprintf(stderr,"%s: can not open %s: %s\n",cmd,boot[c],strerror(errno)); 217 | exit(1); 218 | } 219 | size=read(fd,bootTracks+used,bootTrackSize-used); 220 | #if 0 221 | fprintf(stderr,"%d %04x %s\n",c,used+0x800,boot[c]); 222 | #endif 223 | if (size%drive.secLength) size=(size|(drive.secLength-1))+1; 224 | used+=size; 225 | close(fd); 226 | } 227 | if (mkfs(&drive,image,format,label,bootTracks,timeStamps)==-1) 228 | { 229 | fprintf(stderr,"%s: can not make new file system: %s\n",cmd,boo); 230 | exit(1); 231 | } 232 | else exit(0); 233 | } 234 | /*}}}*/ 235 | --------------------------------------------------------------------------------