diff --git a/bootstrap/README.md b/bootstrap/README.md new file mode 100644 index 0000000..8ffc50c --- /dev/null +++ b/bootstrap/README.md @@ -0,0 +1,138 @@ +# Booting V7 on a SIMH PDP11/70 + +To generate a working V7 installation, I followed [Installing v7 on SIMH](https://gunkies.org/wiki/Installing_v7_on_SIMH). I include a copy of the RP06 disk image that is made by following the instructions on that page. It can be used to give you a fast start to running V7. + +## Basic installation + +Start by making a directory to work in, say _v7_. + +Get a working simulator, _git clone_ [https://github.com/simh/simh](https://github.com/simh/simh), change into the directory and run ```make pdp11```, you'll find the _pdp11_ command in the _BIN_ directory. You can install this binary in the _v7_ directory or on your own _bin_ directory if you have one. + +Copy _rp06-0.disk.gz_ to the _v7_ directory, and unzip it using the _gunzip_ command. + +Copy the _v7_ command script into your _v7_ directory. + +This script expects to use a file called _v7boot.ini_ to configure the installation. It defines the devices that the _pdp11_ will support, the _v7conf.ini_ file in this directory matches the current kernel installed on the disk. Copy _v7boot.ini_ into your _v7_ directory. + +## Initial boot + +Change into your _v7_ directory and type ``v7``: + +``` sh +$ v7 + +PDP-11 simulator V4.0-0 Current git commit id: 37bc857b + +After Disabling XQ is displayed type boot +and at the : prompt type hp(0,0)unix + +Disabling XQ +./v7boot.ini-10> att tm0 tm0.tap +%SIM-INFO: TM0: creating new file +%SIM-INFO: TM0: Tape Image 'tm0.tap' scanned as SIMH format +``` +SIMH has created an empty file _tm0.tap_ that is the tape image, I'll come back to that later. There is a puzzling lack of a prompt, but it's waiting for you to type ``boot`` to load a small program to get things started. You can use ^H to delete characters. Back to the listing (including the last line we had above): + +``` sh +%SIM-INFO: TM0: Tape Image 'tm0.tap' scanned as SIMH format +boot +: +``` +Now we have a prompt _:_, and need to load the kernel code, type ``hp(0,0)unix``. This loads the code for the kernel from HP device zero, looking for the file in partition zero. Note this allows you to load other versions of the kernel, always keep a working version around. + +``` sh +boot +: hp(0,0)unix +mem = 2020544 +# +``` +You are now booted into a single user system, using just the root partition. If you type stuff, you'll find that it comes out in upper-case, which is convenient if your console is an ASR33 teletype. To get into multiuser mode, type Control-D, which is not echoed: + +``` sh +: hp(0,0)unix +mem = 2020544 +# RESTRICTED RIGHTS: USE, DUPLICATION, OR DISCLOSURE +IS SUBJECT TO RESTRICTIONS STATED IN YOUR CONTRACT WITH +WESTERN ELECTRIC COMPANY, INC. +WED DEC 31 19:10:00 EST 1969 + +login: +``` + +You can login as ```root``` with a password of ```root```, or use ```dmr``` as a user with no password, and type a few commands: + +``` sh +login: dmr +$ ls +$ pwd +/usr/dmr +$ +``` + +To stop the simulation, you need to ensure that any disk writes are written to the disk before stopping, to do this type ```sync``` a few times. Then hit ^E to return to the simulator, and finally ```q``` to quit. + +``` sh +$ pwd +/usr/dmr +$ sync +$ sync +$ sync +$ sync +$ +Simulation stopped, PC: 002306 (MOV (SP)+,177776) +sim> q +Goodbye +``` + +# Getting files in and out + +Although V7 has a _tar_, it uses a binary format, and not the text format that was eventually adopted by POSIX to become the standard. I wrote a version of the _tp_ program in Python for the V6 project - see [uxtp](https://github.com/pcollinson/unixv6-extras/tree/main/uxtp). Sadly, _tp_ on the V6 and V7 won't create directories, so I included the ability to add a shell script to the dump called _makedirs.sh_ that creates any directory that is needed. This has proved a very useful idea. + +Here's a demo of getting files from this distribution into V7. Let's move the contents _sh-test_ which looks for and installs _/bin/['_ which I found was missing on my distribution when I started writing shell scripts. The _tp_ internal file format includes the path name to the files, and you are limited to 31 characters, so you need to start close to the source. The program will write into the _tm0.tap_ that SIMH made earlier. Your paths may vary below. + +``` sh +$ cd unixv7-extras +$ uxtp -rvs ../v7/tm0.tap sh-test +Adding makedirs.sh +Adding sh-test/README.md +Adding sh-test/check +``` + +Now you can fire up the V7 system and login: + +``` sh +$ tp xvm makedirs.sh +x makedirs.sh +End +$ sh makedirs.sh +$ tp xvm +x makedirs.sh +x sh-test/README.md +x sh-test/check +End +$ ls sh-test +README.md +check +$ +``` + +This sequence first gets the _makedirs.sh_ file from the tape, and next it's run to create any necessary directories. Then _tp_ command extracts the contents into the directories that were made. Finally run _ls_ command to look at the files. + +You'll find that if you write to the tape from V7 using ```tp rm files```, it will appear on _tm0.tap_ and can be read and used immediately. Incidentally, this is not true for DECTAPE. + +If you want to get files into the system while V7 is live, then you need to detach and reattach the tape file. I have a little SIMH control file that resets the tape. First you need to type ^E to get back to the simulator control level. This pauses the running V7 and you can return using the _co_ command. SIMH has a ```do``` command that allows you to put commands in a file. I have included a file called _relm_ which detaches and re-attaches the tape drive. + +``` sh +$ +Simulation stopped, PC: 002360 (MOV (SP)+,177776) +sim> do relm +./relm-2> att tm0 tm0.tap +%SIM-INFO: TM0: Tape Image 'tm0.tap' scanned as SIMH format + +``` + +The ^E I typed at the V7 prompt is not shown. The _relm_ file ends in ```co``` to continue back to the running system. To get the prompt again, type return. You can now read the new data from the tape. + +## What next? + +The next step is to configure the kernel adding new devices - see [v7conf](../v7conf). diff --git a/bootstrap/nboot.ini b/bootstrap/nboot.ini new file mode 100644 index 0000000..885bc84 --- /dev/null +++ b/bootstrap/nboot.ini @@ -0,0 +1,12 @@ +echo +echo After Disabling XQ is displayed type +echo boot - there is not prompt +echo then at the : prompt type hp(0,0)unix +echo Login as root, password root +set cpu 11/70 +set cpu 2M +set cpu idle +set rp0 rp06 +att rp0 rp06-0.disk +att tm0 tm0.tap +boot rp0 diff --git a/bootstrap/relm b/bootstrap/relm new file mode 100644 index 0000000..3305075 --- /dev/null +++ b/bootstrap/relm @@ -0,0 +1,3 @@ +det tm0 +att tm0 tm0.tap +co diff --git a/bootstrap/research-unix-7-pdp11-45.pdf b/bootstrap/research-unix-7-pdp11-45.pdf new file mode 100644 index 0000000..0fdecd2 Binary files /dev/null and b/bootstrap/research-unix-7-pdp11-45.pdf differ diff --git a/bootstrap/rp06-0.disk.gz b/bootstrap/rp06-0.disk.gz new file mode 100644 index 0000000..da4c302 Binary files /dev/null and b/bootstrap/rp06-0.disk.gz differ diff --git a/bootstrap/tapei.ini b/bootstrap/tapei.ini new file mode 100644 index 0000000..e9e0e57 --- /dev/null +++ b/bootstrap/tapei.ini @@ -0,0 +1,6 @@ +set cpu 11/45 +set cpu idle +set rp0 rp06 +att rp0 rp06-0.disk +att tm0 v7.tap +boot tm0 diff --git a/bootstrap/v7 b/bootstrap/v7 new file mode 100755 index 0000000..f846ff4 --- /dev/null +++ b/bootstrap/v7 @@ -0,0 +1 @@ +exec pdp11 v7boot.ini diff --git a/bootstrap/v7.tap.gz b/bootstrap/v7.tap.gz new file mode 100644 index 0000000..08f0d6b Binary files /dev/null and b/bootstrap/v7.tap.gz differ diff --git a/bootstrap/v7boot.ini b/bootstrap/v7boot.ini new file mode 100644 index 0000000..4e744f1 --- /dev/null +++ b/bootstrap/v7boot.ini @@ -0,0 +1,11 @@ +echo +echo After Disabling XQ is displayed type boot +echo and at the : prompt type hp(0,0)unix +echo +set cpu 11/70 +set cpu 2M +set cpu idle +set rp0 rp06 +att rp0 rp06-0.disk +att tm0 tm0.tap +boot rp0 diff --git a/bootstrap/v7conf b/bootstrap/v7conf new file mode 100644 index 0000000..2ba13d7 --- /dev/null +++ b/bootstrap/v7conf @@ -0,0 +1,6 @@ +hp +root hp 0 +swap hp 1 +swplo 0 +nswap 8778 +tm diff --git a/fsck/README.md b/fsck/README.md new file mode 100644 index 0000000..0c04803 --- /dev/null +++ b/fsck/README.md @@ -0,0 +1,15 @@ +# fsck - file system check program + +This version of _fsck_ was distributed in the _v7addenda_ tape, but needed a little bit of work to make it work nicely on V7. I've added some _#defines_ and associated _#ifdef_ sections to preserve the original code. + +This version will put orphaned files into a _lost+found_ directory that needs to be installed at the top of any file system you intend to run the code on. I've included a shell script that will make a suitable directory with space in its blocks for some files. + +If you use _fsck_ on a large partition, the program asks for a temporary file, this should not be on the disk being checked. + +## Compilation + +Use ```make``` to compile the code, ```make install``` to install the binary and manual page, and ```make clean``` to remove the _fsck_ binary. + +## Tests + +The [tests](tests) directory contains several testing programs that can induce a file system problem, that _fsck_ can fix. diff --git a/fsck/fsck.1m b/fsck/fsck.1m new file mode 100644 index 0000000..899c3af --- /dev/null +++ b/fsck/fsck.1m @@ -0,0 +1,197 @@ +.TH FSCK 1M +.SH NAME +fsck \- file system consistency check and interactive repair +.SH SYNOPSIS +.B /etc/fsck +[ option ] ... +[ filesystem ] ... +] ... +.SH DESCRIPTION +.I Fsck +audits and interactively repairs inconsistent conditions for +the named +.IR filesystems. +If a file system is consistent then the number of files, number of blocks +used, and number of blocks free are reported. +If the file system is inconsistent the operator is prompted for concurrence +before each correction is attempted. +Most corrections lose data; +all losses are reported. +The default action for each correction +is to wait for the operator to respond +`yes' or `no'. +Without write permission +.I fsck +defaults to +.BR "\-n " action. +.PP +These options are recognized: +.TP +.B \-y +Assume a yes response to all questions. +.TP +.B \-n +Assume a no response to all questions. +.TP +.BI \-s X +Ignore the actual free list and (unconditionally) construct a new +one by rewriting the super-block of the file system. +The file system should be unmounted while this is done, +or extreme care should be taken that the system is quiescent +and that it is rebooted immediately afterwards. +This precaution is necessary so that the old, bad, in-core copy +of the superblock will not continue to be used, or written on the file system. +.IP +The free list is created with optimal interleaving +according to the specification +.IR X : +.RS +.IP +.B \-s3 +optimal for RP03 +.br +.B \-s4 +optimal for RP04, RP05, RP06 +.br +.BI \-s c : s +space free blocks +.I s +blocks apart in cylinders of +.I c +blocks each. +.RE +.IP +If +.I X +is not given, +the values used when the filesystem was created +are used. +If these values were not specified, then +.IR c =400, +.IR s =9 +is assumed. +.TP +.BI \-S X +Conditionally reconstruct the free list. +This option +is like +.BR \-s X +except that the free list is rebuilt only +if there were no discrepancies discovered in the +file system. +It is useful for forcing free list reorganization +on uncontaminated file systems. +.B \-S +forces +.BR \-n . +.TP +.B \-t +If +.I fsck +cannot obtain enough memory to keep its tables, +it uses a scratch files. +If the +.B -t +option is +specified, the file named in the next argument +is used as the scratch file. +Without the +.B \-t +option, +.I fsck +prompts if it needs a +scratch file. +The file should not be on the +file system being checked, and if it is not +a special file or did not already exist, it is +removed when +.I fsck +completes. +.PP +If no filesystems are given to +.I fsck +then a default list of file systems is read from +the file +.BR /etc/checklist . +.PP +.ne 10 +Inconsistencies checked are as follows: +.TP +1. +Blocks claimed by more than one inode or the free list. +.TP +2. +Blocks claimed by an inode or the free list outside the range of the file system. +.TP +3. +Incorrect link counts. +.TP +4. +Size checks: +.RS +Incorrect number of blocks in file. +.br +Directory size not a multiple of 16 bytes. +.RE +.TP +5. +Bad inode format. +.TP +6. +Blocks not accounted for anywhere. +.TP +7. +Directory checks: +.RS +File pointing to unallocated inode. +.br +Inode number out of range. +.RE +.TP +8. +Super Block checks: +.RS +More than 65536 inodes. +.br +More blocks for inodes than there are in the file system. +.RE +.TP +9. +Bad free block list format. +.TP +10. +Total free block and/or free inode count incorrect. +.PP +Orphaned files and directories (allocated but unreferenced) are, +with the operator's concurrence, reconnected by +placing them in the "lost+found" directory. +The name assigned is the inode number. The only restriction +is that the directory "lost+found" must preexist +in the root of the filesystem being checked and +must have empty slots in which entries can be made. +This is accomplished by making "lost+found", copying +a number of files to the directory, and then removing them +(before +.I +fsck +is executed). +.PP +Checking the raw device is almost always faster. +.SH FILES +/etc/checklist +contains default list of file systems to check. +.SH "SEE ALSO" +dcheck(1), icheck(1), +checklist(5), fs(5), crash(8) +.SH BUGS +Inode numbers for +.B . +and +.B .. +in each directory should be checked for validity. +.br +The +.B \-b +option of +.IR icheck (1) +should be available. diff --git a/fsck/fsck.c b/fsck/fsck.c new file mode 100644 index 0000000..53c8fff --- /dev/null +++ b/fsck/fsck.c @@ -0,0 +1,1704 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * ignore some values defined in the superblock + * but not implemented in UNIXV7. These need to + * be defined to include the code + * + * HAVE_FNAME - superblk.s_fname, superblk.s_fpack + * HAVE_COUNTS - s_tfree - total free blocks, s_tinode - total free inodes + * CYLS_IN_SUPERBLK = s_m, s_n + * + * Peter Collinson + * August 2023 + */ + +typedef int (*SIG_TYP)(); + +#define NDIRECT (BSIZE/sizeof(struct direct)) +#define SPERB (BSIZE/sizeof(short)) + +#define NO 0 +#define YES 1 + +#define MAXDUP 10 /* limit on dup blks (per inode) */ +#define MAXBAD 10 /* limit on bad blks (per inode) */ + +#define STEPSIZE 9 /* default step for freelist spacing */ +#define CYLSIZE 400 /* default cyl size for spacing */ +#define MAXCYL 500 /* maximum cylinder size */ + +#define BITSPB 8 /* number bits per byte */ +#define BITSHIFT 3 /* log2(BITSPB) */ +#define BITMASK 07 /* BITSPB-1 */ +#define LSTATE 2 /* bits per inode state */ +#define STATEPB (BITSPB/LSTATE) /* inode states per byte */ +#define USTATE 0 /* inode not allocated */ +#define FSTATE 01 /* inode is file */ +#define DSTATE 02 /* inode is directory */ +#define CLEAR 03 /* inode is to be cleared */ +#define SMASK 03 /* mask for inode state */ + +typedef struct dinode DINODE; +typedef struct direct DIRECT; + +#define ALLOC ((dp->di_mode & IFMT) != 0) +#define DIR ((dp->di_mode & IFMT) == IFDIR) +#define REG ((dp->di_mode & IFMT) == IFREG) +#define BLK ((dp->di_mode & IFMT) == IFBLK) +#define CHR ((dp->di_mode & IFMT) == IFCHR) +#define MPC ((dp->di_mode & IFMT) == IFMPC) +#define MPB ((dp->di_mode & IFMT) == IFMPB) +#define SPECIAL (BLK || CHR || MPC || MPB) + +#define NINOBLK 11 /* num blks for raw reading */ +#define MAXRAW 110 /* largest raw read (in blks) */ +daddr_t startib; /* blk num of first in raw area */ +unsigned niblk; /* num of blks in raw area */ + +struct bufarea { + struct bufarea *b_next; /* must be first */ + daddr_t b_bno; + union { + char b_buf[BSIZE]; /* buffer space */ + short b_lnks[SPERB]; /* link counts */ + daddr_t b_indir[NINDIR]; /* indirect block */ + struct filsys b_fs; /* super block */ + struct fblk b_fb; /* free block */ + struct dinode b_dinode[INOPB]; /* inode block */ + DIRECT b_dir[NDIRECT]; /* directory */ + } b_un; + char b_dirty; +}; + +typedef struct bufarea BUFAREA; + +BUFAREA inoblk; /* inode blocks */ +BUFAREA fileblk; /* other blks in filesys */ +BUFAREA sblk; /* file system superblock */ +BUFAREA *poolhead; /* ptr to first buffer in pool */ + +#define initbarea(x) (x)->b_dirty = 0;(x)->b_bno = (daddr_t)-1 +#define dirty(x) (x)->b_dirty = 1 +#define inodirty() inoblk.b_dirty = 1 +#define fbdirty() fileblk.b_dirty = 1 +#define sbdirty() sblk.b_dirty = 1 + +#define freeblk fileblk.b_un.b_fb +#define dirblk fileblk.b_un +#define superblk sblk.b_un.b_fs + +struct filecntl { + int rfdes; + int wfdes; + int mod; +}; + +struct filecntl dfile; /* file descriptors for filesys */ +struct filecntl sfile; /* file descriptors for scratch file */ + +typedef unsigned MEMSIZE; + +MEMSIZE memsize; /* amt of memory we got */ +#ifdef pdp11 +#define MAXDATA ((MEMSIZE)54*1024) +#endif +#if vax +#define MAXDATA ((MEMSIZE)400*1024) +#endif +#if interdata +#define MAXDATA ((MEMSIZE)400*1024) +#endif + +#define DUPTBLSIZE 100 /* num of dup blocks to remember */ +daddr_t duplist[DUPTBLSIZE]; /* dup block table */ +daddr_t *enddup; /* next entry in dup table */ +daddr_t *muldup; /* multiple dups part of table */ + +#define MAXLNCNT 20 /* num zero link cnts to remember */ +ino_t badlncnt[MAXLNCNT]; /* table of inos with zero link cnts */ +ino_t *badlnp; /* next entry in table */ + +char sflag; /* salvage free block list */ +char csflag; /* salvage free block list (conditional) */ +char nflag; /* assume a no response */ +char yflag; /* assume a yes response */ +char tflag; /* scratch file specified */ +char rplyflag; /* any questions asked? */ +char hotroot; /* checking root device */ +char rawflg; /* read raw device */ +char rmscr; /* remove scratch file when done */ +char fixfree; /* corrupted free list */ +char *membase; /* base of memory we get */ +char *blkmap; /* ptr to primary blk allocation map */ +char *freemap; /* ptr to secondary blk allocation map */ +char *statemap; /* ptr to inode state table */ +char *pathp; /* pointer to pathname position */ +char *thisname; /* ptr to current pathname component */ +char *srchname; /* name being searched for in dir */ +char pathname[200]; +char scrfile[80]; +char *lfname = "lost+found"; +char *checklist = "/etc/checklist"; + +short *lncntp; /* ptr to link count table */ + +int cylsize; /* num blocks per cylinder */ +int stepsize; /* num blocks for spacing purposes */ +int badblk; /* num of bad blks seen (per inode) */ +int dupblk; /* num of dup blks seen (per inode) */ +int (*pfunc)(); /* function to call to chk blk */ + +ino_t inum; /* inode we are currently working on */ +ino_t imax; /* number of inodes */ +ino_t parentdir; /* i number of parent directory */ +ino_t lastino; /* hiwater mark of inodes */ +ino_t lfdir; /* lost & found directory */ +ino_t orphan; /* orphaned inode */ + +off_t filsize; /* num blks seen in file */ +off_t maxblk; /* largest logical blk in file */ +off_t bmapsz; /* num chars in blkmap */ + +daddr_t smapblk; /* starting blk of state map */ +daddr_t lncntblk; /* starting blk of link cnt table */ +daddr_t fmapblk; /* starting blk of free map */ +daddr_t n_free; /* number of free blocks */ +daddr_t n_blks; /* number of blocks used */ +daddr_t n_files; /* number of files seen */ +daddr_t fmin; /* block number of the first data block */ +daddr_t fmax; /* number of blocks in the volume */ + +#define howmany(x,y) (((x)+((y)-1))/(y)) +#define roundup(x,y) ((((x)+((y)-1))/(y))*(y)) +#define outrange(x) (x < fmin || x >= fmax) +#define zapino(x) clear((char *)(x),sizeof(DINODE)) + +#define setlncnt(x) dolncnt(x,0) +#define getlncnt() dolncnt(0,1) +#define declncnt() dolncnt(0,2) + +#define setbmap(x) domap(x,0) +#define getbmap(x) domap(x,1) +#define clrbmap(x) domap(x,2) + +#define setfmap(x) domap(x,0+4) +#define getfmap(x) domap(x,1+4) +#define clrfmap(x) domap(x,2+4) + +#define setstate(x) dostate(x,0) +#define getstate() dostate(0,1) + +#define DATA 1 +#define ADDR 0 +#define ALTERD 010 +#define KEEPON 04 +#define SKIP 02 +#define STOP 01 + +int (*signal())(); +long lseek(); +long time(); +DINODE *ginode(); +BUFAREA *getblk(); +BUFAREA *search(); +int dirscan(); +int findino(); +int catch(); +int mkentry(); +int chgdd(); +int pass1(); +int pass1b(); +int pass2(); +int pass3(); +int pass4(); +int pass5(); + +main(argc,argv) +int argc; +char *argv[]; +{ + register FILE *fp; + register n; + register char *p; + char filename[50]; + char *sbrk(); + + sync(); + while(--argc > 0 && **++argv == '-') { + switch(*++*argv) { + case 't': + case 'T': + tflag++; + if(**++argv == '-' || --argc <= 0) + errexit("Bad -t option\n"); + p = scrfile; + while(*p++ = **argv) + (*argv)++; + break; + case 's': /* salvage flag */ + stype(++*argv); + sflag++; + break; + case 'S': /* conditional salvage */ + stype(++*argv); + csflag++; + break; + case 'n': /* default no answer flag */ + case 'N': + nflag++; + yflag = 0; + break; + case 'y': /* default yes answer flag */ + case 'Y': + yflag++; + nflag = 0; + break; + default: + errexit("%c option?\n",**argv); + } + } + if(nflag && (sflag || csflag)) + errexit("Incompatible options: -n and -%s\n",sflag?"s":"S"); + if(sflag && csflag) + sflag = 0; + memsize = (MEMSIZE)sbrk(0); + memsize = MAXDATA - memsize - sizeof(int); + while(memsize >= 2*sizeof(BUFAREA) && + (membase = sbrk(memsize)) == (char *)-1) + memsize -= 1024; + if(memsize < 2*sizeof(BUFAREA)) + errexit("Can't get memory\n"); +#define SIG_IGN (int (*)())1 + if (signal(SIGINT, SIG_IGN) != SIG_IGN) + signal(SIGINT, catch); + if(argc) { /* arg list has file names */ + while(argc-- > 0) + check(*argv++); + } + else { /* use default checklist */ + if((fp = fopen(checklist,"r")) == NULL) + errexit("Can't open checklist file: %s\n",checklist); + while(getline(fp,filename,sizeof(filename)) != EOF) + check(filename); + fclose(fp); + } + exit(0); +} + + +/* VARARGS1 */ +error(s1,s2,s3,s4) +char *s1; +{ + printf(s1,s2,s3,s4); +} + + +/* VARARGS1 */ +errexit(s1,s2,s3,s4) +char *s1; +{ + error(s1,s2,s3,s4); + exit(8); +} + + +check(dev) +char *dev; +{ + register DINODE *dp; + register n; + register ino_t *blp; + ino_t savino; + daddr_t blk; + BUFAREA *bp1, *bp2; + + if(setup(dev) == NO) + return; + + + printf("** Phase 1 - Check Blocks and Sizes\n"); + pfunc = pass1; + for(inum = 1; inum <= imax; inum++) { + if((dp = ginode()) == NULL) + continue; + if(ALLOC) { + lastino = inum; + if(ftypeok(dp) == NO) { + printf("UNKNOWN FILE TYPE I=%u",inum); + if(reply("CLEAR") == YES) { + zapino(dp); + inodirty(); + } + continue; + } + n_files++; + if(setlncnt(dp->di_nlink) <= 0) { + if(badlnp < &badlncnt[MAXLNCNT]) + *badlnp++ = inum; + else { + printf("LINK COUNT TABLE OVERFLOW"); + if(reply("CONTINUE") == NO) + errexit(""); + } + } + setstate(DIR ? DSTATE : FSTATE); + badblk = dupblk = 0; + filsize = 0; + maxblk = 0; + ckinode(dp,ADDR); + if((n = getstate()) == DSTATE || n == FSTATE) + sizechk(dp); + } + else if(dp->di_mode != 0) { + printf("PARTIALLY ALLOCATED INODE I=%u",inum); + if(reply("CLEAR") == YES) { + zapino(dp); + inodirty(); + } + } + } + + + if(enddup != &duplist[0]) { + printf("** Phase 1b - Rescan For More DUPS\n"); + pfunc = pass1b; + for(inum = 1; inum <= lastino; inum++) { + if(getstate() != USTATE && (dp = ginode()) != NULL) + if(ckinode(dp,ADDR) & STOP) + break; + } + } + if(rawflg) { + if(inoblk.b_dirty) + bwrite(&dfile,membase,startib,(int)niblk*BSIZE); + inoblk.b_dirty = 0; + if(poolhead) { + clear(membase,niblk*BSIZE); + for(bp1 = poolhead;bp1->b_next;bp1 = bp1->b_next); + bp2 = &((BUFAREA *)membase)[(niblk*BSIZE)/sizeof(BUFAREA)]; + while(--bp2 >= (BUFAREA *)membase) { + initbarea(bp2); + bp2->b_next = bp1->b_next; + bp1->b_next = bp2; + } + } + rawflg = 0; + + } + + + printf("** Phase 2 - Check Pathnames\n"); + inum = ROOTINO; + thisname = pathp = pathname; + pfunc = pass2; + switch(getstate()) { + case USTATE: + errexit("ROOT INODE UNALLOCATED. TERMINATING.\n"); + case FSTATE: + printf("ROOT INODE NOT DIRECTORY"); + if(reply("FIX") == NO || (dp = ginode()) == NULL) + errexit(""); + dp->di_mode &= ~IFMT; + dp->di_mode |= IFDIR; + inodirty(); + setstate(DSTATE); + case DSTATE: + descend(); + break; + case CLEAR: + printf("DUPS/BAD IN ROOT INODE\n"); + if(reply("CONTINUE") == NO) + errexit(""); + setstate(DSTATE); + descend(); + } + + + printf("** Phase 3 - Check Connectivity\n"); + for(inum = ROOTINO; inum <= lastino; inum++) { + if(getstate() == DSTATE) { + pfunc = findino; + srchname = ".."; + savino = inum; + do { + orphan = inum; + if((dp = ginode()) == NULL) + break; + filsize = dp->di_size; + parentdir = 0; + ckinode(dp,DATA); + if((inum = parentdir) == 0) + break; + } while(getstate() == DSTATE); + inum = orphan; + if(linkup() == YES) { + thisname = pathp = pathname; + *pathp++ = '?'; + pfunc = pass2; + descend(); + } + inum = savino; + } + } + + + printf("** Phase 4 - Check Reference Counts\n"); + pfunc = pass4; + for(inum = ROOTINO; inum <= lastino; inum++) { + switch(getstate()) { + case FSTATE: + if(n = getlncnt()) + adjust((short)n); + else { + for(blp = badlncnt;blp < badlnp; blp++) + if(*blp == inum) { + clri("UNREF",YES); + break; + } + } + break; + case DSTATE: + clri("UNREF",YES); + break; + case CLEAR: + clri("BAD/DUP",YES); + } + } +#ifdef HAVE_COUNTS +#ifndef interdata + if(imax - n_files != superblk.s_tinode) { + printf("FREE INODE COUNT WRONG IN SUPERBLK"); + if(reply("FIX") == YES) { + superblk.s_tinode = imax - n_files; + sbdirty(); + } + } +#endif +#endif + flush(&dfile,&fileblk); + + + printf("** Phase 5 - Check Free List "); + if(sflag || (csflag && rplyflag == 0)) { + printf("(Ignored)\n"); + fixfree = 1; + } + else { + printf("\n"); + if(freemap) + copy(blkmap,freemap,(MEMSIZE)bmapsz); + else { + for(blk = 0; blk < fmapblk; blk++) { + bp1 = getblk((BUFAREA *)NULL,blk); + bp2 = getblk((BUFAREA *)NULL,blk+fmapblk); + copy(bp1->b_un.b_buf,bp2->b_un.b_buf,BSIZE); + dirty(bp2); + } + } + badblk = dupblk = 0; + freeblk.df_nfree = superblk.s_nfree; + for(n = 0; n < NICFREE; n++) + freeblk.df_free[n] = superblk.s_free[n]; + freechk(); + if(badblk) + printf("%d BAD BLKS IN FREE LIST\n",badblk); + if(dupblk) + printf("%d DUP BLKS IN FREE LIST\n",dupblk); + if(fixfree == 0) { + if((n_blks+n_free) != (fmax-fmin)) { + printf("%ld BLK(S) MISSING\n", + fmax-fmin-n_blks-n_free); + fixfree = 1; + } +#ifdef HAVE_COUNTS + else if(n_free != superblk.s_tfree) { +#ifndef interdata + printf("FREE BLK COUNT WRONG IN SUPERBLK"); + if(reply("FIX") == YES) { + superblk.s_tfree = n_free; + sbdirty(); + } +#endif + } +#endif + } + if(fixfree) { + printf("BAD FREE LIST"); + if(reply("SALVAGE") == NO) + fixfree = 0; + } + } + + + if(fixfree) { + printf("** Phase 6 - Salvage Free List\n"); + makefree(); + n_free = superblk.s_tfree; + } + + + printf("%ld files %ld blocks %ld free\n", + n_files,n_blks,n_free); + if(dfile.mod) { + time(&superblk.s_time); + sbdirty(); + } + ckfini(); + sync(); + if(dfile.mod && hotroot) { + printf("\n***** BOOT UNIX (NO SYNC!) *****\n"); + for(;;); + } + if(dfile.mod) + printf("\n***** FILE SYSTEM WAS MODIFIED *****\n"); +} + + +ckinode(dp,flg) +DINODE *dp; +register flg; +{ + register daddr_t *ap; + register ret; + int (*func)(), n; + daddr_t iaddrs[NADDR]; + + if(SPECIAL) + return(KEEPON); + l3tol(iaddrs,dp->di_addr,NADDR); + func = (flg == ADDR) ? pfunc : dirscan; + for(ap = iaddrs; ap < &iaddrs[NADDR-3]; ap++) { + if(*ap && (ret = (*func)(*ap)) & STOP) + return(ret); + } + for(n = 1; n < 4; n++) { + if(*ap && (ret = iblock(*ap,n,flg)) & STOP) + return(ret); + ap++; + } + return(KEEPON); +} + + +iblock(blk,ilevel,flg) +daddr_t blk; +register ilevel; +{ + register daddr_t *ap; + register n; + int (*func)(); + BUFAREA ib; + + if(flg == ADDR) { + func = pfunc; + if(((n = (*func)(blk)) & KEEPON) == 0) + return(n); + } + else + func = dirscan; + if(outrange(blk)) /* protect thyself */ + return(SKIP); + initbarea(&ib); + if(getblk(&ib,blk) == NULL) + return(SKIP); + ilevel--; + for(ap = ib.b_un.b_indir; ap < &ib.b_un.b_indir[NINDIR]; ap++) { + if(*ap) { + if(ilevel > 0) { + n = iblock(*ap,ilevel,flg); + } + else + n = (*func)(*ap); + if(n & STOP) + return(n); + } + } + return(KEEPON); +} + + +pass1(blk) +daddr_t blk; +{ + register daddr_t *dlp; + + if(outrange(blk)) { + blkerr("BAD",blk); + if(++badblk >= MAXBAD) { + printf("EXCESSIVE BAD BLKS I=%u",inum); + if(reply("CONTINUE") == NO) + errexit(""); + return(STOP); + } + return(SKIP); + } + if(getbmap(blk)) { + blkerr("DUP",blk); + if(++dupblk >= MAXDUP) { + printf("EXCESSIVE DUP BLKS I=%u",inum); + if(reply("CONTINUE") == NO) + errexit(""); + return(STOP); + } + if(enddup >= &duplist[DUPTBLSIZE]) { + printf("DUP TABLE OVERFLOW."); + if(reply("CONTINUE") == NO) + errexit(""); + return(STOP); + } + for(dlp = duplist; dlp < muldup; dlp++) { + if(*dlp == blk) { + *enddup++ = blk; + break; + } + } + if(dlp >= muldup) { + *enddup++ = *muldup; + *muldup++ = blk; + } + } + else { + n_blks++; + setbmap(blk); + } + filsize++; + return(KEEPON); +} + + +pass1b(blk) +daddr_t blk; +{ + register daddr_t *dlp; + + if(outrange(blk)) + return(SKIP); + for(dlp = duplist; dlp < muldup; dlp++) { + if(*dlp == blk) { + blkerr("DUP",blk); + *dlp = *--muldup; + *muldup = blk; + return(muldup == duplist ? STOP : KEEPON); + } + } + return(KEEPON); +} + + +pass2(dirp) +register DIRECT *dirp; +{ + register char *p; + register n; + DINODE *dp; + + if((inum = dirp->d_ino) == 0) + return(KEEPON); + thisname = pathp; + for(p = dirp->d_name; p < &dirp->d_name[DIRSIZ]; ) + if((*pathp++ = *p++) == 0) { + --pathp; + break; + } + *pathp = 0; + n = NO; + if(inum > imax || inum < ROOTINO) + n = direrr("I OUT OF RANGE"); + else { + again: + switch(getstate()) { + case USTATE: + n = direrr("UNALLOCATED"); + break; + case CLEAR: + if((n = direrr("DUP/BAD")) == YES) + break; + if((dp = ginode()) == NULL) + break; + setstate(DIR ? DSTATE : FSTATE); + goto again; + case FSTATE: + declncnt(); + break; + case DSTATE: + declncnt(); + descend(); + } + } + pathp = thisname; + if(n == NO) + return(KEEPON); + dirp->d_ino = 0; + return(KEEPON|ALTERD); +} + + +pass4(blk) +daddr_t blk; +{ + register daddr_t *dlp; + + if(outrange(blk)) + return(SKIP); + if(getbmap(blk)) { + for(dlp = duplist; dlp < enddup; dlp++) + if(*dlp == blk) { + *dlp = *--enddup; + return(KEEPON); + } + clrbmap(blk); + n_blks--; + } + return(KEEPON); +} + + +pass5(blk) +daddr_t blk; +{ + if(outrange(blk)) { + fixfree = 1; + if(++badblk >= MAXBAD) { + printf("EXCESSIVE BAD BLKS IN FREE LIST."); + if(reply("CONTINUE") == NO) + errexit(""); + return(STOP); + } + return(SKIP); + } + if(getfmap(blk)) { + fixfree = 1; + if(++dupblk >= DUPTBLSIZE) { + printf("EXCESSIVE DUP BLKS IN FREE LIST."); + if(reply("CONTINUE") == NO) + errexit(""); + return(STOP); + } + } + else { + n_free++; + setfmap(blk); + } + return(KEEPON); +} + + +blkerr(s,blk) +daddr_t blk; +char *s; +{ + printf("%ld %s I=%u\n",blk,s,inum); + setstate(CLEAR); /* mark for possible clearing */ +} + + +descend() +{ + register DINODE *dp; + register char *savname; + off_t savsize; + + setstate(FSTATE); + if((dp = ginode()) == NULL) + return; + savname = thisname; + *pathp++ = '/'; + savsize = filsize; + filsize = dp->di_size; + ckinode(dp,DATA); + thisname = savname; + *--pathp = 0; + filsize = savsize; +} + + +dirscan(blk) +daddr_t blk; +{ + register DIRECT *dirp; + register char *p1, *p2; + register n; + DIRECT direntry; + + if(outrange(blk)) { + filsize -= BSIZE; + return(SKIP); + } + for(dirp = dirblk.b_dir; dirp < &dirblk.b_dir[NDIRECT] && + filsize > 0; dirp++, filsize -= sizeof(DIRECT)) { + if(getblk(&fileblk,blk) == NULL) { + filsize -= (&dirblk.b_dir[NDIRECT]-dirp)*sizeof(DIRECT); + return(SKIP); + } + p1 = &dirp->d_name[DIRSIZ]; + p2 = &direntry.d_name[DIRSIZ]; + while(p1 > (char *)dirp) + *--p2 = *--p1; + if((n = (*pfunc)(&direntry)) & ALTERD) { + if(getblk(&fileblk,blk) != NULL) { + p1 = &dirp->d_name[DIRSIZ]; + p2 = &direntry.d_name[DIRSIZ]; + while(p1 > (char *)dirp) + *--p1 = *--p2; + fbdirty(); + } + else + n &= ~ALTERD; + } + if(n & STOP) + return(n); + } + return(filsize > 0 ? KEEPON : STOP); +} + + +direrr(s) +char *s; +{ + register DINODE *dp; + + printf("%s ",s); + pinode(); + if((dp = ginode()) != NULL && ftypeok(dp)) + printf("\n%s=%s",DIR?"DIR":"FILE",pathname); + else + printf("\nNAME=%s",pathname); + return(reply("REMOVE")); +} + + +adjust(lcnt) +register short lcnt; +{ + register DINODE *dp; + + if((dp = ginode()) == NULL) + return; + if(dp->di_nlink == lcnt) { + if(linkup() == NO) + clri("UNREF",NO); + } + else { + printf("LINK COUNT %s", + (lfdir==inum)?lfname:(DIR?"DIR":"FILE")); + pinode(); + printf(" COUNT %d SHOULD BE %d", + dp->di_nlink,dp->di_nlink-lcnt); + if(reply("ADJUST") == YES) { + dp->di_nlink -= lcnt; + inodirty(); + } + } +} + + +clri(s,flg) +char *s; +{ + register DINODE *dp; + + if((dp = ginode()) == NULL) + return; + if(flg == YES) { + printf("%s %s",s,DIR?"DIR":"FILE"); + pinode(); + } + if(reply("CLEAR") == YES) { + n_files--; + pfunc = pass4; + ckinode(dp,ADDR); + zapino(dp); + inodirty(); + } +} + + +setup(dev) +char *dev; +{ + register n; + register BUFAREA *bp; + register MEMSIZE msize; + char *mbase; + daddr_t bcnt, nscrblk; + dev_t rootdev; + off_t smapsz, lncntsz, totsz; + struct { + daddr_t tfree; + ino_t tinode; + char fname[6]; + char fpack[6]; + } ustatarea; + struct stat statarea; + + if(stat("/",&statarea) < 0) + errexit("Can't stat root\n"); + rootdev = statarea.st_dev; + if(stat(dev,&statarea) < 0) { + error("Can't stat %s\n",dev); + return(NO); + } + hotroot = 0; + rawflg = 0; + if((statarea.st_mode & S_IFMT) == S_IFBLK) { + if(ustat(statarea.st_rdev, (char *)&ustatarea) >= 0) { + hotroot++; + } + } + else if((statarea.st_mode & S_IFMT) == S_IFCHR) + rawflg++; + else { + if (reply("file is not a block or character device; OK") == NO) + return(NO); + } + if(rootdev == statarea.st_rdev) + hotroot++; + if((dfile.rfdes = open(dev,0)) < 0) { + error("Can't open %s\n",dev); + return(NO); + } + printf("\n%s",dev); + if(nflag || (dfile.wfdes = open(dev,1)) < 0) { + dfile.wfdes = -1; + printf(" (NO WRITE)"); + } + printf("\n"); + fixfree = 0; + dfile.mod = 0; + n_files = n_blks = n_free = 0; + muldup = enddup = &duplist[0]; + badlnp = &badlncnt[0]; + lfdir = 0; + rplyflag = 0; + initbarea(&sblk); + initbarea(&fileblk); + initbarea(&inoblk); + sfile.wfdes = sfile.rfdes = -1; + rmscr = 0; + if(getblk(&sblk,SUPERB) == NULL) { + ckfini(); + return(NO); + } + imax = ((ino_t)superblk.s_isize - (SUPERB+1)) * INOPB; + fmin = (daddr_t)superblk.s_isize; /* first data blk num */ + fmax = superblk.s_fsize; /* first invalid blk num */ + if(fmin >= fmax || + (imax/INOPB) != ((ino_t)superblk.s_isize-(SUPERB+1))) { + error("Size check: fsize %ld isize %d\n", + superblk.s_fsize,superblk.s_isize); + ckfini(); + return(NO); + } +#ifdef HAVE_FNAME + printf("File System: %.6s Volume: %.6s\n\n", superblk.s_fname, + superblk.s_fpack); +#endif + bmapsz = roundup(howmany(fmax,BITSPB),sizeof(*lncntp)); + smapsz = roundup(howmany((long)(imax+1),STATEPB),sizeof(*lncntp)); + lncntsz = (long)(imax+1) * sizeof(*lncntp); + if(bmapsz > smapsz+lncntsz) + smapsz = bmapsz-lncntsz; + totsz = bmapsz+smapsz+lncntsz; + msize = memsize; + mbase = membase; + if(rawflg) { + if(msize < (MEMSIZE)(NINOBLK*BSIZE) + 2*sizeof(BUFAREA)) + rawflg = 0; + else { + msize -= (MEMSIZE)NINOBLK*BSIZE; + mbase += (MEMSIZE)NINOBLK*BSIZE; + niblk = NINOBLK; + startib = fmax; + } + } + clear(mbase,msize); + if((off_t)msize < totsz) { + bmapsz = roundup(bmapsz,BSIZE); + smapsz = roundup(smapsz,BSIZE); + lncntsz = roundup(lncntsz,BSIZE); + nscrblk = (bmapsz+smapsz+lncntsz)>>BSHIFT; + if(tflag == 0) { + printf("\nNEED SCRATCH FILE (%ld BLKS)\n",nscrblk); + do { + printf("ENTER FILENAME: "); + if((n = getline(stdin,scrfile,sizeof(scrfile))) == EOF) + errexit("\n"); + } while(n == 0); + } + if(stat(scrfile,&statarea) < 0 || + (statarea.st_mode & S_IFMT) == S_IFREG) + rmscr++; + if((sfile.wfdes = creat(scrfile,0666)) < 0 || + (sfile.rfdes = open(scrfile,0)) < 0) { + error("Can't create %s\n",scrfile); + ckfini(); + return(NO); + } + bp = &((BUFAREA *)mbase)[(msize/sizeof(BUFAREA))]; + poolhead = NULL; + while(--bp >= (BUFAREA *)mbase) { + initbarea(bp); + bp->b_next = poolhead; + poolhead = bp; + } + bp = poolhead; + for(bcnt = 0; bcnt < nscrblk; bcnt++) { + bp->b_bno = bcnt; + dirty(bp); + flush(&sfile,bp); + } + blkmap = freemap = statemap = (char *) NULL; + lncntp = (short *) NULL; + smapblk = bmapsz / BSIZE; + lncntblk = smapblk + smapsz / BSIZE; + fmapblk = smapblk; + } + else { + if(rawflg && (off_t)msize > totsz+BSIZE) { + niblk += (unsigned)((off_t)msize-totsz)>>BSHIFT; + if(niblk > MAXRAW) + niblk = MAXRAW; + msize = memsize - (niblk*BSIZE); + mbase = membase + (niblk*BSIZE); + } + poolhead = NULL; + blkmap = mbase; + statemap = &mbase[(MEMSIZE)bmapsz]; + freemap = statemap; + lncntp = (short *)&statemap[(MEMSIZE)smapsz]; + } + return(YES); +} + + +DINODE * +ginode() +{ + register DINODE *dp; + register char *mbase; + daddr_t iblk; + + if(inum > imax) + return(NULL); + iblk = itod(inum); + if(rawflg) { + mbase = membase; + if(iblk < startib || iblk >= startib+niblk) { + if(inoblk.b_dirty) + bwrite(&dfile,mbase,startib,(int)niblk*BSIZE); + inoblk.b_dirty = 0; + if(bread(&dfile,mbase,iblk,(int)niblk*BSIZE) == NO) { + startib = fmax; + return(NULL); + } + startib = iblk; + } + dp = (DINODE *)&mbase[(unsigned)((iblk-startib)<di_mode & IFMT) { + case IFDIR: + case IFREG: + case IFBLK: + case IFCHR: + case IFMPC: + case IFMPB: + return(YES); + default: + return(NO); + } +} + + +reply(s) +char *s; +{ + char line[80]; + + rplyflag = 1; + printf("\n%s? ",s); + if(nflag || csflag || dfile.wfdes < 0) { + printf(" no\n\n"); + return(NO); + } + if(yflag) { + printf(" yes\n\n"); + return(YES); + } + if(getline(stdin,line,sizeof(line)) == EOF) + errexit("\n"); + printf("\n"); + if(line[0] == 'y' || line[0] == 'Y') + return(YES); + else + return(NO); +} + + +getline(fp,loc,maxlen) +FILE *fp; +char *loc; +{ + register n; + register char *p, *lastloc; + + p = loc; + lastloc = &p[maxlen-1]; + while((n = getc(fp)) != '\n') { + if(n == EOF) + return(EOF); + if(!isspace(n) && p < lastloc) + *p++ = n; + } + *p = 0; + return(p - loc); +} + + +stype(p) +register char *p; +{ + if(*p == 0) + return; + if (*(p+1) == 0) { + if (*p == '3') { + cylsize = 200; + stepsize = 5; + return; + } + if (*p == '4') { + cylsize = 418; + stepsize = 9; + return; + } + } + cylsize = atoi(p); + while(*p && *p != ':') + p++; + if(*p) + p++; + stepsize = atoi(p); + if(stepsize <= 0 || stepsize > cylsize || + cylsize <= 0 || cylsize > MAXCYL) { + error("Invalid -s argument, defaults assumed\n"); + cylsize = stepsize = 0; + } +} + + +dostate(s,flg) +{ + register char *p; + register unsigned byte, shift; + BUFAREA *bp; + + byte = (inum)/STATEPB; + shift = LSTATE * ((inum)%STATEPB); + if(statemap != NULL) { + bp = NULL; + p = &statemap[byte]; + } + else if((bp = getblk((BUFAREA *)NULL,(daddr_t)(smapblk+(byte/BSIZE)))) == NULL) + errexit("Fatal I/O error\n"); + else + p = &bp->b_un.b_buf[byte%BSIZE]; + switch(flg) { + case 0: + *p &= ~(SMASK<<(shift)); + *p |= s<<(shift); + if(bp != NULL) + dirty(bp); + return(s); + case 1: + return((*p>>(shift)) & SMASK); + } + return(USTATE); +} + + +domap(blk,flg) +daddr_t blk; +{ + register char *p; + register unsigned n; + register BUFAREA *bp; + off_t byte; + + byte = blk >> BITSHIFT; + n = 1<<((unsigned)(blk & BITMASK)); + if(flg & 04) { + p = freemap; + blk = fmapblk; + } + else { + p = blkmap; + blk = 0; + } + if(p != NULL) { + bp = NULL; + p += (unsigned)byte; + } + else if((bp = getblk((BUFAREA *)NULL,blk+(byte>>BSHIFT))) == NULL) + errexit("Fatal I/O error\n"); + else + p = &bp->b_un.b_buf[(unsigned)(byte&BMASK)]; + switch(flg&03) { + case 0: + *p |= n; + break; + case 1: + n &= *p; + bp = NULL; + break; + case 2: + *p &= ~n; + } + if(bp != NULL) + dirty(bp); + return(n); +} + + +dolncnt(val,flg) +short val; +{ + register short *sp; + register BUFAREA *bp; + + if(lncntp != NULL) { + bp = NULL; + sp = &lncntp[inum]; + } + else if((bp = getblk((BUFAREA *)NULL,(daddr_t)(lncntblk+(inum/SPERB)))) == NULL) + errexit("Fatal I/O error\n"); + else + sp = &bp->b_un.b_lnks[inum%SPERB]; + switch(flg) { + case 0: + *sp = val; + break; + case 1: + bp = NULL; + break; + case 2: + (*sp)--; + } + if(bp != NULL) + dirty(bp); + return(*sp); +} + + +BUFAREA * +getblk(bp,blk) +daddr_t blk; +register BUFAREA *bp; +{ + register struct filecntl *fcp; + + if(bp == NULL) { + bp = search(blk); + fcp = &sfile; + } + else + fcp = &dfile; + if(bp->b_bno == blk) + return(bp); + flush(fcp,bp); + if(bread(fcp,bp->b_un.b_buf,blk,BSIZE) != NO) { + bp->b_bno = blk; + return(bp); + } + bp->b_bno = (daddr_t)-1; + return(NULL); +} + + +flush(fcp,bp) +struct filecntl *fcp; +register BUFAREA *bp; +{ + if(bp->b_dirty) { + bwrite(fcp,bp->b_un.b_buf,bp->b_bno,BSIZE); + } + bp->b_dirty = 0; +} + + +rwerr(s,blk) +char *s; +daddr_t blk; +{ + printf("\nCAN NOT %s: BLK %ld",s,blk); + if(reply("CONTINUE") == NO) + errexit("Program terminated\n"); +} + + +sizechk(dp) +register DINODE *dp; +{ +/* + if (maxblk != howmany(dp->di_size, BSIZE)) + printf("POSSIBLE FILE SIZE ERROR I=%u (%ld,%ld)\n\n",inum, maxblk, howmany(dp->di_size,BSIZE)); +*/ + if(DIR && (dp->di_size % sizeof(DIRECT)) != 0) { + printf("DIRECTORY MISALIGNED I=%u\n\n",inum); + } +} + + +ckfini() +{ + flush(&dfile,&fileblk); + flush(&dfile,&sblk); + flush(&dfile,&inoblk); + close(dfile.rfdes); + close(dfile.wfdes); + close(sfile.rfdes); + close(sfile.wfdes); + if(rmscr) { + unlink(scrfile); + } +} + + +pinode() +{ + register DINODE *dp; + register char *p; + char uidbuf[200]; + char *ctime(); + + printf(" I=%u ",inum); + if((dp = ginode()) == NULL) + return; + printf(" OWNER="); + if(getpw((int)dp->di_uid,uidbuf) == 0) { + for(p = uidbuf; *p != ':'; p++); + *p = 0; + printf("%s ",uidbuf); + } + else { + printf("%d ",dp->di_uid); + } + printf("MODE=%o\n",dp->di_mode); + printf("SIZE=%ld ",dp->di_size); + p = ctime(&dp->di_mtime); + printf("MTIME=%12.12s %4.4s ",p+4,p+20); +} + + +copy(fp,tp,size) +register char *tp, *fp; +MEMSIZE size; +{ + while(size--) + *tp++ = *fp++; +} + + +freechk() +{ + register daddr_t *ap; + + if(freeblk.df_nfree == 0) + return; + do { + if(freeblk.df_nfree <= 0 || freeblk.df_nfree > NICFREE) { + printf("BAD FREEBLK COUNT\n"); + fixfree = 1; + return; + } + ap = &freeblk.df_free[freeblk.df_nfree]; + while(--ap > &freeblk.df_free[0]) { + if(pass5(*ap) == STOP) + return; + } + if(*ap == (daddr_t)0 || pass5(*ap) != KEEPON) + return; + } while(getblk(&fileblk,*ap) != NULL); +} + + +makefree() +{ + register i, cyl, step; + int j; + char flg[MAXCYL]; + short addr[MAXCYL]; + daddr_t blk, baseblk; + + superblk.s_nfree = 0; + superblk.s_flock = 0; + superblk.s_fmod = 0; + superblk.s_tfree = 0; + superblk.s_ninode = 0; + superblk.s_ilock = 0; + superblk.s_ronly = 0; +#ifdef CYLS_IN_SUPERBLK + if(cylsize == 0 || stepsize == 0) { + step = superblk.s_dinfo[0]; + cyl = superblk.s_dinfo[1]; + } + else +#endif + { + step = stepsize; + cyl = cylsize; + + } + if(step > cyl || step <= 0 || cyl <= 0 || cyl > MAXCYL) { + error("Default free list spacing assumed\n"); + step = STEPSIZE; + cyl = CYLSIZE; + } +#ifdef CYLS_IN_SUPERBLK + superblk.s_dinfo[0] = step; + superblk.s_dinfo[1] = cyl; +#endif + clear(flg,sizeof(flg)); + i = 0; + for(j = 0; j < cyl; j++) { + while(flg[i]) + i = (i + 1) % cyl; + addr[j] = i + 1; + flg[i]++; + i = (i + step) % cyl; + } + baseblk = (daddr_t)roundup(fmax,cyl); + clear((char *)&freeblk,BSIZE); + freeblk.df_nfree++; + for( ; baseblk > 0; baseblk -= cyl) + for(i = 0; i < cyl; i++) { + blk = baseblk - addr[i]; + if(!outrange(blk) && !getbmap(blk)) { + superblk.s_tfree++; + if(freeblk.df_nfree >= NICFREE) { + fbdirty(); + fileblk.b_bno = blk; + flush(&dfile,&fileblk); + clear((char *)&freeblk,BSIZE); + } + freeblk.df_free[freeblk.df_nfree] = blk; + freeblk.df_nfree++; + } + } + superblk.s_nfree = freeblk.df_nfree; + for(i = 0; i < NICFREE; i++) + superblk.s_free[i] = freeblk.df_free[i]; + sbdirty(); +} + + +clear(p,cnt) +register char *p; +MEMSIZE cnt; +{ + while(cnt--) + *p++ = 0; +} + + +BUFAREA * +search(blk) +daddr_t blk; +{ + register BUFAREA *pbp, *bp; + + for(bp = (BUFAREA *) &poolhead; bp->b_next; ) { + pbp = bp; + bp = pbp->b_next; + if(bp->b_bno == blk) + break; + } + pbp->b_next = bp->b_next; + bp->b_next = poolhead; + poolhead = bp; + return(bp); +} + + +findino(dirp) +register DIRECT *dirp; +{ + register char *p1, *p2; + + if(dirp->d_ino == 0) + return(KEEPON); + for(p1 = dirp->d_name,p2 = srchname;*p2++ == *p1; p1++) { + if(*p1 == 0 || p1 == &dirp->d_name[DIRSIZ-1]) { + if(dirp->d_ino >= ROOTINO && dirp->d_ino <= imax) + parentdir = dirp->d_ino; + return(STOP); + } + } + return(KEEPON); +} + + +mkentry(dirp) +register DIRECT *dirp; +{ + register ino_t in; + register char *p; + + if(dirp->d_ino) + return(KEEPON); + dirp->d_ino = orphan; + in = orphan; + p = &dirp->d_name[7]; + *--p = 0; + while(p > dirp->d_name) { + *--p = (in % 10) + '0'; + in /= 10; + } + return(ALTERD|STOP); +} + + +chgdd(dirp) +register DIRECT *dirp; +{ + if(dirp->d_name[0] == '.' && dirp->d_name[1] == '.' && + dirp->d_name[2] == 0) { + dirp->d_ino = lfdir; + return(ALTERD|STOP); + } + return(KEEPON); +} + + +linkup() +{ + register DINODE *dp; + register lostdir; + register ino_t pdir; + + if((dp = ginode()) == NULL) + return(NO); + lostdir = DIR; + pdir = parentdir; + printf("UNREF %s ",lostdir ? "DIR" : "FILE"); + pinode(); + if(reply("RECONNECT") == NO) + return(NO); + orphan = inum; + if(lfdir == 0) { + inum = ROOTINO; + if((dp = ginode()) == NULL) { + inum = orphan; + return(NO); + } + pfunc = findino; + srchname = lfname; + filsize = dp->di_size; + parentdir = 0; + ckinode(dp,DATA); + inum = orphan; + if((lfdir = parentdir) == 0) { + printf("SORRY. NO lost+found DIRECTORY\n\n"); + return(NO); + } + } + inum = lfdir; + if((dp = ginode()) == NULL || !DIR || getstate() != FSTATE) { + inum = orphan; + printf("SORRY. NO lost+found DIRECTORY\n\n"); + return(NO); + } + if(dp->di_size & BMASK) { + dp->di_size = roundup(dp->di_size,BSIZE); + inodirty(); + } + filsize = dp->di_size; + inum = orphan; + pfunc = mkentry; + if((ckinode(dp,DATA) & ALTERD) == 0) { + printf("SORRY. NO SPACE IN lost+found DIRECTORY\n\n"); + return(NO); + } + declncnt(); + if(lostdir) { + pfunc = chgdd; + dp = ginode(); + filsize = dp->di_size; + ckinode(dp,DATA); + inum = lfdir; + if((dp = ginode()) != NULL) { + dp->di_nlink++; + inodirty(); + setlncnt(getlncnt()+1); + } + inum = orphan; + printf("DIR I=%u CONNECTED. ",orphan); + printf("PARENT WAS I=%u\n\n",pdir); + } + return(YES); +} + + +bread(fcp,buf,blk,size) +daddr_t blk; +register struct filecntl *fcp; +register size; +char *buf; +{ + if(lseek(fcp->rfdes,blk<rfdes,buf,size) == size) + return(YES); + rwerr("READ",blk); + return(NO); +} + + +bwrite(fcp,buf,blk,size) +daddr_t blk; +register struct filecntl *fcp; +register size; +char *buf; +{ + if(fcp->wfdes < 0) + return(NO); + if(lseek(fcp->wfdes,blk<wfdes,buf,size) == size) { + fcp->mod = 1; + return(YES); + } + rwerr("WRITE",blk); + return(NO); +} + +catch() +{ + ckfini(); + exit(4); +} + +ustat(x, s) +char *s; +{ + return(-1); +} diff --git a/fsck/makefile b/fsck/makefile new file mode 100644 index 0000000..7ad2714 --- /dev/null +++ b/fsck/makefile @@ -0,0 +1,9 @@ +fsck: fsck.c + cc -s -n -O -o fsck fsck.c + +install: fsck + cp fsck /bin + cp fsck.1m /usr/man/man1 + +clean: + rm fsck diff --git a/fsck/mklostfnd.sh b/fsck/mklostfnd.sh new file mode 100644 index 0000000..d948386 --- /dev/null +++ b/fsck/mklostfnd.sh @@ -0,0 +1,21 @@ +: Make lost+found directory on named filesystem +SLOTS=20 + +for mntpt in "$@"; do + if [ ! -d $mntpt ]; then + echo "$mntpt is not a directory" + exit 1 + fi + if [ -d $mntpt/lost+found ]; then + echo "$mntpt/lost+found exists" + exit 1 + fi + ( cd $mntpt/lost+found + cnt=1 + while [ $cnt -le $SLOTS ]; do + touch fi$cnt + cnt=`expr $cnt + 1` + done + rm fi* + ) +done diff --git a/fsck/tests/MT b/fsck/tests/MT new file mode 100755 index 0000000..5403fab --- /dev/null +++ b/fsck/tests/MT @@ -0,0 +1 @@ +/etc/mount /dev/rk0 /mnt diff --git a/fsck/tests/README.md b/fsck/tests/README.md new file mode 100644 index 0000000..800d362 --- /dev/null +++ b/fsck/tests/README.md @@ -0,0 +1,73 @@ +# Testing fsck + +This directory contains testing programs that will induce problems in a file system. + +## How to compile + +* ```cd lib``` +* ```make``` - will create a library called _testlib_ in the current directory. +* ```cd ..``` +* ```make``` will generate three binaries. + +## How to use + +Running these tests on a live file system is a bad idea. I've added a new RK05 to my system to provide a test platform. See [../../v7conf/4-testrk](../../v7conf/4-testrk) for how to do this. Once the device is added, make a file system on the drive and copy some files in to give the file system some substance. + +The tests here are intended to be done on a 'disk' whose file system is sacrificial. + +## Tests + +To avoid finger trouble, the file _conf.ini_ holds the name of the disk, the directory used to mount it, and the name of a safety file that must be present for the disk to be modified. Doing this avoids using the command line which is subject to finger trouble. + +Use the commands below one at a time and fix the filesystem using _fsck_ after the damage is caused. + +### confpr +Prints out the current settings in conf.ini. + +### superb +Will print superblock contents and can cause a duplicate block to be added to the free block list in the superblock on the disk. Fix the filesystem when you break it. + +``` C +Usage: superb [-z][-v] [device] +-z - corrupt superblock freelist - uses conf.ini for settings +-v - verbose, default is to just print error messages + +device - print superblock info for the device, -v not needed +superb with no arguments, prints out the superblock on the current +testing filesystem, and puts the verbose flag on. + +The -z flag is needed to corrupts the filesystem. +``` + +### badino +Introduces various errors into the filesystem. They are intended to be used and then the filesystem repaired before using another action. + +The general format: + +``` C +badino [-l][-z][-v] ID + +ID is an action number in the range 0-5 +Other config information in conf.ini +-z is needed to create the bad file or directory +-v verbose, print all text not just error messages +-l list action numbers and their actions + +The -z flag is needed to break the file system. The code will +create a directory called baddir, a file called zero, and a file +called fiveblks - depending on what it wants to do. + +badino -l prints + +0: Clear link in empty file +1: Clear link in file with contents +2: Clear link in empty directory +3: Clear link in a directory with content +4: Create incorrect link count in a directory +5: Corrupt directory entry, set entry in directory to an illegal value + +``` + +## Library + +I put a lot of useful code into the [library](lib). diff --git a/fsck/tests/UT b/fsck/tests/UT new file mode 100755 index 0000000..b4c7421 --- /dev/null +++ b/fsck/tests/UT @@ -0,0 +1 @@ +/etc/umount /dev/rk0 diff --git a/fsck/tests/badino.c b/fsck/tests/badino.c new file mode 100644 index 0000000..9f15430 --- /dev/null +++ b/fsck/tests/badino.c @@ -0,0 +1,652 @@ +/* + * Create bad inodes on the disk + * + * Strategy: + * Create a new file to 'corrupt' + * need to be given a block device that's + * not mounted. This will be mounted on /mnt + * the necessary files created, and the + * then the inodes for the files will be + * messed with. fsck can then be run to + * patch the file system. + * + * Usage: badino [-l][-z][-v] ID + * ID is an action number to execute + * Other config information in conf.ini + * -z is needed to create the bad file or directory + * -v verbose, print all text not just error messages + * -l list action numbers and their actions + * + * Peter Collinson + * June 2023 + */ + +#include +#include +#include +#include +#include +#include +#include +#include "lib/lib.h" + + +#define zerofile "zero" /* zero length file */ +#define fiveblk "fiveblk" /* file with five blocks */ +#define baddir "baddir" /* directory */ + +#define DOUMNT 1 +#define NOUMNT 0 + +int zflag; /* create bad file */ + +/* actions */ +int clearlink(), clearadd(), badct(), badent(); + +/* descriptions */ +#define ad0 "Clear link in empty file" +#define ad1 "Clear link in file with contents" +#define ad2 "Clear link in empty directory" +#define ad3 "Clear link in a directory with content" +#define ad4 "Create incorrect link count in a directory" +#define ad5 "Corrupt directory entry, set entry in directory to an illegal value" + +struct actn { + char *fname; /* file name to make */ + int isdir; /* if 1, make a directory */ + int blks; /* if isdir == 0, then use this file size in blocks */ + int (*funcn)(); /* function to call to make things happen */ + char *desc; /* description */ + + +} acttab[] { + zerofile, 0, 0, &clearlink, ad0, /* action 0 - clear link in empty file */ + fiveblk, 0, 5, &clearlink, ad1, /* action 1 - clear link in file with contents */ + baddir, 1, 0, &clearlink, ad2, /* action 2 - clear link in directory */ + baddir, 1, 0, &clearadd, ad3, /* action 3 - clear link in a directory with content */ + baddir, 1, 0, &badct, ad4, /* action 4 - Create incorrect link count in a directory */ + baddir, 1, 0, &badent, ad5, /* action 5 - Corrupt directory entry, set entry in + * directory to an illegal value */ + 0, 0, 0, 0 +}; + +/* convert inodes to block numbers, and offset */ +/* current is the inode decode in play */ +struct ino2blk { + daddr_t bno; + int offset; +} current; + +/* Expanded disk addresses from a disk inode + * created by addrexp + */ +daddr_t iaddr[NADDR]; + +/* buffer for reading inode blocks */ +char blkbuf[BLKSIZ]; + +main(argc, argv) +int argc; +char **argv; +{ + int actionid; + int acnt; + + if (suser() == 0) { + printf("Run as root\n"); + exit(1); + } + + acnt = ctactions(); + + actionid = -1; + + if (argc == 1) { + usage("No arguments"); + } + + while (--argc > 0 && *argv[1] == '-') { + argv++; + while (*++*argv) switch (**argv) { + case 'l': + listactions(); + exit(0); + case 'z': + zflag = 1; + continue; + case 'v': + verbose = 1; + continue; + case 'q': + /* assist debugging */ + abort(); + continue; + default: + usage(); + } + } + + if (argc == 0) { + usage("Please supply an action id"); + } else { + argv++; + actionid = atoi(*argv); + if (actionid < 0 || actionid >= acnt) + usage("Action id out of range"); + } + if (actionid < 0) + usage("Please supply an action id"); + + /* + * safety checks and get info from conf.ini + */ + config(zflag); + + (*acttab[actionid].funcn)(&acttab[actionid]); +} + +/* count the number of actions we have */ +ctactions() +{ + register struct actn *ap; + register int ct; + + ct = 0; + for (ap = acttab; ap->fname; ap++) + ct++; + return(ct); +} + +/* List actions */ +listactions() +{ + register struct actn *ap; + register int ct; + + for (ap = acttab; ap->fname; ap++) { + printf("%2d: %s\n", ct, ap->desc); + ct++; + } +} + +usage(msg) +char *msg; +{ + int acnt; + + acnt = ctactions(); + printf("*** %s\n", msg); + printf("Usage: badinode [-l][-z][-v] ID\n"); + printf("ID is an action number in the range 0-%d\n", acnt-1); + printf("Other config information in conf.ini\n"); + printf("-z is needed to create the bad file or directory\n"); + printf("-v verbose, print all text not just error messages\n"); + printf("-l list action numbers and their actions\n"); + exit(1); +} + +/* + * start of actions + */ + +/* + * create a file, or directory + * then set link count to 0 on the disk + */ +clearlink(ap) +struct actn *ap; +{ + register struct dinode *ip; + ino_t ino; + + ino = inoset(ap, DOUMNT); + + ip = geti(ino); + if (verbose) + inopr(ino, ip); + /* are we zapping */ + if (zflag) { + /* doing the deed */ + ip->di_nlink = 0; + puti(); + printf("Inode %d - %s now corrupt, run fsck or icheck -s to repair\n", ino, ap->fname); + } +} + +/* + * create a directory, install a single file + * then set link count to 0 on the disk + */ +clearadd(ap) +struct actn *ap; +{ + register struct dinode *ip; + ino_t ino; + + ino = inoset(ap, NOUMNT); + + /* Now add content */ + addfile(mountpt, ap->fname, "fiveblk", 5); + umntdev(); + + ip = geti(ino); + if (verbose) + inopr(ino, ip); + /* are we zapping */ + if (zflag) { + /* doing the deed */ + ip->di_nlink = 0; + puti(); + printf("Inode %d - %s now corrupt, run fsck or icheck -s to repair\n", ino, ap->fname); + } +} + +/* + * create a directory, install a single file + * then increase the link count by 5 + */ +badct(ap) +struct actn *ap; +{ + register struct dinode *ip; + int ino; + + ino = inoset(ap, NOUMNT); + /* Now add content */ + addfile(mountpt, ap->fname, "fiveblk", 5); + umntdev(); + + ip = geti(ino); + if (verbose) + inopr(ino, ip); + /* are we zapping */ + if (zflag) { + /* doing the deed */ + ip->di_nlink = ip->di_nlink + 3; + puti(); + printf("Inode %d - %s now corrupt, run fsck or icheck -s to repair\n", ino, ap->fname); + } +} + +/* + * create a directory, insert two files + * set inode value in an entry to more than + * max inodes on the disk + */ +badent(ap) +struct actn *ap; +{ + ino_t ino; + int imax; + daddr_t dblk; + int fd; + off_t fsize; + struct direct *dp; + struct dinode *ip; + + /* need to evalate max inodes on the system */ + imax = maxino(); + + ino = inoset(ap, NOUMNT); + + /* Add files */ + addfile(mountpt, ap->fname, "fiveblk", 5); + addfile(mountpt, ap->fname, "zero", 0); + umntdev(); + + ip = geti(ino); + if (verbose) + inopr(ino, ip); + + /* get directory block from extended list*/ + dblk = iaddr[0]; + fsize = ip->di_size; + + /* about to zap the inode info */ + if ((fd = open(device, 2)) < 0) { + perror(efmt("Open R/W access", device)); + exit(1); + } + bread(fd, blkbuf, dblk); + + dp = findent(blkbuf, "fiveblk", fsize); + if (dp == 0) { + printf("Cannot find fiveblk in the directory\n"); + close(fd); + return; + } + printf("Found Inode: %d, filename: fiveblk\n", dp->d_ino); + if (zflag) { + dp->d_ino = imax + 30; + printf("Directory entry is now corrupt, run fsck or icheck -s to repair\n", ino, ap->fname); + printf("NB - fsck should also reconnect the file in lost+found\n"); + bwrite(fd, blkbuf, dblk); + } + close(fd); +} + +/* + * actions have lots of repeated code + * put them into functions here + */ + +/* + * inoset the initial file from action + * return ino of created file + * DOUMNT and NOUMNT defines are used + * to make the code more readable + */ +inoset(ap, doumount) +struct actn *ap; +int doumount; +{ + int ino; + + if (dev_is_mounted == 0) + mntdev(); + ino = crfile(ap); + if (doumount == DOUMNT) + umntdev(); + return(ino); +} + +/* add a file under a directory */ +addfile(base, dirname, fname, blks) +char *base; +char *dirname; +char *fname; +int blks; +{ + char contents[64]; + + makepath(contents, base, dirname); + addpath(contents, fname); + fileblk(contents, blks); +} + +/* + * get max number of inodes + */ +maxino() +{ + struct filsys *sup; + int imax; + int fd; + + sync(); + if ((fd = open(device, 0)) < 0) { + perror(efmt("Open", device)); + exit(1); + } + bread(fd, blkbuf, SUPERB); + close(fd); + sup = blkbuf; + imax = sizeof(struct dinode) * sup->s_isize; + return(imax); +} + +/* + * find a filename in a directory block + * return pointer to directory entry + */ +findent(bbuf, lookfor, fsize) +char *bbuf; +char *lookfor; +daddr_t fsize; +{ + register struct direct *dp; + register int i; + char fpadded[16]; + + dp = bbuf; + if (fsize > 512) + fsize = 512; + /* ignore . and .. */ + dp = &dp[2]; + for (fsize = fsize - 2*sizeof(struct direct); + fsize > 0; + fsize = fsize - sizeof(struct direct)) { + for (i = 0; i < DIRSIZ; i++) { + fpadded[i] = dp->d_name[i]; + } + fpadded[DIRSIZ] = '\0'; + if (dp->d_ino != 0 && streq(fpadded, lookfor)) { + return(dp); + } + dp++; + } + return(0); +} + +/* + * create test files + * return inode of created object + */ +crfile(ap) +struct actn *ap; +{ + struct stat stino; + char destfile[64]; + int mkstat; + + makepath(destfile, mountpt, ap->fname); + + if (stat(destfile, &stino) >= 0) { + return(stino.st_ino); + } + + if (ap->isdir) { + if ((mkstat = mkdir(destfile)) != 0) { + printf("Mkdir of %s failed, status %d\n", destfile, mkstat); + exit(1); + } + if (verbose) + printf("Created dir %s in %s\n", destfile, device); + + } else { + fileblk(destfile, ap->blks); + if (verbose) + printf("Created file %s in %s\n", destfile, device); + } + if (stat(destfile, &stino) < 0) { + perror(efmt("Stat", destfile)); + exit(1); + } + printf("%s, inode %d\n", destfile, stino.st_ino); + return(stino.st_ino); +} + +/* + * create a file with random contents + * of the number of blocks in bcnt + */ +fileblk(fname, bcnt) +char *fname; +int bcnt; +{ + register int fd; + register int i; + struct stat stino; + char buf[512]; + + /* don't make files that exist */ + if (stat(fname, &stino) == 0) + return; + + fd = creat(fname, 0644); + if (fd < 0) { + perror(efmt("Create", fname)); + exit(1); + } + for (i = 0; i < bcnt; i++) { + randblk(&buf); + if (write(fd, buf, 512) < 0) { + perror(efmt("Write", fname)); + exit(1); + } + } + close(fd); +} + +/* + * Generate 512 random characters + * in 8 lines of 63 characters and a newline + */ +randblk(dp) +char *dp; +{ + register *cp; + register int i; + int l; + + seedrand(); + + for (l = 0; l < 8; l++) { + for (i = 0; i < 16; i++) { + cp = randchars(); + *dp++ = *cp++; + *dp++ = *cp++; + *dp++ = *cp++; + if (i == 15) + *dp++ = '\n'; + else + *dp++ = *cp++; + } + } +} + +/* random list of 63 characters */ +char srclist[] "Ob1j4ioaGmIueswzvT9JhgNp62Z0CHYd3BqWXk Ql7U8DyK5LrVARfPnEtMxcSF"; + +/* + * use rand to generate random chars from the list + * rand will give us a number max of 077777 + * generate 4 characters from this number + * by masking with 077000 and shifting by 9 + */ +randchars() +{ + static char quad[4]; + register char *qp; + register int mask; + register int ashift; + int randv; + + randv = rand(); + mask = 077000; + qp = quad; + for (ashift = 9; ashift >= 0; ashift =- 3) { + if (ashift) { + *qp++ = srclist[((randv & mask) >> ashift)]; + } else { + *qp++ = srclist[(randv & mask)]; + } + mask = mask >> 3; + } + return quad; +} + +/* + * seed rand + */ +seedrand() +{ + int tvec[2]; + + time(tvec); + srand(tvec[1]); +} + +/* + * Code dealing with inodes on the disk + */ + +/* Convert an inode number to a block address */ +i2b(ino) +ino_t ino; +{ + register ino_t ino15; + + ino15 = ino + 15; + current.bno = (daddr_t)(ino15 >> 3); + current.offset = ino15&07; +} + +/* Given an inode return a pointer to its contents */ +geti(ino) +ino_t ino; +{ + register struct dinode *ip; + int fd; + + sync(); + /* open the device */ + if ((fd = open(device, 0)) < 0) { + printf("Cannot open: %s\n", device); + exit(1); + } + i2b(ino); + bread(fd, &blkbuf, current.bno); + close(fd); + + ip = blkbuf; + ip += current.offset; + /* expand addresses */ + addrexp(ip); + return (ip); +} + +addrexp(ip) +struct dinode *ip; +{ + register char *p1; + register char *p2; + int i; + + p1 = (char *)iaddr; + p2 = (char *)ip->di_addr; + for(i=0; idi_mode); + printf("Nlink %d\n", ip->di_nlink); + printf("Uid %d\n", ip->di_uid); + printf("Gid %d\n", ip->di_uid); + printf("Size %D\n", ip->di_size); + printf("Addr "); + printf("Addr "); + for (i = 0; i < NADDR; i++) + printf("%D ", iaddr[i]); + printf("\n"); + printf("Atime %s", ctime(&ip->di_atime)); + printf("Mtime %s", ctime(&ip->di_mtime)); + printf("Ctime %s", ctime(&ip->di_ctime)); +} diff --git a/fsck/tests/conf.ini b/fsck/tests/conf.ini new file mode 100644 index 0000000..539042e --- /dev/null +++ b/fsck/tests/conf.ini @@ -0,0 +1,18 @@ +# Config file for fsck tests +# here to prevent finger trouble +# format is +# key: Value + +# Device we are using for tests + +# include /dev +device: /dev/rk0 + +# Where we are mounting it +mount: /mnt + +# name of safety file +# a file called this must exist +# on the root of the nominated device +# This has to be created by hand +safety: /mnt/allowtests diff --git a/fsck/tests/confpr.c b/fsck/tests/confpr.c new file mode 100644 index 0000000..6bb5808 --- /dev/null +++ b/fsck/tests/confpr.c @@ -0,0 +1,20 @@ +# +/* + * Print test config file + * + * really a test routine + */ + +#include "lib/lib.h" + +main() +{ + + if (rdconfig("conf.ini") == 0) { + printf("%s %s\n", "device", device); + printf("%s %s\n", "mount", mountpt); + printf("%s %s\n", "safety", safety); + } + + exit(0); +} diff --git a/fsck/tests/makefile b/fsck/tests/makefile new file mode 100644 index 0000000..ef53644 --- /dev/null +++ b/fsck/tests/makefile @@ -0,0 +1,10 @@ +all: superb confpr badino + +superb: superb.c testlib + cc -o superb -O superb.c testlib + +confpr: confpr.c testlib + cc -o confpr -O confpr.c testlib + +badino: badino.c testlib + cc -o badino -O badino.c testlib diff --git a/fsck/tests/superb.c b/fsck/tests/superb.c new file mode 100644 index 0000000..66d232f --- /dev/null +++ b/fsck/tests/superb.c @@ -0,0 +1,196 @@ +# +/* + * Superblock reader + * Using conf.ini to establish test device + * avoiding 'finger trouble' + * and ensure that it's configured to safelt break a file system + * See conf.ini + * + * Can be used with a device argument to print + * superblock contents. + * + * -z - corrupt superblock freelist - uses conf.ini for settings + * -v - verbose, default is to just print error messages + * device - print superblock info for the device, -v not needed + + * Peter Collinson + * June 2023 + */ + +#include "lib/lib.h" +#include +#include +#include + +char dbuf[BLKSIZ]; + +/* zap flag set */ +int zflag; +/* use conf.ini to get info */ +int useconf; + +main(argc, argv) +int argc; +char **argv; +{ + register char *p; + + if (suser() == 0) { + printf("Run as root\n"); + exit(1); + } + + if (argc == 1) + verbose = 1; + while (--argc > 0 && *argv[1] == '-') { + argv++; + while (*++*argv) switch (**argv) { + case 'z': + zflag = 1; + useconf = 1; + continue; + case 'v': + verbose = 1; + continue; + default: + usage(); + } + } + if (argc == 0) + useconf = 1; + else { + argv++; + if (zflag) + usage(); + verbose = 1; + strcpy(device, *argv); + } + if (useconf) + config(zflag); + if (zflag && dev_is_mounted) + umntdev(); + process(); + exit(0); +} + +/* + * do the work + */ +process() +{ + int fd; + + if ((fd = open(device, 0)) < 0) { + printf("Cannot open: %s\n", device); + exit(1); + } + sync(); + bread(fd, &dbuf, SUPERB); + close(fd); + printsuper(); + if (zflag) { + if ((fd = open(device, 2)) >= 0) { + makebad(fd); + close(fd); + sync(); + } else { + printf("Cannot open %s for read/write\n", device); + } + } +} + +usage() +{ + printf("Usage: superb [-z][-v] [device]\n"); + printf("-z - corrupt superblock freelist - uses conf.ini for settings\n"); + printf("-v - verbose, default is to just print error messages\n"); + printf("device - print superblock info for the device, -v not needed\n"); + exit(1); +} + +printsuper() +{ + register struct filsys *sup; + + if (verbose == 0) return; + sup = dbuf; + printf("%s:\n", device); + printf("%6d size in blocks of I list\n", sup->s_isize); + printf("%6D size in blocks of entire volume\n", sup->s_fsize); + printf("%6d number free blocks (0-%d)\n", sup->s_nfree, NICFREE); + printf("%6D block address of next %d free blocks\n", sup->s_free[0], NICFREE); + if (sup->s_nfree != 0) + blkprint(sup->s_free, sup->s_nfree); + printf("%6d number of free I nodes (0-100)\n", sup->s_ninode); + if (sup->s_ninode != 0) + iprint(sup->s_inode, sup->s_ninode); + printf("Last change: %s", ctime(&sup->s_time)); +} + +makebad(fd) +int fd; +{ + register struct filsys *sup; + + printf("Changing free list information in super block\n"); + sup = dbuf; + if (sup->s_free[0] <= 1) { + printf("Requires more than one free block\n"); + return; + } + printf("Duplicating first entry in block free list in second entry\n"); + /* duplicate block */ + sup->s_free[2] = sup->s_free[1]; + bwrite(fd, &dbuf, SUPERB); + printf("Superblock is now corrupt, run fsck or icheck -s to repair\n"); +} + +blkprint(sp, free) +daddr_t *sp; +int free; +{ + register int i; + register daddr_t *src; + int linect; + + src = sp; + linect = 0; + for (i = 1; i < free; i++) { + /* ignore the first entry */ + if (*(++src)) { + printf("%7D", *src); + if (++linect == 10) { + printf("\n"); + linect = 0; + } + } + } + if (linect != 10) { + printf("\n"); + } +} + +iprint(sp, free) +ino_t *sp; +int free; +{ + register int i; + register ino_t *src; + int linect; + + src = sp; + linect = 0; + for (i = 1; i < free; i++) { + /* ignore the first entry */ + if (*(++src)) { + printf("%6d", *src); + if (++linect == 10) { + printf("\n"); + linect = 0; + } + } + } + if (linect != 10) { + printf("\n"); + } +} diff --git a/halt/README.md b/halt/README.md new file mode 100644 index 0000000..a7f1247 --- /dev/null +++ b/halt/README.md @@ -0,0 +1,33 @@ +# Halt the running UNIX system safely + +This code provides a small program and a system call to halt the UNIX V7 processor safely. + +Back in 1976, I came to the conclusion that the UNIX filesystem tended to get corrupted when the system was closed down. + +I implemented program called _killunix_, that would: + +1. Find and kill all the running processes. +2. Then make a system call that would sync the disks and then call a piece of assembler in the kernel that halted the processor. + +Closure was clean, and filesystem problems became rare. I now find on a simulated PDP11, I've been taking the system up and down very frequently sometimes causing disk problems, so implementing this system was an early step. + +I've recoded the _killunix_ program as _halt_ for simpler use on SIMH. I've called it _halt_ so if it's typed on the host system by accident, it does nothing. + +To install this system you need to: + +1. Run _installsys_, this: + a. Installs _mch.s_ into _/usr/sys/conf/mch.s_. The version of the _mch.s_ has been edited to include the code in _ins_mch.s_. The original _mch.s_ is copied to a new the _config/orig_ directory. + + b. Replaces _/usr/sys/sys/sys4.c_ with _sys4.c_. A new function from _ins_sys4.c_ is appended at the end of the original file. + + c. Replaces /usr/sys/sys/sysent.c with sysent.c. This nominates an unused system call id, 62 to link to the stopunix() function in _sys4.c_. If you change the number, alter _stopunix.s_. + +2. Now recompile and install your kernel. The scripts I use to do this can be found on _[../v7conf](../v7conf)_. + +3. Compile the halt command using _make_ will generate the command. Install it in /bin and halt will now take your machine down relatively safely. + +## The _halt_ command + +When the command is run, it lists the process ids of the processes that it kills. You may get the output from any shell you are running as it gets nuked. + +The _halt_ command gets addresses from _/unix_ and reads data from the running kernel. If you've update the _/unix_ file, then processes will not be killed because the addresses it needs are not available but the processor will still halt. diff --git a/halt/halt.c b/halt/halt.c new file mode 100644 index 0000000..ae2f84b --- /dev/null +++ b/halt/halt.c @@ -0,0 +1,204 @@ +/* + * halt + * + * Kill all running processes + * Kill all processes then call stopunix system call + * which stops the processor + * + * Peter Collinson + * July 2023 + */ + +#include +#include +#include + +char *namelist "/unix"; +char *corefile "/dev/kmem"; + +struct nlist nl[2]; + +struct proc proc[NPROC]; +int proclist[NPROC]; + +unsigned getproc(); +int *getps(); + +/* debug - set to non-zero for no killing or stopping */ +#define DEBUG 0 + +main() +{ + register int *pr; + int i, mypid, stopret; + unsigned procaddr; + + if (!suser()) { + printf("Use su to run this\n"); + exit(0); + } + + /* get my pid */ + mypid = getpid(); + + /* + * get address of _proc + * will be zero on error + * getps tests for this + */ + procaddr = getproc(); + + /* + * early sync - sync returns if + * a sync is happening so + * delays make sense + */ + sync(); + sleep(1); + + /* get the details from proc */ + pr = getps(procaddr, mypid); + + /* pr is 0 on error, or no processes */ + if (pr) { + printf("Kill running processes:\n"); + while (*pr) { + printf("%d ", *pr); + if (DEBUG == 0) + kill(*pr, SIGKIL); + pr++; + } + printf("\n"); + /* + * Allow processes to die + * because init is killed + * they will be in Zombie state + */ + sleep(3); + } + if (DEBUG == 0) { + /* now shutdown the machine */ + sync(); + /* allow sync to settle */ + sleep(2); + printf("Halting system\n"); + /* will return if update is busy */ + i = 0; + while (stopunix() < 0) { + printf("Disks busy\n"); + sleep(2); + if (++i > 15) { + printf("Halt abandoned - please stop by hand\n"); + break; + } + } + } + exit(0); +} + +/* + * Are we superuser or effective gid is superuser + * return 1 if yes, 0 if no + */ +suser() +{ + + return (getuid() == 0 + || geteuid() == 0); +} + +/* + * Access the kernel's proc table + * get active pids, ignore pid == 0 and mypid + * return a zero terminated list or + * zero if some failure or no processes to kill + */ +int * +getps(procaddr, mypid) +unsigned procaddr; +int mypid; +{ + register struct proc *pp; + register int *lp; + + if (procaddr == 0 + || readtable(procaddr) == 0) + return(0); + + /* + * clear proclist so we can reuse this code + * if wanted + */ + for (lp = proclist; lp < &proclist[NPROC]; *lp++ = 0); + + /* + * We have data - but it might not be valid + * because /unix may not be the running kernel + * check all the p_stat values should be between 0 and SSTOP + */ + for (pp = &proc; pp < &proc[NPROC]; pp++) { + if (pp->p_stat < 0 + || pp->p_stat > SSTOP) + return(0); + } + + /* + * find active processes, ignore zombies + */ + lp = proclist; + for (pp = &proc; pp < &proc[NPROC]; pp++) { + if (pp->p_stat + && pp->p_stat != SZOMB + && pp->p_pid != 0 + && pp->p_pid != mypid) + *lp++ = pp->p_pid; + } + if (lp == proclist) { + return(0); + } + return(proclist); +} + +/* Get address of _proc in the current running kernel */ +unsigned +getproc() +{ + copy7("_proc", nl[0].n_name); + nlist(namelist, nl); + if (nl[0].n_type == -1) { + printf("Cannot get names from /unix\n"); + return(0); + } + return(nl[0].n_value); +} + +/* read the table into memory */ +readtable(procaddr) +unsigned procaddr; +{ + register int corefd; + long pa; + + if ((corefd = open(corefile, 0)) < 0) { + printf("Cannot open %s\n", corefile); + return(0); + } + pa = procaddr; + lseek(corefd, pa, 0); + if (read(corefd, &proc, sizeof(proc)) != sizeof(proc)) { + printf("Cannot read area for procs\n"); + close(corefd); + return(0); + } + close(corefd); + return(1); +} + +/* copy names - limit space to 7 characters */ +copy7(s, d) +register char *s, *d; +{ register i; + + for(i = 0; i < 7; i++) + if(!(*d++ = *s++)) return; +} diff --git a/halt/ins_mch.s b/halt/ins_mch.s new file mode 100644 index 0000000..5e92c38 --- /dev/null +++ b/halt/ins_mch.s @@ -0,0 +1,17 @@ +/ Source of assembler to be inserted into mch.s +/ This assembler code halts the machine. +/ Inserted after the start code +/ and before the various bootstraps +/ The code that stops the system is in stopunix() +/ should be added at the end of ken/sys4.c +/ + +halt = 0 // Halt instruction not in assembler + +.text + +.globl _stopit +_stopit: + mov $0340,*$6 + reset + halt diff --git a/halt/ins_sys4.c b/halt/ins_sys4.c new file mode 100644 index 0000000..d402fa3 --- /dev/null +++ b/halt/ins_sys4.c @@ -0,0 +1,17 @@ +/* + * Added as a system call to cleanly stop the system + * Originally Peter C Nov 1976 + * revised 2023 + */ +stopunix() +{ + if(suser()) { + /* if still updating then return to caller */ + if (updlock) + u.u_error = EBUSY; + else { + update(); + stopit(); + } + } +} diff --git a/halt/installsys b/halt/installsys new file mode 100755 index 0000000..94ae7bf --- /dev/null +++ b/halt/installsys @@ -0,0 +1,34 @@ +#!/bin/sh +# install new files into the system +DEST=/usr/sys +DESTCONF=$DEST/conf +DESTSYS=$DEST/sys + +# make backup files +if test ! -d $DESTCONF/orig ; then + echo Making $DESTCONF/orig + mkdir $DESTCONF/orig +fi +if test ! -f $DESTCONF/orig/mch.s ; then + echo Backup of mch.s in + cp $DESTCONF/mch.s $DESTCONF/orig/mch.s +fi +echo Copy mch.s to $DESTCONF +cp mch.s $DESTCONF + +if test ! -d $DESTSYS/orig ; then + echo Making $DESTSYS/orig + mkdir $DESTSYS/orig +fi +for sysfile in sysent.c sys4.c; do + if test ! -f $DESTSYS/orig/$sysfile ; then + echo Backup of $sysfile in $DESTSYS + cp $DESTSYS/$sysfile $DESTSYS/orig/ + fi + echo cp $sysfile $DESTSYS +done +echo 'Now rebuild the system' +echo 'You will need to do' +echo 'make all' +echo 'then' +echo 'make unix' diff --git a/halt/makefile b/halt/makefile new file mode 100644 index 0000000..d9821be --- /dev/null +++ b/halt/makefile @@ -0,0 +1,10 @@ +# Make halt +halt: halt.c stopunix.o + cc -O -o halt halt.c stopunix.o + +stopunix.o: stopunix.s + as -o stopunix.o stopunix.s + +install: halt + mv halt /bin/halt + rm *.o diff --git a/halt/mch.s b/halt/mch.s new file mode 100644 index 0000000..f28c4f3 --- /dev/null +++ b/halt/mch.s @@ -0,0 +1,1076 @@ +/ machine language assist +/ for 11/45 or 11/70 CPUs + + +/ non-UNIX instructions +mfpi = 6500^tst +stst = 170300^tst +mtpi = 6600^tst +mfpd = 106500^tst +mtpd = 106600^tst +spl = 230 +ldfps = 170100^tst +stfps = 170200^tst +wait = 1 +rtt = 6 +reset = 5 + +.PROFIL = 0 +HIPRI = 340 +HIGH = 7 + .if .PROFIL +HIGH = 6 +HIPRI = 300 + .endif + +/ Mag tape dump +/ save registers in low core and +/ write all core onto mag tape. +/ entry is thru 44 abs + +.data +.globl dump +dump: + +/ save regs r0,r1,r2,r3,r4,r5,r6,KIA6 +/ starting at abs location 4 + + mov r0,4 + mov $6,r0 + mov r1,(r0)+ + mov r2,(r0)+ + mov r3,(r0)+ + mov r4,(r0)+ + mov r5,(r0)+ + mov sp,(r0)+ + mov KDSA6,(r0)+ + +/ dump all of core (ie to first mt error) +/ onto mag tape. (9 track or 7 track 'binary') + +.if HTDUMP + mov $HTCS1,r0 + mov $40,*$HTCS2 + mov $2300,*$HTTC + clr *$HTBA + mov $1,(r0) +1: + mov $-512.,*$HTFC + mov $-256.,*$HTWC + movb $61,(r0) +2: + tstb (r0) + bge 2b + bit $1,(r0) + bne 2b + bit $40000,(r0) + beq 1b + mov $27,(r0) +.endif +HT = 0172440 +HTCS1 = HT+0 +HTWC = HT+2 +HTBA = HT+4 +HTFC = HT+6 +HTCS2 = HT+10 +HTTC = HT+32 + +MTC = 172522 +.if TUDUMP + mov $MTC,r0 + mov $60004,(r0)+ + clr 2(r0) +1: + mov $-512.,(r0) + inc -(r0) +2: + tstb (r0) + bge 2b + tst (r0)+ + bge 1b + reset + +/ end of file and loop + + mov $60007,-(r0) +.endif + br . + +.text +.globl start, _end, _edata, _etext, _main + + +/ 11/45 and 11/70 startup. +/ entry is thru 0 abs. +/ since core is shuffled, +/ this code can be executed but once + +start: + bit $1,SSR0 + beq 1f + mov $trap,34 + mov $trap,0 + mov $340+15.,2 + bit $20,SSR3 + beq 9f + mov $70.,_cputype + mov $3,*$MSCR +9: + clr PS + br 9f +1: + inc $-1 + bne . + reset +/ Set loc. 0 to trap to system, in case of +/ hardware glitch + mov $trap,0 / in case of bad trap through 0 + mov $340+15.,2 / high pri, trap type 15 + clr PS + +/ set KI0 to physical 0 + + mov $77406,r3 + mov $KISA0,r0 + mov $KISD0,r1 + clr (r0)+ + mov r3,(r1)+ + +/ set KI1-6 to eventual text resting place + + mov $_end+63.,r2 + ash $-6,r2 + bic $!1777,r2 +1: + mov r2,(r0)+ + mov r3,(r1)+ + add $200,r2 + cmp r0,$KISA7 + blos 1b + +/ set KI7 to IO seg for escape + + mov $IO,-(r0) + +/ set KD0-7 to physical + + mov $KDSA0,r0 + mov $KDSD0,r1 + clr r2 +1: + mov r2,(r0)+ + mov r3,(r1)+ + add $200,r2 + cmp r0,$KDSA7 + blos 1b + +/ initialization +/ get a temp (1-word) stack +/ turn on segmentation +/ copy text to I space +/ clear bss in D space + + mov $stk+2,sp + mov $65,SSR3 / 22-bit, map, K+U sep + bit $20,SSR3 + beq 1f + mov $70.,_cputype + mov $3,*$MSCR / Disable UNIBUS traps, non-fatal traps +1: + inc SSR0 + mov $_etext+100,r2 + mov $_edata+100,r1 + add $_etext-8192.,r1 +1: + mov -(r1),-(sp) + mtpi -(r2) + cmp r1,$_edata + bhi 1b +1: + clr (r1)+ + cmp r1,$_end + blo 1b + +/ use KI escape to set KD7 to IO seg +/ set KD6 to first available core +/ If profiling, snag supervisor registers. + + mov $IO,-(sp) + mtpi *$KDSA7 +9: + mov $_etext-8192.+63.,r2 + ash $-6,r2 + bic $!1777,r2 + add KISA1,r2 + .if .PROFIL + mov r2,SISA2 + mov r2,_proloc + mov $77406,SISD2 + add $200,r2 + mov r2,SISA2+2 + mov $77406,SISD2+2 + add $200,r2 + .endif + mov r2,KDSA6 + +/ Turn off write permission on kernel text +/ Take stuff above data out of address space + + mov $KISD0,r0 +1: + mov $77402,(r0)+ + cmp r0,$KISD7 + blos 1b + + mov $_end+63.,r0 + ash $-6,r0 + bic $!1777,r0 + mov $KDSD0,r1 +1: + cmp r0,$200 + bge 2f + dec r0 + bge 4f + clr (r1) + br 3f +4: + movb r0,1(r1) + br 3f +2: + movb $177,1(r1) +3: + tst (r1)+ + sub $200,r0 + cmp r1,$KDSD5 + blos 1b + +/ set up supervisor D registers + +9: + mov $6,SISD0 + mov $6,SISD1 + +/ set up real sp +/ clear user block +/ test for floating point hardware + + mov $_u+[usize*64.],sp + mov $1f,nofault + setd / jump to 1f if this traps + inc fpp +1: + clr nofault + mov $_u,r0 +1: + clr (r0)+ + cmp r0,sp + blo 1b + .if .PROFIL + mov $40000,r0 + mov $10000,PS / prev = super +1: + clr -(sp) + mtpi (r0)+ + cmp r0,$100000 + blo 1b + jsr pc,_isprof + .endif + +/ set up previous mode and call main +/ on return, enter user mode at 0R + + mov $30000,PS + jsr pc,_main + mov $170000,-(sp) + clr -(sp) + rtt + +/ Source of assembler to be inserted into mch.s +/ This assembler code halts the machine +/ Inserted after the start code +/ and before the various bootstraps +/ The code that stops the system is in stopunix() +/ should be added at the end of ken/sys4.c +/ This just does a halt +/ + +halt = 0 // Halt instruction not in assembler + +.globl _stopit +_stopit: + mov $0340,*$6 + reset + halt + +.globl _rkboot, _rpboot +_rkboot: + jmp *$173000 + +_rpboot: + jmp *$173006 + + +.globl trap, call +.globl _trap + +/ all traps and interrupts are +/ vectored thru this routine. + +trap: + mov PS,saveps + tst nofault + bne 1f + mov SSR0,ssr + mov SSR1,ssr+2 + mov SSR2,ssr+4 + mov $1,SSR0 + jsr r0,call1; jmp _trap + / no return +1: + mov $1,SSR0 + mov nofault,(sp) + rtt +.text + +.globl _runrun +call1: + mov saveps,-(sp) + spl 0 + br 1f + +call: + mov PS,-(sp) +1: + mov r1,-(sp) + mfpd sp + mov 4(sp),-(sp) + bic $!37,(sp) + bit $30000,PS + beq 1f + jsr pc,(r0)+ + tstb _runrun + beq 2f + mov $12.,(sp) / trap 12 is give up cpu + jsr pc,_trap +2: + tst (sp)+ + mtpd sp + br 2f +1: + bis $30000,PS + jsr pc,(r0)+ + cmp (sp)+,(sp)+ +2: + mov (sp)+,r1 + tst (sp)+ + mov (sp)+,r0 + rtt + +.globl _savfp +_savfp: + tst fpp + beq 9f / No FP hardware + mov 2(sp),r1 + stfps (r1)+ + setd + movf fr0,(r1)+ + movf fr1,(r1)+ + movf fr2,(r1)+ + movf fr3,(r1)+ + movf fr4,fr0 + movf fr0,(r1)+ + movf fr5,fr0 + movf fr0,(r1)+ +9: + rts pc + +.globl _restfp +_restfp: + tst fpp + beq 9f + mov 2(sp),r1 + mov r1,r0 + setd + add $8.+2.,r1 + movf (r1)+,fr1 + movf (r1)+,fr2 + movf (r1)+,fr3 + movf (r1)+,fr0 + movf fr0,fr4 + movf (r1)+,fr0 + movf fr0,fr5 + movf 2(r0),fr0 + ldfps (r0) +9: + rts pc + +.globl _stst +_stst: + tst fpp + beq 9f + stst r0 + mov r0,*2(sp) +9: + rts pc + +.globl _addupc +_addupc: + mov r2,-(sp) + mov 6(sp),r2 / base of prof with base,leng,off,scale + mov 4(sp),r0 / pc + sub 4(r2),r0 / offset + clc + ror r0 + mov 6(r2),r1 + clc + ror r1 + mul r1,r0 / scale + ashc $-14.,r0 + inc r1 + bic $1,r1 + cmp r1,2(r2) / length + bhis 1f + add (r2),r1 / base + mov nofault,-(sp) + mov $2f,nofault + mfpd (r1) + add 12.(sp),(sp) + mtpd (r1) + br 3f +2: + clr 6(r2) +3: + mov (sp)+,nofault +1: + mov (sp)+,r2 + rts pc + +.globl _display +_display: + dec dispdly + bge 2f + clr dispdly + mov PS,-(sp) + mov $HIPRI,PS + mov CSW,r1 + bit $1,r1 + beq 1f + bis $30000,PS + dec r1 +1: + jsr pc,fuword + mov r0,CSW + mov (sp)+,PS + cmp r0,$-1 + bne 2f + mov $120.,dispdly / 2 sec delay after CSW fault +2: + rts pc + +.globl _backup +.globl _regloc +_backup: + mov 2(sp),r0 + movb ssr+2,r1 + jsr pc,1f + movb ssr+3,r1 + jsr pc,1f + movb _regloc+7,r1 + asl r1 + add r0,r1 + mov ssr+4,(r1) + clr r0 +2: + rts pc +1: + mov r1,-(sp) + asr (sp) + asr (sp) + asr (sp) + bic $!7,r1 + movb _regloc(r1),r1 + asl r1 + add r0,r1 + sub (sp)+,(r1) + rts pc + + +.globl _fubyte, _subyte +.globl _fuword, _suword +.globl _fuibyte, _suibyte +.globl _fuiword, _suiword +_fuibyte: + mov 2(sp),r1 + bic $1,r1 + jsr pc,giword + br 2f + +_fubyte: + mov 2(sp),r1 + bic $1,r1 + jsr pc,gword + +2: + cmp r1,2(sp) + beq 1f + swab r0 +1: + bic $!377,r0 + rts pc + +_suibyte: + mov 2(sp),r1 + bic $1,r1 + jsr pc,giword + mov r0,-(sp) + cmp r1,4(sp) + beq 1f + movb 6(sp),1(sp) + br 2f +1: + movb 6(sp),(sp) +2: + mov (sp)+,r0 + jsr pc,piword + clr r0 + rts pc + +_subyte: + mov 2(sp),r1 + bic $1,r1 + jsr pc,gword + mov r0,-(sp) + cmp r1,4(sp) + beq 1f + movb 6(sp),1(sp) + br 2f +1: + movb 6(sp),(sp) +2: + mov (sp)+,r0 + jsr pc,pword + clr r0 + rts pc + +_fuiword: + mov 2(sp),r1 +fuiword: + jsr pc,giword + rts pc + +_fuword: + mov 2(sp),r1 +fuword: + jsr pc,gword + rts pc + +giword: + mov PS,-(sp) + spl HIGH + mov nofault,-(sp) + mov $err,nofault + mfpi (r1) + mov (sp)+,r0 + br 1f + +gword: + mov PS,-(sp) + spl HIGH + mov nofault,-(sp) + mov $err,nofault + mfpd (r1) + mov (sp)+,r0 + br 1f + +_suiword: + mov 2(sp),r1 + mov 4(sp),r0 +suiword: + jsr pc,piword + rts pc + +_suword: + mov 2(sp),r1 + mov 4(sp),r0 +suword: + jsr pc,pword + rts pc + +piword: + mov PS,-(sp) + spl HIGH + mov nofault,-(sp) + mov $err,nofault + mov r0,-(sp) + mtpi (r1) + br 1f + +pword: + mov PS,-(sp) + spl HIGH + mov nofault,-(sp) + mov $err,nofault + mov r0,-(sp) + mtpd (r1) +1: + mov (sp)+,nofault + mov (sp)+,PS + rts pc + +err: + mov (sp)+,nofault + mov (sp)+,PS + tst (sp)+ + mov $-1,r0 + rts pc + +.globl _copyin, _copyout +.globl _copyiin, _copyiout +_copyiin: + jsr pc,copsu +1: + mfpi (r0)+ + mov (sp)+,(r1)+ + sob r2,1b + br 2f + +_copyin: + jsr pc,copsu +1: + mfpd (r0)+ + mov (sp)+,(r1)+ + sob r2,1b + br 2f + +_copyiout: + jsr pc,copsu +1: + mov (r0)+,-(sp) + mtpi (r1)+ + sob r2,1b + br 2f + +_copyout: + jsr pc,copsu +1: + mov (r0)+,-(sp) + mtpd (r1)+ + sob r2,1b +2: + mov (sp)+,nofault + mov (sp)+,r2 + clr r0 + rts pc + +copsu: + mov (sp)+,r0 + mov r2,-(sp) + mov nofault,-(sp) + mov r0,-(sp) + mov 10(sp),r0 + mov 12(sp),r1 + mov 14(sp),r2 + asr r2 + mov $1f,nofault + rts pc + +1: + mov (sp)+,nofault + mov (sp)+,r2 + mov $-1,r0 + rts pc + +.globl _idle, _waitloc +_idle: + mov PS,-(sp) + spl 0 + wait +waitloc: + mov (sp)+,PS + rts pc + + .data +_waitloc: + waitloc + .text + +.globl _save +_save: + mov (sp)+,r1 + mov (sp),r0 + mov r2,(r0)+ + mov r3,(r0)+ + mov r4,(r0)+ + mov r5,(r0)+ + mov sp,(r0)+ + mov r1,(r0)+ + clr r0 + jmp (r1) + + .globl _resume +_resume: + mov 2(sp),r0 / new process + mov 4(sp),r1 / new stack + spl 7 + mov r0,KDSA6 / In new process + mov (r1)+,r2 + mov (r1)+,r3 + mov (r1)+,r4 + mov (r1)+,r5 + mov (r1)+,sp + mov $1,r0 + spl 0 + jmp *(r1)+ + +.globl _spl0, _spl1, _spl4, _spl5, _spl6, _spl7, _splx +_spl0: + mov PS,r0 + spl 0 + rts pc + +_spl1: + mov PS,r0 + spl 1 + rts pc + +_spl4: + mov PS,r0 + spl 4 + rts pc + +_spl5: + mov PS,r0 + spl 5 + rts pc + +_spl6: + mov PS,r0 + spl 6 + rts pc + +_spl7: + mov PS,r0 + spl HIGH + rts pc + +_splx: + mov 2(sp),PS + rts pc + +.globl _copyseg +_copyseg: + mov PS,-(sp) + mov 4(sp),SISA0 + mov 6(sp),SISA1 + mov $10000+HIPRI,PS + mov r2,-(sp) + clr r0 + mov $8192.,r1 + mov $32.,r2 +1: + mfpd (r0)+ + mtpd (r1)+ + sob r2,1b + mov (sp)+,r2 + mov (sp)+,PS + rts pc + +.globl _clearseg +_clearseg: + mov PS,-(sp) + mov 4(sp),SISA0 + mov $10000+HIPRI,PS + clr r0 + mov $32.,r1 +1: + clr -(sp) + mtpd (r0)+ + sob r1,1b + mov (sp)+,PS + rts pc + +/ Long quotient + + .globl ldiv +ldiv: + jsr r5,csv + mov 10.(r5),r3 + sxt r4 + bpl 1f + neg r3 +1: + cmp r4,8.(r5) + bne hardldiv + mov 6.(r5),r2 + mov 4.(r5),r1 + bge 1f + neg r1 + neg r2 + sbc r1 + com r4 +1: + mov r4,-(sp) + clr r0 + div r3,r0 + mov r0,r4 /high quotient + mov r1,r0 + mov r2,r1 + div r3,r0 + bvc 1f + sub r3,r0 / this is the clever part + div r3,r0 + tst r1 + sxt r1 + add r1,r0 / cannot overflow! +1: + mov r0,r1 + mov r4,r0 + tst (sp)+ + bpl 9f + neg r0 + neg r1 + sbc r0 +9: + jmp cret + +hardldiv: + 4 + +/ Long remainder + + .globl lrem +lrem: + jsr r5,csv + mov 10.(r5),r3 + sxt r4 + bpl 1f + neg r3 +1: + cmp r4,8.(r5) + bne hardlrem + mov 6.(r5),r2 + mov 4.(r5),r1 + mov r1,r4 + bge 1f + neg r1 + neg r2 + sbc r1 +1: + clr r0 + div r3,r0 + mov r1,r0 + mov r2,r1 + div r3,r0 + bvc 1f + sub r3,r0 + div r3,r0 + tst r1 + beq 9f + add r3,r1 +1: + tst r4 + bpl 9f + neg r1 +9: + sxt r0 + jmp cret + +/ The divisor is known to be >= 2^15. Only 16 cycles are +/ needed to get a remainder. +hardlrem: + 4 + +/.globl lmul +/lmul: +/ mov r2,-(sp) +/ mov r3,-(sp) +/ mov 8(sp),r2 +/ sxt r1 +/ sub 6(sp),r1 +/ mov 12.(sp),r0 +/ sxt r3 +/ sub 10.(sp),r3 +/ mul r0,r1 +/ mul r2,r3 +/ add r1,r3 +/ mul r2,r0 +/ sub r3,r0 +/ mov (sp)+,r3 +/ mov (sp)+,r2 +/ rts pc + +.globl csv +csv: + mov r5,r0 + mov sp,r5 + mov r4,-(sp) + mov r3,-(sp) + mov r2,-(sp) + jsr pc,(r0) + +.globl cret +cret: + mov r5,r2 + mov -(r2),r4 + mov -(r2),r3 + mov -(r2),r2 + mov r5,sp + mov (sp)+,r5 + rts pc + +.globl _u +_u = 140000 +usize = 16. + +CSW = 177570 +PS = 177776 +SSR0 = 177572 +SSR1 = 177574 +SSR2 = 177576 +SSR3 = 172516 +KISA0 = 172340 +KISA1 = 172342 +KISA7 = 172356 +KISD0 = 172300 +KISD7 = 172316 +KDSA0 = 172360 +KDSA6 = 172374 +KDSA7 = 172376 +KDSD0 = 172320 +KDSD5 = 172332 +SISA0 = 172240 +SISA1 = 172242 +SISA2 = 172244 +SISD0 = 172200 +SISD1 = 172202 +SISD2 = 172204 +MSCR = 017777746 / 11/70 memory control register +IO = 177600 + +SWR = 177570 +.data +.globl _ka6 +.globl _cputype + +_ka6: KDSA6 +_cputype:45. +stk: 0 + +.bss +nofault:.=.+2 +fpp: .=.+2 +ssr: .=.+6 +dispdly:.=.+2 +saveps: .=.+2 + +.text +/ system profiler +/ Expects to have a KW11-P in addition to the line-frequency +/ clock, and it should be set to BR7. +/ Uses supervisor I space register 2&3 (40000-100000) +/ to maintain the profile. + + .if .PROFIL +CCSB = 172542 +CCSR = 172540 + +.globl _sprof, _xprobuf, _probsiz, _mode +_probsiz = 37777 + +_isprof: + mov $1f,nofault + mov $_sprof,104 / interrupt + mov $340,106 / pri + mov $100.,CCSB / count set = 100 + mov $113,CCSR / count down, 10kHz, repeat +1: + clr nofault + rts pc + +_sprof: + mov r0,-(sp) + mov PS,r0 + ash $-10.,r0 + bic $!14,r0 + add $1,_mode+2(r0) + adc _mode(r0) + cmp r0,$14 / user + beq done + mov 2(sp),r0 / pc + asr r0 + asr r0 + bic $140001,r0 + cmp r0,$_probsiz + blo 1f + inc _outside + br done +1: + mov $10340,PS / prev = super + mfpi 40000(r0) + inc (sp) + mtpi 40000(r0) + bne done + mov r1,-(sp) + mov $_xprobuf,r1 +2: + cmp (r1)+,r0 + bne 3f + inc (r1) + br 4f +3: + tst (r1)+ + bne 2b + sub $4,r1 + mov r0,(r1)+ + mov $1,(r1)+ +4: + mov (sp)+,r1 +done: + mov (sp)+,r0 + mov $113,CCSR + rtt + +/ count subroutine calls during profiling +/ of the system. + +.globl mcount, _profcnts, _profsize + +.bss +_profcnts: + .=.+[6*340.] + +.globl countbase +.data +countbase: + _profcnts +_profsize: + 340. +.text + +mcount: + mov (r0),r1 + bne 1f + mov countbase,r1 + beq 2f + add $6,countbase + cmp countbase,$_profcnts+[6*340.] + blo 3f + clr countbase + rts pc +3: + mov (sp),(r1)+ + mov r1,(r0) +1: + inc 2(r1) + bne 2f + inc (r1) +2: + rts pc + +.bss +_xprobuf:.=.+512. +_proloc:.=.+2 +_mode: .=.+16. +_outside: .=.+2 + + .endif diff --git a/halt/orig/mch.s b/halt/orig/mch.s new file mode 100644 index 0000000..28718b4 --- /dev/null +++ b/halt/orig/mch.s @@ -0,0 +1,1058 @@ +/ machine language assist +/ for 11/45 or 11/70 CPUs + + +/ non-UNIX instructions +mfpi = 6500^tst +stst = 170300^tst +mtpi = 6600^tst +mfpd = 106500^tst +mtpd = 106600^tst +spl = 230 +ldfps = 170100^tst +stfps = 170200^tst +wait = 1 +rtt = 6 +reset = 5 + +.PROFIL = 0 +HIPRI = 340 +HIGH = 7 + .if .PROFIL +HIGH = 6 +HIPRI = 300 + .endif + +/ Mag tape dump +/ save registers in low core and +/ write all core onto mag tape. +/ entry is thru 44 abs + +.data +.globl dump +dump: + +/ save regs r0,r1,r2,r3,r4,r5,r6,KIA6 +/ starting at abs location 4 + + mov r0,4 + mov $6,r0 + mov r1,(r0)+ + mov r2,(r0)+ + mov r3,(r0)+ + mov r4,(r0)+ + mov r5,(r0)+ + mov sp,(r0)+ + mov KDSA6,(r0)+ + +/ dump all of core (ie to first mt error) +/ onto mag tape. (9 track or 7 track 'binary') + +.if HTDUMP + mov $HTCS1,r0 + mov $40,*$HTCS2 + mov $2300,*$HTTC + clr *$HTBA + mov $1,(r0) +1: + mov $-512.,*$HTFC + mov $-256.,*$HTWC + movb $61,(r0) +2: + tstb (r0) + bge 2b + bit $1,(r0) + bne 2b + bit $40000,(r0) + beq 1b + mov $27,(r0) +.endif +HT = 0172440 +HTCS1 = HT+0 +HTWC = HT+2 +HTBA = HT+4 +HTFC = HT+6 +HTCS2 = HT+10 +HTTC = HT+32 + +MTC = 172522 +.if TUDUMP + mov $MTC,r0 + mov $60004,(r0)+ + clr 2(r0) +1: + mov $-512.,(r0) + inc -(r0) +2: + tstb (r0) + bge 2b + tst (r0)+ + bge 1b + reset + +/ end of file and loop + + mov $60007,-(r0) +.endif + br . + +.text +.globl start, _end, _edata, _etext, _main + +/ 11/45 and 11/70 startup. +/ entry is thru 0 abs. +/ since core is shuffled, +/ this code can be executed but once + +start: + bit $1,SSR0 + beq 1f + mov $trap,34 + mov $trap,0 + mov $340+15.,2 + bit $20,SSR3 + beq 9f + mov $70.,_cputype + mov $3,*$MSCR +9: + clr PS + br 9f +1: + inc $-1 + bne . + reset +/ Set loc. 0 to trap to system, in case of +/ hardware glitch + mov $trap,0 / in case of bad trap through 0 + mov $340+15.,2 / high pri, trap type 15 + clr PS + +/ set KI0 to physical 0 + + mov $77406,r3 + mov $KISA0,r0 + mov $KISD0,r1 + clr (r0)+ + mov r3,(r1)+ + +/ set KI1-6 to eventual text resting place + + mov $_end+63.,r2 + ash $-6,r2 + bic $!1777,r2 +1: + mov r2,(r0)+ + mov r3,(r1)+ + add $200,r2 + cmp r0,$KISA7 + blos 1b + +/ set KI7 to IO seg for escape + + mov $IO,-(r0) + +/ set KD0-7 to physical + + mov $KDSA0,r0 + mov $KDSD0,r1 + clr r2 +1: + mov r2,(r0)+ + mov r3,(r1)+ + add $200,r2 + cmp r0,$KDSA7 + blos 1b + +/ initialization +/ get a temp (1-word) stack +/ turn on segmentation +/ copy text to I space +/ clear bss in D space + + mov $stk+2,sp + mov $65,SSR3 / 22-bit, map, K+U sep + bit $20,SSR3 + beq 1f + mov $70.,_cputype + mov $3,*$MSCR / Disable UNIBUS traps, non-fatal traps +1: + inc SSR0 + mov $_etext+100,r2 + mov $_edata+100,r1 + add $_etext-8192.,r1 +1: + mov -(r1),-(sp) + mtpi -(r2) + cmp r1,$_edata + bhi 1b +1: + clr (r1)+ + cmp r1,$_end + blo 1b + +/ use KI escape to set KD7 to IO seg +/ set KD6 to first available core +/ If profiling, snag supervisor registers. + + mov $IO,-(sp) + mtpi *$KDSA7 +9: + mov $_etext-8192.+63.,r2 + ash $-6,r2 + bic $!1777,r2 + add KISA1,r2 + .if .PROFIL + mov r2,SISA2 + mov r2,_proloc + mov $77406,SISD2 + add $200,r2 + mov r2,SISA2+2 + mov $77406,SISD2+2 + add $200,r2 + .endif + mov r2,KDSA6 + +/ Turn off write permission on kernel text +/ Take stuff above data out of address space + + mov $KISD0,r0 +1: + mov $77402,(r0)+ + cmp r0,$KISD7 + blos 1b + + mov $_end+63.,r0 + ash $-6,r0 + bic $!1777,r0 + mov $KDSD0,r1 +1: + cmp r0,$200 + bge 2f + dec r0 + bge 4f + clr (r1) + br 3f +4: + movb r0,1(r1) + br 3f +2: + movb $177,1(r1) +3: + tst (r1)+ + sub $200,r0 + cmp r1,$KDSD5 + blos 1b + +/ set up supervisor D registers + +9: + mov $6,SISD0 + mov $6,SISD1 + +/ set up real sp +/ clear user block +/ test for floating point hardware + + mov $_u+[usize*64.],sp + mov $1f,nofault + setd / jump to 1f if this traps + inc fpp +1: + clr nofault + mov $_u,r0 +1: + clr (r0)+ + cmp r0,sp + blo 1b + .if .PROFIL + mov $40000,r0 + mov $10000,PS / prev = super +1: + clr -(sp) + mtpi (r0)+ + cmp r0,$100000 + blo 1b + jsr pc,_isprof + .endif + +/ set up previous mode and call main +/ on return, enter user mode at 0R + + mov $30000,PS + jsr pc,_main + mov $170000,-(sp) + clr -(sp) + rtt + +.globl _rkboot, _rpboot +_rkboot: + jmp *$173000 + +_rpboot: + jmp *$173006 + + +.globl trap, call +.globl _trap + +/ all traps and interrupts are +/ vectored thru this routine. + +trap: + mov PS,saveps + tst nofault + bne 1f + mov SSR0,ssr + mov SSR1,ssr+2 + mov SSR2,ssr+4 + mov $1,SSR0 + jsr r0,call1; jmp _trap + / no return +1: + mov $1,SSR0 + mov nofault,(sp) + rtt +.text + +.globl _runrun +call1: + mov saveps,-(sp) + spl 0 + br 1f + +call: + mov PS,-(sp) +1: + mov r1,-(sp) + mfpd sp + mov 4(sp),-(sp) + bic $!37,(sp) + bit $30000,PS + beq 1f + jsr pc,(r0)+ + tstb _runrun + beq 2f + mov $12.,(sp) / trap 12 is give up cpu + jsr pc,_trap +2: + tst (sp)+ + mtpd sp + br 2f +1: + bis $30000,PS + jsr pc,(r0)+ + cmp (sp)+,(sp)+ +2: + mov (sp)+,r1 + tst (sp)+ + mov (sp)+,r0 + rtt + +.globl _savfp +_savfp: + tst fpp + beq 9f / No FP hardware + mov 2(sp),r1 + stfps (r1)+ + setd + movf fr0,(r1)+ + movf fr1,(r1)+ + movf fr2,(r1)+ + movf fr3,(r1)+ + movf fr4,fr0 + movf fr0,(r1)+ + movf fr5,fr0 + movf fr0,(r1)+ +9: + rts pc + +.globl _restfp +_restfp: + tst fpp + beq 9f + mov 2(sp),r1 + mov r1,r0 + setd + add $8.+2.,r1 + movf (r1)+,fr1 + movf (r1)+,fr2 + movf (r1)+,fr3 + movf (r1)+,fr0 + movf fr0,fr4 + movf (r1)+,fr0 + movf fr0,fr5 + movf 2(r0),fr0 + ldfps (r0) +9: + rts pc + +.globl _stst +_stst: + tst fpp + beq 9f + stst r0 + mov r0,*2(sp) +9: + rts pc + +.globl _addupc +_addupc: + mov r2,-(sp) + mov 6(sp),r2 / base of prof with base,leng,off,scale + mov 4(sp),r0 / pc + sub 4(r2),r0 / offset + clc + ror r0 + mov 6(r2),r1 + clc + ror r1 + mul r1,r0 / scale + ashc $-14.,r0 + inc r1 + bic $1,r1 + cmp r1,2(r2) / length + bhis 1f + add (r2),r1 / base + mov nofault,-(sp) + mov $2f,nofault + mfpd (r1) + add 12.(sp),(sp) + mtpd (r1) + br 3f +2: + clr 6(r2) +3: + mov (sp)+,nofault +1: + mov (sp)+,r2 + rts pc + +.globl _display +_display: + dec dispdly + bge 2f + clr dispdly + mov PS,-(sp) + mov $HIPRI,PS + mov CSW,r1 + bit $1,r1 + beq 1f + bis $30000,PS + dec r1 +1: + jsr pc,fuword + mov r0,CSW + mov (sp)+,PS + cmp r0,$-1 + bne 2f + mov $120.,dispdly / 2 sec delay after CSW fault +2: + rts pc + +.globl _backup +.globl _regloc +_backup: + mov 2(sp),r0 + movb ssr+2,r1 + jsr pc,1f + movb ssr+3,r1 + jsr pc,1f + movb _regloc+7,r1 + asl r1 + add r0,r1 + mov ssr+4,(r1) + clr r0 +2: + rts pc +1: + mov r1,-(sp) + asr (sp) + asr (sp) + asr (sp) + bic $!7,r1 + movb _regloc(r1),r1 + asl r1 + add r0,r1 + sub (sp)+,(r1) + rts pc + + +.globl _fubyte, _subyte +.globl _fuword, _suword +.globl _fuibyte, _suibyte +.globl _fuiword, _suiword +_fuibyte: + mov 2(sp),r1 + bic $1,r1 + jsr pc,giword + br 2f + +_fubyte: + mov 2(sp),r1 + bic $1,r1 + jsr pc,gword + +2: + cmp r1,2(sp) + beq 1f + swab r0 +1: + bic $!377,r0 + rts pc + +_suibyte: + mov 2(sp),r1 + bic $1,r1 + jsr pc,giword + mov r0,-(sp) + cmp r1,4(sp) + beq 1f + movb 6(sp),1(sp) + br 2f +1: + movb 6(sp),(sp) +2: + mov (sp)+,r0 + jsr pc,piword + clr r0 + rts pc + +_subyte: + mov 2(sp),r1 + bic $1,r1 + jsr pc,gword + mov r0,-(sp) + cmp r1,4(sp) + beq 1f + movb 6(sp),1(sp) + br 2f +1: + movb 6(sp),(sp) +2: + mov (sp)+,r0 + jsr pc,pword + clr r0 + rts pc + +_fuiword: + mov 2(sp),r1 +fuiword: + jsr pc,giword + rts pc + +_fuword: + mov 2(sp),r1 +fuword: + jsr pc,gword + rts pc + +giword: + mov PS,-(sp) + spl HIGH + mov nofault,-(sp) + mov $err,nofault + mfpi (r1) + mov (sp)+,r0 + br 1f + +gword: + mov PS,-(sp) + spl HIGH + mov nofault,-(sp) + mov $err,nofault + mfpd (r1) + mov (sp)+,r0 + br 1f + +_suiword: + mov 2(sp),r1 + mov 4(sp),r0 +suiword: + jsr pc,piword + rts pc + +_suword: + mov 2(sp),r1 + mov 4(sp),r0 +suword: + jsr pc,pword + rts pc + +piword: + mov PS,-(sp) + spl HIGH + mov nofault,-(sp) + mov $err,nofault + mov r0,-(sp) + mtpi (r1) + br 1f + +pword: + mov PS,-(sp) + spl HIGH + mov nofault,-(sp) + mov $err,nofault + mov r0,-(sp) + mtpd (r1) +1: + mov (sp)+,nofault + mov (sp)+,PS + rts pc + +err: + mov (sp)+,nofault + mov (sp)+,PS + tst (sp)+ + mov $-1,r0 + rts pc + +.globl _copyin, _copyout +.globl _copyiin, _copyiout +_copyiin: + jsr pc,copsu +1: + mfpi (r0)+ + mov (sp)+,(r1)+ + sob r2,1b + br 2f + +_copyin: + jsr pc,copsu +1: + mfpd (r0)+ + mov (sp)+,(r1)+ + sob r2,1b + br 2f + +_copyiout: + jsr pc,copsu +1: + mov (r0)+,-(sp) + mtpi (r1)+ + sob r2,1b + br 2f + +_copyout: + jsr pc,copsu +1: + mov (r0)+,-(sp) + mtpd (r1)+ + sob r2,1b +2: + mov (sp)+,nofault + mov (sp)+,r2 + clr r0 + rts pc + +copsu: + mov (sp)+,r0 + mov r2,-(sp) + mov nofault,-(sp) + mov r0,-(sp) + mov 10(sp),r0 + mov 12(sp),r1 + mov 14(sp),r2 + asr r2 + mov $1f,nofault + rts pc + +1: + mov (sp)+,nofault + mov (sp)+,r2 + mov $-1,r0 + rts pc + +.globl _idle, _waitloc +_idle: + mov PS,-(sp) + spl 0 + wait +waitloc: + mov (sp)+,PS + rts pc + + .data +_waitloc: + waitloc + .text + +.globl _save +_save: + mov (sp)+,r1 + mov (sp),r0 + mov r2,(r0)+ + mov r3,(r0)+ + mov r4,(r0)+ + mov r5,(r0)+ + mov sp,(r0)+ + mov r1,(r0)+ + clr r0 + jmp (r1) + + .globl _resume +_resume: + mov 2(sp),r0 / new process + mov 4(sp),r1 / new stack + spl 7 + mov r0,KDSA6 / In new process + mov (r1)+,r2 + mov (r1)+,r3 + mov (r1)+,r4 + mov (r1)+,r5 + mov (r1)+,sp + mov $1,r0 + spl 0 + jmp *(r1)+ + +.globl _spl0, _spl1, _spl4, _spl5, _spl6, _spl7, _splx +_spl0: + mov PS,r0 + spl 0 + rts pc + +_spl1: + mov PS,r0 + spl 1 + rts pc + +_spl4: + mov PS,r0 + spl 4 + rts pc + +_spl5: + mov PS,r0 + spl 5 + rts pc + +_spl6: + mov PS,r0 + spl 6 + rts pc + +_spl7: + mov PS,r0 + spl HIGH + rts pc + +_splx: + mov 2(sp),PS + rts pc + +.globl _copyseg +_copyseg: + mov PS,-(sp) + mov 4(sp),SISA0 + mov 6(sp),SISA1 + mov $10000+HIPRI,PS + mov r2,-(sp) + clr r0 + mov $8192.,r1 + mov $32.,r2 +1: + mfpd (r0)+ + mtpd (r1)+ + sob r2,1b + mov (sp)+,r2 + mov (sp)+,PS + rts pc + +.globl _clearseg +_clearseg: + mov PS,-(sp) + mov 4(sp),SISA0 + mov $10000+HIPRI,PS + clr r0 + mov $32.,r1 +1: + clr -(sp) + mtpd (r0)+ + sob r1,1b + mov (sp)+,PS + rts pc + +/ Long quotient + + .globl ldiv +ldiv: + jsr r5,csv + mov 10.(r5),r3 + sxt r4 + bpl 1f + neg r3 +1: + cmp r4,8.(r5) + bne hardldiv + mov 6.(r5),r2 + mov 4.(r5),r1 + bge 1f + neg r1 + neg r2 + sbc r1 + com r4 +1: + mov r4,-(sp) + clr r0 + div r3,r0 + mov r0,r4 /high quotient + mov r1,r0 + mov r2,r1 + div r3,r0 + bvc 1f + sub r3,r0 / this is the clever part + div r3,r0 + tst r1 + sxt r1 + add r1,r0 / cannot overflow! +1: + mov r0,r1 + mov r4,r0 + tst (sp)+ + bpl 9f + neg r0 + neg r1 + sbc r0 +9: + jmp cret + +hardldiv: + 4 + +/ Long remainder + + .globl lrem +lrem: + jsr r5,csv + mov 10.(r5),r3 + sxt r4 + bpl 1f + neg r3 +1: + cmp r4,8.(r5) + bne hardlrem + mov 6.(r5),r2 + mov 4.(r5),r1 + mov r1,r4 + bge 1f + neg r1 + neg r2 + sbc r1 +1: + clr r0 + div r3,r0 + mov r1,r0 + mov r2,r1 + div r3,r0 + bvc 1f + sub r3,r0 + div r3,r0 + tst r1 + beq 9f + add r3,r1 +1: + tst r4 + bpl 9f + neg r1 +9: + sxt r0 + jmp cret + +/ The divisor is known to be >= 2^15. Only 16 cycles are +/ needed to get a remainder. +hardlrem: + 4 + +/.globl lmul +/lmul: +/ mov r2,-(sp) +/ mov r3,-(sp) +/ mov 8(sp),r2 +/ sxt r1 +/ sub 6(sp),r1 +/ mov 12.(sp),r0 +/ sxt r3 +/ sub 10.(sp),r3 +/ mul r0,r1 +/ mul r2,r3 +/ add r1,r3 +/ mul r2,r0 +/ sub r3,r0 +/ mov (sp)+,r3 +/ mov (sp)+,r2 +/ rts pc + +.globl csv +csv: + mov r5,r0 + mov sp,r5 + mov r4,-(sp) + mov r3,-(sp) + mov r2,-(sp) + jsr pc,(r0) + +.globl cret +cret: + mov r5,r2 + mov -(r2),r4 + mov -(r2),r3 + mov -(r2),r2 + mov r5,sp + mov (sp)+,r5 + rts pc + +.globl _u +_u = 140000 +usize = 16. + +CSW = 177570 +PS = 177776 +SSR0 = 177572 +SSR1 = 177574 +SSR2 = 177576 +SSR3 = 172516 +KISA0 = 172340 +KISA1 = 172342 +KISA7 = 172356 +KISD0 = 172300 +KISD7 = 172316 +KDSA0 = 172360 +KDSA6 = 172374 +KDSA7 = 172376 +KDSD0 = 172320 +KDSD5 = 172332 +SISA0 = 172240 +SISA1 = 172242 +SISA2 = 172244 +SISD0 = 172200 +SISD1 = 172202 +SISD2 = 172204 +MSCR = 017777746 / 11/70 memory control register +IO = 177600 + +SWR = 177570 +.data +.globl _ka6 +.globl _cputype + +_ka6: KDSA6 +_cputype:45. +stk: 0 + +.bss +nofault:.=.+2 +fpp: .=.+2 +ssr: .=.+6 +dispdly:.=.+2 +saveps: .=.+2 + +.text +/ system profiler +/ Expects to have a KW11-P in addition to the line-frequency +/ clock, and it should be set to BR7. +/ Uses supervisor I space register 2&3 (40000-100000) +/ to maintain the profile. + + .if .PROFIL +CCSB = 172542 +CCSR = 172540 + +.globl _sprof, _xprobuf, _probsiz, _mode +_probsiz = 37777 + +_isprof: + mov $1f,nofault + mov $_sprof,104 / interrupt + mov $340,106 / pri + mov $100.,CCSB / count set = 100 + mov $113,CCSR / count down, 10kHz, repeat +1: + clr nofault + rts pc + +_sprof: + mov r0,-(sp) + mov PS,r0 + ash $-10.,r0 + bic $!14,r0 + add $1,_mode+2(r0) + adc _mode(r0) + cmp r0,$14 / user + beq done + mov 2(sp),r0 / pc + asr r0 + asr r0 + bic $140001,r0 + cmp r0,$_probsiz + blo 1f + inc _outside + br done +1: + mov $10340,PS / prev = super + mfpi 40000(r0) + inc (sp) + mtpi 40000(r0) + bne done + mov r1,-(sp) + mov $_xprobuf,r1 +2: + cmp (r1)+,r0 + bne 3f + inc (r1) + br 4f +3: + tst (r1)+ + bne 2b + sub $4,r1 + mov r0,(r1)+ + mov $1,(r1)+ +4: + mov (sp)+,r1 +done: + mov (sp)+,r0 + mov $113,CCSR + rtt + +/ count subroutine calls during profiling +/ of the system. + +.globl mcount, _profcnts, _profsize + +.bss +_profcnts: + .=.+[6*340.] + +.globl countbase +.data +countbase: + _profcnts +_profsize: + 340. +.text + +mcount: + mov (r0),r1 + bne 1f + mov countbase,r1 + beq 2f + add $6,countbase + cmp countbase,$_profcnts+[6*340.] + blo 3f + clr countbase + rts pc +3: + mov (sp),(r1)+ + mov r1,(r0) +1: + inc 2(r1) + bne 2f + inc (r1) +2: + rts pc + +.bss +_xprobuf:.=.+512. +_proloc:.=.+2 +_mode: .=.+16. +_outside: .=.+2 + + .endif diff --git a/halt/orig/sys4.c b/halt/orig/sys4.c new file mode 100644 index 0000000..c8fba7c --- /dev/null +++ b/halt/orig/sys4.c @@ -0,0 +1,422 @@ +#include "../h/param.h" +#include "../h/systm.h" +#include "../h/dir.h" +#include "../h/user.h" +#include "../h/reg.h" +#include "../h/inode.h" +#include "../h/proc.h" +#include "../h/timeb.h" + +/* + * Everything in this file is a routine implementing a system call. + */ + +/* + * return the current time (old-style entry) + */ +gtime() +{ + u.u_r.r_time = time; +} + +/* + * New time entry-- return TOD with milliseconds, timezone, + * DST flag + */ +ftime() +{ + register struct a { + struct timeb *tp; + } *uap; + struct timeb t; + register unsigned ms; + + uap = (struct a *)u.u_ap; + spl7(); + t.time = time; + ms = lbolt; + spl0(); + if (ms > HZ) { + ms -= HZ; + t.time++; + } + t.millitm = (1000*ms)/HZ; + t.timezone = TIMEZONE; + t.dstflag = DSTFLAG; + if (copyout((caddr_t)&t, (caddr_t)uap->tp, sizeof(t)) < 0) + u.u_error = EFAULT; +} + +/* + * Set the time + */ +stime() +{ + register struct a { + time_t time; + } *uap; + + uap = (struct a *)u.u_ap; + if(suser()) + time = uap->time; +} + +setuid() +{ + register uid; + register struct a { + int uid; + } *uap; + + uap = (struct a *)u.u_ap; + uid = uap->uid; + if(u.u_ruid == uid || suser()) { + u.u_uid = uid; + u.u_procp->p_uid = uid; + u.u_ruid = uid; + } +} + +getuid() +{ + + u.u_r.r_val1 = u.u_ruid; + u.u_r.r_val2 = u.u_uid; +} + +setgid() +{ + register gid; + register struct a { + int gid; + } *uap; + + uap = (struct a *)u.u_ap; + gid = uap->gid; + if(u.u_rgid == gid || suser()) { + u.u_gid = gid; + u.u_rgid = gid; + } +} + +getgid() +{ + + u.u_r.r_val1 = u.u_rgid; + u.u_r.r_val2 = u.u_gid; +} + +getpid() +{ + u.u_r.r_val1 = u.u_procp->p_pid; + u.u_r.r_val2 = u.u_procp->p_ppid; +} + +sync() +{ + + update(); +} + +nice() +{ + register n; + register struct a { + int niceness; + } *uap; + + uap = (struct a *)u.u_ap; + n = uap->niceness; + if(n < 0 && !suser()) + n = 0; + n += u.u_procp->p_nice; + if(n >= 2*NZERO) + n = 2*NZERO -1; + if(n < 0) + n = 0; + u.u_procp->p_nice = n; +} + +/* + * Unlink system call. + * Hard to avoid races here, especially + * in unlinking directories. + */ +unlink() +{ + register struct inode *ip, *pp; + struct a { + char *fname; + }; + + pp = namei(uchar, 2); + if(pp == NULL) + return; + /* + * Check for unlink(".") + * to avoid hanging on the iget + */ + if (pp->i_number == u.u_dent.d_ino) { + ip = pp; + ip->i_count++; + } else + ip = iget(pp->i_dev, u.u_dent.d_ino); + if(ip == NULL) + goto out1; + if((ip->i_mode&IFMT)==IFDIR && !suser()) + goto out; + /* + * Don't unlink a mounted file. + */ + if (ip->i_dev != pp->i_dev) { + u.u_error = EBUSY; + goto out; + } + if (ip->i_flag&ITEXT) + xrele(ip); /* try once to free text */ + if (ip->i_flag&ITEXT && ip->i_nlink==1) { + u.u_error = ETXTBSY; + goto out; + } + u.u_offset -= sizeof(struct direct); + u.u_base = (caddr_t)&u.u_dent; + u.u_count = sizeof(struct direct); + u.u_dent.d_ino = 0; + writei(pp); + ip->i_nlink--; + ip->i_flag |= ICHG; + +out: + iput(ip); +out1: + iput(pp); +} +chdir() +{ + chdirec(&u.u_cdir); +} + +chroot() +{ + if (suser()) + chdirec(&u.u_rdir); +} + +chdirec(ipp) +register struct inode **ipp; +{ + register struct inode *ip; + struct a { + char *fname; + }; + + ip = namei(uchar, 0); + if(ip == NULL) + return; + if((ip->i_mode&IFMT) != IFDIR) { + u.u_error = ENOTDIR; + goto bad; + } + if(access(ip, IEXEC)) + goto bad; + prele(ip); + if (*ipp) { + plock(*ipp); + iput(*ipp); + } + *ipp = ip; + return; + +bad: + iput(ip); +} + +chmod() +{ + register struct inode *ip; + register struct a { + char *fname; + int fmode; + } *uap; + + uap = (struct a *)u.u_ap; + if ((ip = owner()) == NULL) + return; + ip->i_mode &= ~07777; + if (u.u_uid) + uap->fmode &= ~ISVTX; + ip->i_mode |= uap->fmode&07777; + ip->i_flag |= ICHG; + if (ip->i_flag&ITEXT && (ip->i_mode&ISVTX)==0) + xrele(ip); + iput(ip); +} + +chown() +{ + register struct inode *ip; + register struct a { + char *fname; + int uid; + int gid; + } *uap; + + uap = (struct a *)u.u_ap; + if (!suser() || (ip = owner()) == NULL) + return; + ip->i_uid = uap->uid; + ip->i_gid = uap->gid; + ip->i_flag |= ICHG; + iput(ip); +} + +ssig() +{ + register a; + struct a { + int signo; + int fun; + } *uap; + + uap = (struct a *)u.u_ap; + a = uap->signo; + if(a<=0 || a>=NSIG || a==SIGKIL) { + u.u_error = EINVAL; + return; + } + u.u_r.r_val1 = u.u_signal[a]; + u.u_signal[a] = uap->fun; + u.u_procp->p_sig &= ~(1<<(a-1)); +} + +kill() +{ + register struct proc *p, *q; + register a; + register struct a { + int pid; + int signo; + } *uap; + int f, priv; + + uap = (struct a *)u.u_ap; + f = 0; + a = uap->pid; + priv = 0; + if (a==-1 && u.u_uid==0) { + priv++; + a = 0; + } + q = u.u_procp; + for(p = &proc[0]; p < &proc[NPROC]; p++) { + if(p->p_stat == NULL) + continue; + if(a != 0 && p->p_pid != a) + continue; + if(a==0 && ((p->p_pgrp!=q->p_pgrp&&priv==0) || p<=&proc[1])) + continue; + if(u.u_uid != 0 && u.u_uid != p->p_uid) + continue; + f++; + psignal(p, uap->signo); + } + if(f == 0) + u.u_error = ESRCH; +} + +times() +{ + register struct a { + time_t (*times)[4]; + } *uap; + + uap = (struct a *)u.u_ap; + if (copyout((caddr_t)&u.u_utime, (caddr_t)uap->times, sizeof(*uap->times)) < 0) + u.u_error = EFAULT; +} + +profil() +{ + register struct a { + short *bufbase; + unsigned bufsize; + unsigned pcoffset; + unsigned pcscale; + } *uap; + + uap = (struct a *)u.u_ap; + u.u_prof.pr_base = uap->bufbase; + u.u_prof.pr_size = uap->bufsize; + u.u_prof.pr_off = uap->pcoffset; + u.u_prof.pr_scale = uap->pcscale; +} + +/* + * alarm clock signal + */ +alarm() +{ + register struct proc *p; + register c; + register struct a { + int deltat; + } *uap; + + uap = (struct a *)u.u_ap; + p = u.u_procp; + c = p->p_clktim; + p->p_clktim = uap->deltat; + u.u_r.r_val1 = c; +} + +/* + * indefinite wait. + * no one should wakeup(&u) + */ +pause() +{ + + for(;;) + sleep((caddr_t)&u, PSLEP); +} + +/* + * mode mask for creation of files + */ +umask() +{ + register struct a { + int mask; + } *uap; + register t; + + uap = (struct a *)u.u_ap; + t = u.u_cmask; + u.u_cmask = uap->mask & 0777; + u.u_r.r_val1 = t; +} + +/* + * Set IUPD and IACC times on file. + * Can't set ICHG. + */ +utime() +{ + register struct a { + char *fname; + time_t *tptr; + } *uap; + register struct inode *ip; + time_t tv[2]; + + uap = (struct a *)u.u_ap; + if ((ip = owner()) == NULL) + return; + if (copyin((caddr_t)uap->tptr, (caddr_t)tv, sizeof(tv))) { + u.u_error = EFAULT; + return; + } + ip->i_flag |= IACC|IUPD|ICHG; + iupdat(ip, &tv[0], &tv[1]); + iput(ip); +} diff --git a/halt/orig/sysent.c b/halt/orig/sysent.c new file mode 100644 index 0000000..81ca2d6 --- /dev/null +++ b/halt/orig/sysent.c @@ -0,0 +1,131 @@ +#include "../h/param.h" +#include "../h/systm.h" + +/* + * This table is the switch used to transfer + * to the appropriate routine for processing a system call. + * Each row contains the number of arguments expected + * and a pointer to the routine. + */ +int alarm(); +int mpxchan(); +int chdir(); +int chmod(); +int chown(); +int chroot(); +int close(); +int creat(); +int dup(); +int exec(); +int exece(); +int fork(); +int fstat(); +int getgid(); +int getpid(); +int getuid(); +int gtime(); +int gtty(); +int ioctl(); +int kill(); +int link(); +int mknod(); +int nice(); +int nosys(); +int nullsys(); +int open(); +int pause(); +int pipe(); +int profil(); +int ptrace(); +int read(); +int rexit(); +int saccess(); +int sbreak(); +int seek(); +int setgid(); +int setuid(); +int smount(); +int ssig(); +int stat(); +int stime(); +int stty(); +int sumount(); +int ftime(); +int sync(); +int sysacct(); +int syslock(); +int sysphys(); +int times(); +int umask(); +int unlink(); +int utime(); +int wait(); +int write(); + +struct sysent sysent[64] = +{ + 0, 0, nullsys, /* 0 = indir */ + 1, 1, rexit, /* 1 = exit */ + 0, 0, fork, /* 2 = fork */ + 3, 1, read, /* 3 = read */ + 3, 1, write, /* 4 = write */ + 2, 0, open, /* 5 = open */ + 1, 1, close, /* 6 = close */ + 0, 0, wait, /* 7 = wait */ + 2, 0, creat, /* 8 = creat */ + 2, 0, link, /* 9 = link */ + 1, 0, unlink, /* 10 = unlink */ + 2, 0, exec, /* 11 = exec */ + 1, 0, chdir, /* 12 = chdir */ + 0, 0, gtime, /* 13 = time */ + 3, 0, mknod, /* 14 = mknod */ + 2, 0, chmod, /* 15 = chmod */ + 3, 0, chown, /* 16 = chown; now 3 args */ + 1, 0, sbreak, /* 17 = break */ + 2, 0, stat, /* 18 = stat */ + 4, 1, seek, /* 19 = seek; now 3 args */ + 0, 0, getpid, /* 20 = getpid */ + 3, 0, smount, /* 21 = mount */ + 1, 0, sumount, /* 22 = umount */ + 1, 1, setuid, /* 23 = setuid */ + 0, 0, getuid, /* 24 = getuid */ + 2, 2, stime, /* 25 = stime */ + 4, 1, ptrace, /* 26 = ptrace */ + 1, 1, alarm, /* 27 = alarm */ + 2, 1, fstat, /* 28 = fstat */ + 0, 0, pause, /* 29 = pause */ + 2, 0, utime, /* 30 = utime */ + 2, 1, stty, /* 31 = stty */ + 2, 1, gtty, /* 32 = gtty */ + 2, 0, saccess, /* 33 = access */ + 1, 1, nice, /* 34 = nice */ + 1, 0, ftime, /* 35 = ftime; formerly sleep */ + 0, 0, sync, /* 36 = sync */ + 2, 1, kill, /* 37 = kill */ + 0, 0, nullsys, /* 38 = switch; inoperative */ + 0, 0, nullsys, /* 39 = setpgrp (not in yet) */ + 1, 1, nosys, /* 40 = tell (obsolete) */ + 2, 2, dup, /* 41 = dup */ + 0, 0, pipe, /* 42 = pipe */ + 1, 0, times, /* 43 = times */ + 4, 0, profil, /* 44 = prof */ + 0, 0, nosys, /* 45 = unused */ + 1, 1, setgid, /* 46 = setgid */ + 0, 0, getgid, /* 47 = getgid */ + 2, 0, ssig, /* 48 = sig */ + 0, 0, nosys, /* 49 = reserved for USG */ + 0, 0, nosys, /* 50 = reserved for USG */ + 1, 0, sysacct, /* 51 = turn acct off/on */ + 3, 0, sysphys, /* 52 = set user physical addresses */ + 1, 0, syslock, /* 53 = lock user in core */ + 3, 0, ioctl, /* 54 = ioctl */ + 0, 0, nosys, /* 55 = readwrite (in abeyance) */ + 4, 0, mpxchan, /* 56 = creat mpx comm channel */ + 0, 0, nosys, /* 57 = reserved for USG */ + 0, 0, nosys, /* 58 = reserved for USG */ + 3, 0, exece, /* 59 = exece */ + 1, 0, umask, /* 60 = umask */ + 1, 0, chroot, /* 61 = chroot */ + 0, 0, nosys, /* 62 = x */ + 0, 0, nosys /* 63 = used internally */ +}; diff --git a/halt/stopunix.s b/halt/stopunix.s new file mode 100644 index 0000000..986af54 --- /dev/null +++ b/halt/stopunix.s @@ -0,0 +1,14 @@ +.globl _stopunix +.globl cerror +stopunix = 62. + +_stopunix: + mov r5,-(sp) + mov sp,r5 + sys stopunix + bec 1f + jmp cerror +1: + clr r0 + mov (sp)+,r5 + rts pc diff --git a/halt/sys4.c b/halt/sys4.c new file mode 100644 index 0000000..dce0a32 --- /dev/null +++ b/halt/sys4.c @@ -0,0 +1,440 @@ +#include "../h/param.h" +#include "../h/systm.h" +#include "../h/dir.h" +#include "../h/user.h" +#include "../h/reg.h" +#include "../h/inode.h" +#include "../h/proc.h" +#include "../h/timeb.h" + +/* + * Everything in this file is a routine implementing a system call. + */ + +/* + * return the current time (old-style entry) + */ +gtime() +{ + u.u_r.r_time = time; +} + +/* + * New time entry-- return TOD with milliseconds, timezone, + * DST flag + */ +ftime() +{ + register struct a { + struct timeb *tp; + } *uap; + struct timeb t; + register unsigned ms; + + uap = (struct a *)u.u_ap; + spl7(); + t.time = time; + ms = lbolt; + spl0(); + if (ms > HZ) { + ms -= HZ; + t.time++; + } + t.millitm = (1000*ms)/HZ; + t.timezone = TIMEZONE; + t.dstflag = DSTFLAG; + if (copyout((caddr_t)&t, (caddr_t)uap->tp, sizeof(t)) < 0) + u.u_error = EFAULT; +} + +/* + * Set the time + */ +stime() +{ + register struct a { + time_t time; + } *uap; + + uap = (struct a *)u.u_ap; + if(suser()) + time = uap->time; +} + +setuid() +{ + register uid; + register struct a { + int uid; + } *uap; + + uap = (struct a *)u.u_ap; + uid = uap->uid; + if(u.u_ruid == uid || suser()) { + u.u_uid = uid; + u.u_procp->p_uid = uid; + u.u_ruid = uid; + } +} + +getuid() +{ + + u.u_r.r_val1 = u.u_ruid; + u.u_r.r_val2 = u.u_uid; +} + +setgid() +{ + register gid; + register struct a { + int gid; + } *uap; + + uap = (struct a *)u.u_ap; + gid = uap->gid; + if(u.u_rgid == gid || suser()) { + u.u_gid = gid; + u.u_rgid = gid; + } +} + +getgid() +{ + + u.u_r.r_val1 = u.u_rgid; + u.u_r.r_val2 = u.u_gid; +} + +getpid() +{ + u.u_r.r_val1 = u.u_procp->p_pid; + u.u_r.r_val2 = u.u_procp->p_ppid; +} + +sync() +{ + + update(); +} + +nice() +{ + register n; + register struct a { + int niceness; + } *uap; + + uap = (struct a *)u.u_ap; + n = uap->niceness; + if(n < 0 && !suser()) + n = 0; + n += u.u_procp->p_nice; + if(n >= 2*NZERO) + n = 2*NZERO -1; + if(n < 0) + n = 0; + u.u_procp->p_nice = n; +} + +/* + * Unlink system call. + * Hard to avoid races here, especially + * in unlinking directories. + */ +unlink() +{ + register struct inode *ip, *pp; + struct a { + char *fname; + }; + + pp = namei(uchar, 2); + if(pp == NULL) + return; + /* + * Check for unlink(".") + * to avoid hanging on the iget + */ + if (pp->i_number == u.u_dent.d_ino) { + ip = pp; + ip->i_count++; + } else + ip = iget(pp->i_dev, u.u_dent.d_ino); + if(ip == NULL) + goto out1; + if((ip->i_mode&IFMT)==IFDIR && !suser()) + goto out; + /* + * Don't unlink a mounted file. + */ + if (ip->i_dev != pp->i_dev) { + u.u_error = EBUSY; + goto out; + } + if (ip->i_flag&ITEXT) + xrele(ip); /* try once to free text */ + if (ip->i_flag&ITEXT && ip->i_nlink==1) { + u.u_error = ETXTBSY; + goto out; + } + u.u_offset -= sizeof(struct direct); + u.u_base = (caddr_t)&u.u_dent; + u.u_count = sizeof(struct direct); + u.u_dent.d_ino = 0; + writei(pp); + ip->i_nlink--; + ip->i_flag |= ICHG; + +out: + iput(ip); +out1: + iput(pp); +} +chdir() +{ + chdirec(&u.u_cdir); +} + +chroot() +{ + if (suser()) + chdirec(&u.u_rdir); +} + +chdirec(ipp) +register struct inode **ipp; +{ + register struct inode *ip; + struct a { + char *fname; + }; + + ip = namei(uchar, 0); + if(ip == NULL) + return; + if((ip->i_mode&IFMT) != IFDIR) { + u.u_error = ENOTDIR; + goto bad; + } + if(access(ip, IEXEC)) + goto bad; + prele(ip); + if (*ipp) { + plock(*ipp); + iput(*ipp); + } + *ipp = ip; + return; + +bad: + iput(ip); +} + +chmod() +{ + register struct inode *ip; + register struct a { + char *fname; + int fmode; + } *uap; + + uap = (struct a *)u.u_ap; + if ((ip = owner()) == NULL) + return; + ip->i_mode &= ~07777; + if (u.u_uid) + uap->fmode &= ~ISVTX; + ip->i_mode |= uap->fmode&07777; + ip->i_flag |= ICHG; + if (ip->i_flag&ITEXT && (ip->i_mode&ISVTX)==0) + xrele(ip); + iput(ip); +} + +chown() +{ + register struct inode *ip; + register struct a { + char *fname; + int uid; + int gid; + } *uap; + + uap = (struct a *)u.u_ap; + if (!suser() || (ip = owner()) == NULL) + return; + ip->i_uid = uap->uid; + ip->i_gid = uap->gid; + ip->i_flag |= ICHG; + iput(ip); +} + +ssig() +{ + register a; + struct a { + int signo; + int fun; + } *uap; + + uap = (struct a *)u.u_ap; + a = uap->signo; + if(a<=0 || a>=NSIG || a==SIGKIL) { + u.u_error = EINVAL; + return; + } + u.u_r.r_val1 = u.u_signal[a]; + u.u_signal[a] = uap->fun; + u.u_procp->p_sig &= ~(1<<(a-1)); +} + +kill() +{ + register struct proc *p, *q; + register a; + register struct a { + int pid; + int signo; + } *uap; + int f, priv; + + uap = (struct a *)u.u_ap; + f = 0; + a = uap->pid; + priv = 0; + if (a==-1 && u.u_uid==0) { + priv++; + a = 0; + } + q = u.u_procp; + for(p = &proc[0]; p < &proc[NPROC]; p++) { + if(p->p_stat == NULL) + continue; + if(a != 0 && p->p_pid != a) + continue; + if(a==0 && ((p->p_pgrp!=q->p_pgrp&&priv==0) || p<=&proc[1])) + continue; + if(u.u_uid != 0 && u.u_uid != p->p_uid) + continue; + f++; + psignal(p, uap->signo); + } + if(f == 0) + u.u_error = ESRCH; +} + +times() +{ + register struct a { + time_t (*times)[4]; + } *uap; + + uap = (struct a *)u.u_ap; + if (copyout((caddr_t)&u.u_utime, (caddr_t)uap->times, sizeof(*uap->times)) < 0) + u.u_error = EFAULT; +} + +profil() +{ + register struct a { + short *bufbase; + unsigned bufsize; + unsigned pcoffset; + unsigned pcscale; + } *uap; + + uap = (struct a *)u.u_ap; + u.u_prof.pr_base = uap->bufbase; + u.u_prof.pr_size = uap->bufsize; + u.u_prof.pr_off = uap->pcoffset; + u.u_prof.pr_scale = uap->pcscale; +} + +/* + * alarm clock signal + */ +alarm() +{ + register struct proc *p; + register c; + register struct a { + int deltat; + } *uap; + + uap = (struct a *)u.u_ap; + p = u.u_procp; + c = p->p_clktim; + p->p_clktim = uap->deltat; + u.u_r.r_val1 = c; +} + +/* + * indefinite wait. + * no one should wakeup(&u) + */ +pause() +{ + + for(;;) + sleep((caddr_t)&u, PSLEP); +} + +/* + * mode mask for creation of files + */ +umask() +{ + register struct a { + int mask; + } *uap; + register t; + + uap = (struct a *)u.u_ap; + t = u.u_cmask; + u.u_cmask = uap->mask & 0777; + u.u_r.r_val1 = t; +} + +/* + * Set IUPD and IACC times on file. + * Can't set ICHG. + */ +utime() +{ + register struct a { + char *fname; + time_t *tptr; + } *uap; + register struct inode *ip; + time_t tv[2]; + + uap = (struct a *)u.u_ap; + if ((ip = owner()) == NULL) + return; + if (copyin((caddr_t)uap->tptr, (caddr_t)tv, sizeof(tv))) { + u.u_error = EFAULT; + return; + } + ip->i_flag |= IACC|IUPD|ICHG; + iupdat(ip, &tv[0], &tv[1]); + iput(ip); +} + +/* + * Added as a system call to cleanly stop the system + * Originally Peter C Nov 1976 + * revised 2023 + */ +stopunix() +{ + if(suser()) { + /* if still updating then return to caller */ + if (updlock) + u.u_error = EBUSY; + else { + update(); + stopit(); + } + } +} diff --git a/halt/sysent.c b/halt/sysent.c new file mode 100644 index 0000000..33e59dc --- /dev/null +++ b/halt/sysent.c @@ -0,0 +1,141 @@ +#include "../h/param.h" +#include "../h/systm.h" + +/* add the stopunix system call */ +#define STOPUNIX + +/* + * This table is the switch used to transfer + * to the appropriate routine for processing a system call. + * Each row contains the number of arguments expected + * and a pointer to the routine. + */ +int alarm(); +int mpxchan(); +int chdir(); +int chmod(); +int chown(); +int chroot(); +int close(); +int creat(); +int dup(); +int exec(); +int exece(); +int fork(); +int fstat(); +int getgid(); +int getpid(); +int getuid(); +int gtime(); +int gtty(); +int ioctl(); +int kill(); +int link(); +int mknod(); +int nice(); +int nosys(); +int nullsys(); +int open(); +int pause(); +int pipe(); +int profil(); +int ptrace(); +int read(); +int rexit(); +int saccess(); +int sbreak(); +int seek(); +int setgid(); +int setuid(); +int smount(); +int ssig(); +int stat(); +int stime(); +#ifdef STOPUNIX +int stopunix(); +#endif +int stty(); +int sumount(); +int ftime(); +int sync(); +int sysacct(); +int syslock(); +int sysphys(); +int times(); +int umask(); +int unlink(); +int utime(); +int wait(); +int write(); + +struct sysent sysent[64] = +{ + 0, 0, nullsys, /* 0 = indir */ + 1, 1, rexit, /* 1 = exit */ + 0, 0, fork, /* 2 = fork */ + 3, 1, read, /* 3 = read */ + 3, 1, write, /* 4 = write */ + 2, 0, open, /* 5 = open */ + 1, 1, close, /* 6 = close */ + 0, 0, wait, /* 7 = wait */ + 2, 0, creat, /* 8 = creat */ + 2, 0, link, /* 9 = link */ + 1, 0, unlink, /* 10 = unlink */ + 2, 0, exec, /* 11 = exec */ + 1, 0, chdir, /* 12 = chdir */ + 0, 0, gtime, /* 13 = time */ + 3, 0, mknod, /* 14 = mknod */ + 2, 0, chmod, /* 15 = chmod */ + 3, 0, chown, /* 16 = chown; now 3 args */ + 1, 0, sbreak, /* 17 = break */ + 2, 0, stat, /* 18 = stat */ + 4, 1, seek, /* 19 = seek; now 3 args */ + 0, 0, getpid, /* 20 = getpid */ + 3, 0, smount, /* 21 = mount */ + 1, 0, sumount, /* 22 = umount */ + 1, 1, setuid, /* 23 = setuid */ + 0, 0, getuid, /* 24 = getuid */ + 2, 2, stime, /* 25 = stime */ + 4, 1, ptrace, /* 26 = ptrace */ + 1, 1, alarm, /* 27 = alarm */ + 2, 1, fstat, /* 28 = fstat */ + 0, 0, pause, /* 29 = pause */ + 2, 0, utime, /* 30 = utime */ + 2, 1, stty, /* 31 = stty */ + 2, 1, gtty, /* 32 = gtty */ + 2, 0, saccess, /* 33 = access */ + 1, 1, nice, /* 34 = nice */ + 1, 0, ftime, /* 35 = ftime; formerly sleep */ + 0, 0, sync, /* 36 = sync */ + 2, 1, kill, /* 37 = kill */ + 0, 0, nullsys, /* 38 = switch; inoperative */ + 0, 0, nullsys, /* 39 = setpgrp (not in yet) */ + 1, 1, nosys, /* 40 = tell (obsolete) */ + 2, 2, dup, /* 41 = dup */ + 0, 0, pipe, /* 42 = pipe */ + 1, 0, times, /* 43 = times */ + 4, 0, profil, /* 44 = prof */ + 0, 0, nosys, /* 45 = unused */ + 1, 1, setgid, /* 46 = setgid */ + 0, 0, getgid, /* 47 = getgid */ + 2, 0, ssig, /* 48 = sig */ + 0, 0, nosys, /* 49 = reserved for USG */ + 0, 0, nosys, /* 50 = reserved for USG */ + 1, 0, sysacct, /* 51 = turn acct off/on */ + 3, 0, sysphys, /* 52 = set user physical addresses */ + 1, 0, syslock, /* 53 = lock user in core */ + 3, 0, ioctl, /* 54 = ioctl */ + 0, 0, nosys, /* 55 = readwrite (in abeyance) */ + 4, 0, mpxchan, /* 56 = creat mpx comm channel */ + 0, 0, nosys, /* 57 = reserved for USG */ + 0, 0, nosys, /* 58 = reserved for USG */ + 3, 0, exece, /* 59 = exece */ + 1, 0, umask, /* 60 = umask */ + 1, 0, chroot, /* 61 = chroot */ +#ifdef STOPUNIX + 0, 0, stopunix, /* 62 = stopunix */ +#else + 0, 0, nosys, /* 62 = x */ +#endif + 0, 0, nosys /* 63 = used internally */ +}; diff --git a/man/README.md b/man/README.md new file mode 100644 index 0000000..ac6521c --- /dev/null +++ b/man/README.md @@ -0,0 +1,3 @@ +# Change to the _man_ macros + +The _man_ command uses _/usr/lib/tmac/tmac.an_ as its basic troff/nroff macro package. It assumes that the value of the year coming out of the _localtime_ routine is in the 1900's. This change adds 1900 to the value of the two year number registers and removes '19' from the heading definitions. diff --git a/man/tmac.an b/man/tmac.an new file mode 100644 index 0000000..8b0de83 --- /dev/null +++ b/man/tmac.an @@ -0,0 +1,270 @@ +' # month name +.if "\nd"0" .nr m \n(mo-1 +.if "\nm"0" .ds ]m January +.if "\nm"1" .ds ]m February +.if "\nm"2" .ds ]m March +.if "\nm"3" .ds ]m April +.if "\nm"4" .ds ]m May +.if "\nm"5" .ds ]m June +.if "\nm"6" .ds ]m July +.if "\nm"7" .ds ]m August +.if "\nm"8" .ds ]m September +.if "\nm"9" .ds ]m October +.if "\nm"10" .ds ]m November +.if "\nm"11" .ds ]m December +' # set the date +' # Y2K change +.nr yr +1900 +.nr y +1900 +.if n \{.nr m \nm+1 +. ie \nd .ds ]W Modified \nm/\nd/\ny +. el .ds ]W Printed \n(mo/\n(dy/\n(yr\} +.if t \{.ie \nd .ds ]W \*(]m \nd, \ny +. el .ds ]W \*(]m \n(dy, \n(yr\} +.if t .ds ]W 7th Edition +' # reset the basic page layout +.de }E +.}f +.in \\n()Ru+\\n(INu +.ll \\n(LLu +.. +' # default tabs +.de DT +'ta .5i 1i 1.5i 2i 2.5i 3i 3.5i 4i 4.5i 5i 5.5i 6i 6.5i +.. +' # set type font and size +.de }f +.ps 10 +.ft 1 +.. +' # handle the head of the page +.de }H +.ev 1 +.}C +'sp .5i +.ft 1 +.ps 10 +.tl @\\*(]H@\\*(]D@\\*(]H@ +'sp .5i +.ev +.ns +.. +' # handle the foot of the page +.de }F +.ev 1 +.ft 1 +.ps 10 +'sp .5i +.tl @\\*(]W@\\*(]L@%@ +'bp +.ev +.. +' # the cut mark +.if n .ig +.de }C +.po .1i +.tl '-' +.po +.. +' # the final cut mark +.de }M +.}N +.wh -1p }C +.ll \\n(LLu +.. +' # no runout unless there was a .TH +.de }K +.}N +.pl 1 +.ll \\n(LLu +.. +.em }K +' # set title and heading +.de TH +.PD +.if n .nr IN .5i +.if t .nr IN .5i +.nr LL \\n(.l +.ds ]H \\$1\|(\|\\$2\|) +.ds ]D UNIX Programmer's Manual +.ds ]L \\$3 +.wh 0 }H +.if t .wh -1i }F +.if n .wh -1.167i }F +.em }M +.if \\n(nl .bp 1 +.}E +.DT +.nr )I .5i +.nr )R 0 +.if n .na +.. +' # section heading +.de SH +.}X 0 +.nr )E 2 +\&\\$1 \|\\$2 \|\\$3 \|\\$4 \|\\$5 \|\\$6 +.. +' # sub section heading +.de SS +.}X \\n()Ru+\\n(INu +\&\\$1 \|\\$2 \|\\$3 \|\\$4 \|\\$5 \|\\$6 +.br +.. +' # subroutine for section heading +.de }X +.}E +.ti \\$1 +.sp \\n()Pu +.ne 2 +.nr )R 0 +.fi +.it 1 }N +.SM +.B +.. +' # end of SH (cf }X above and }N below) +.de }2 +.nr )E 0 +.}E +.nr )I .5i +.ns +.. +' # italic +.de I +.ft 2 +.it 1 }N +.if !"\\$1"" \&\\$1 \\$2 \\$3 \\$4 \\$5 \\$6 +.. +' # bold +.de B +.ft 3 +.it 1 }N +.if !"\\$1"" \&\\$1 \\$2 \\$3 \\$4 \\$5 \\$6 +.. +' # small +.de SM +.ps 9 +.it 1 }N +.if !"\\$1"" \&\\$1 \\$2 \\$3 \\$4 \\$5 \\$6 +.. +' # combinations of Roman, italic, bold +.de RI +.}S 1 2 \& "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" +.. +.de RB +.}S 1 3 \& "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" +.. +.de IR +.}S 2 1 \& "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" +.. +.de IB +.}S 2 3 \& "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" +.. +.de BR +.}S 3 1 \& "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" +.. +.de BI +.}S 3 2 \& "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" +.. +' # make special case of shift out of italic +.de }S +.ds ]F +.if "\\$1"2" .if !"\\$5"" .ds ]F\^ +.ie !"\\$4"" .}S \\$2 \\$1 "\\$3\f\\$1\\$4\\*(]F" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9" +.el \\$3 +.}f +.. +' # paragraph +.de LP +.PP +.. +.de PP +.sp \\n()Pu +.ne 2 +.}E +.nr )I .5i +.ns +.. +' # paragraph distance +.de PD +.if t .nr )P .4v +.if n .nr )P 1v +.if !"\\$1"" .nr )P \\$1v +.. +' # hanging indent +.de HP +.sp \\n()Pu +.ne 2 +.if !"\\$1"" .nr )I \\$1n +.ll \\n(LLu +.in \\n()Ru+\\n(INu+\\n()Iu +.ti \\n()Ru+\\n(INu +.}f +.. +' # indented paragraph +.de IP +.TP \\$2 +\&\\$1 +.. +' # hanging label +.de TP +.if !"\\$1"" .nr )I \\$1n +.sp \\n()Pu +.in \\n()Ru +.nr )E 1 +.ns +.it 1 }N +.di ]B +.. +' # end of TP (cf }N below) +.de }1 +.ds ]X \&\\*(]B\\ +.nr )E 0 +.if !"\\$1"" .nr )I \\$1n +.}f +.ll \\n(LLu +.in \\n()Ru+\\n(INu+\\n()Iu +.ti \\n(INu +.ie !\\n()Iu+\\n()Ru-\w@\\*(]X@u-3p \{\\*(]X +.br\} +.el \\*(]X\h@|\\n()Iu+\\n()Ru@\c +.}f +.. +' # handle end of 1-line features +.de }N +.if \\n()E .br +.di +.if "\\n()E"0" .}f +.if "\\n()E"1" .}1 +.if "\\n()E"2" .}2 +.nr )E 0 +.. +' # increase relative indent +.de RS +.nr ]\\n+()p \\n()I +.nr )\\n()p \\n()R +.ie !"\\$1"" .nr )R +\\$1n +.el .nr )R +\\n()I +.nr )I .5i +.}E +.. +' # decrease relative indent +.de RE +.if !"\\$1"" \{.ie "\\$1"0" .nr )p 1 1 +. el .nr )p \\$1 1\} +.ds ]i \\*(]I\\n()p +.ds ]r \\*(]R\\n()p +.nr )I \\*(]i +.nr )R \\*(]r +.if \\n()p .nr )p -1 +.}E +.. +.nr )p 0 1 +.ds ]I \\\\n(] +.ds ]R \\\\n() +.bd S 3 3 +.if t .ds R \(rg +.if n .ds R (Reg.) +.ds S \s10 +.hy 14 diff --git a/sh-test/README.md b/sh-test/README.md new file mode 100644 index 0000000..a6106ed --- /dev/null +++ b/sh-test/README.md @@ -0,0 +1,10 @@ +# Missing [ command + +I found that on my distribution the __[__ command was missing. This should be a link to the _test_ command and makes the shell syntax for __if__ work. + +``` sh +if [ -f /bin/test]; then + echo 'test exists' +``` + +The script here checks for the existence of ``[`` and makes a link if needed. diff --git a/sh-test/check b/sh-test/check new file mode 100755 index 0000000..784e529 --- /dev/null +++ b/sh-test/check @@ -0,0 +1,9 @@ +#!/bin/sh +# Script to test if [ is missing from /bin +cd /bin +if ! test -f '/bin/['; then + ln test '[' + echo 'The [ command is now linked to test' +else + echo 'The [ command exists' +fi diff --git a/ssp/README.md b/ssp/README.md new file mode 100644 index 0000000..afabff8 --- /dev/null +++ b/ssp/README.md @@ -0,0 +1,22 @@ +# The ssp command + +_ssp_ is program written by Bill Joy, and was extracted from University of California’s 1BSD release. _ssp_ stands for - single space output, and removes successive blank lines from its standard input. The program is used automatically when output is to the terminal, or not otherwise. + +### Compilation + +To compile use the _makefile_: + +``` sh +make +``` +### Installation + +Again use the _makefile_ to install the binary and the manual page: + +``` sh +make install +``` + +### Man scripts + +I've install the _man_ script here into my own private _bin_ directory. The _man7_ command is useful it you have a manual page in a file and you want to see what it says. diff --git a/ssp/makefile b/ssp/makefile new file mode 100644 index 0000000..d11e9a1 --- /dev/null +++ b/ssp/makefile @@ -0,0 +1,8 @@ +CFLAGS=-s -n -O + +ssp: ssp.c + cc $(CFLAGS) -o ssp ssp.c + +install: ssp + cp ssp /usr/bin + cp ssp.1 /usr/man/man1 diff --git a/ssp/man b/ssp/man new file mode 100755 index 0000000..53dedc5 --- /dev/null +++ b/ssp/man @@ -0,0 +1,4 @@ +if [ $# -eq 0 ]; then + exit +fi +/bin/man "$@" | ssp diff --git a/ssp/man7 b/ssp/man7 new file mode 100755 index 0000000..15197b1 --- /dev/null +++ b/ssp/man7 @@ -0,0 +1 @@ +nroff -man -h "$@" | ssp diff --git a/ssp/ssp.1 b/ssp/ssp.1 new file mode 100644 index 0000000..1eab355 --- /dev/null +++ b/ssp/ssp.1 @@ -0,0 +1,29 @@ +.TH SSP UCB 2/24/79 UCB +.SH NAME +ssp \- limit to single spacing +.SH SYNOPSIS +.B ssp +[ +.B \- +] [ +file ... +] +.SH DESCRIPTION +.I Ssp +compresses consecutive blank lines to at most one blank line to produce +more compact output for a \s-2CRT\s0 or to save paper on the printer. +If the optional +.B \- +is given +.I ssp +is even more zealous, getting rid of all empty lines. +.PP +.I Ssp +is useful for compressing text with blank lines onto a \s-2CRT\s0, +and is used by +.IR man (1). +.SH SEE\ ALSO +colcrt(UCB), cr3(UCB), man(UCB) +.SH AUTHOR +Bill Joy +.SH BUGS diff --git a/ssp/ssp.c b/ssp/ssp.c new file mode 100644 index 0000000..bb5e97e --- /dev/null +++ b/ssp/ssp.c @@ -0,0 +1,87 @@ +/* + * ssp - single space output + * + * Bill Joy UCB August 25, 1977 + * + * Compress multiple empty lines to a single empty line. + * Option - compresses to nothing. + * + * Peter Collinson June 2023 + * nroff will output escape sequences, 033 and another character + * for up and down movements on the line. + * This code now removes those sequences, which makes pages like + * man printf + * look OK. + * Of course, there should be a special tool for this, but it's + * convenient to insert it here. + */ + +#include + +char poof, hadsome; + +extern int fout; + +main(argc, argv) + int argc; + char *argv[]; +{ + register int c; + FILE *f; + + argc--, argv++; + do { + while (argc > 0 && argv[0][0] == '-') { + poof = 1; + argc--, argv++; + } + f = stdin; + if (argc > 0) { + if ((f = fopen(argv[0], "r")) < 0) { + flush(); + perror(argv[0]); + exit(1); + } + argc--, argv++; + } + for (;;) { + c = getc(f); + if (c == -1) + break; + if (c == '\033') { + c = getc(f); + if (c == -1) + break; + continue; + } + if (c != '\n') { + hadsome = 1; + putchar(c); + continue; + } + + /* + * Eat em up + */ + if (hadsome) + putchar('\n'); + c = getc(f); + if (c == -1) + break; + if (c != '\n') { + putchar(c); + hadsome = 1; + continue; + } + do + c = getc(f); + while (c == '\n'); + if (!poof && hadsome) + putchar('\n'); + if (c == -1) + break; + putchar(c); + hadsome = 1; + } + } while (argc > 0); +} diff --git a/time/README.md b/time/README.md new file mode 100644 index 0000000..7622bee --- /dev/null +++ b/time/README.md @@ -0,0 +1,37 @@ +# Making time Y2K compliant + +Applications on the system that deal with time use a library routine _ctime.c_. It contains _gmtime_ that computes meaningful human time from the kernel clock running in seconds from 1970. This is a 32 bit value, and the problems with it running out of bits are well known. There are other routines in _ctime.c_ that are wrappers for _gmtime_ delivering the decoded information in various formats. + +Unlike V6, there isn't a lot to do for V7. It's compiler is dealing nicely (or nearly nicely) with 32 bit 'longs'. + +The _ctime.c_ routines also contain the timezone information, the offset from GMT and the local timezone names. These are set to suit users on the east side of the USA. The names of the timezone and the offset are in _timezone.c_, and that file can contain your timezone names. However, the actual days in the year that the time flips in and out of daylight savings time are set in _ctime.c_. + +The main command that needs changing is _date_ that needs a tiny change to understand the two digits of the year given in its argument when setting time. As I was using _tp_ a lot, I've made a small change in that command to understand current dates - see [tp](tp). The code should be compiled after the library has been updated. + +## Installation + +The _makefile_ is used to make a version of _date_ using the code in this directory, the intention is to provide a way of testing date setting using the new routines without having to change the C library. Running ```make``` will compile _date.c_, _ctime.c_ and _timezone.c_. + +The binaries for _ctime.c_ and _timezone.c_ can be installed in _/usr/lib_ using ```make install```. + +## Changes to timezone.c: + +I've added the name for the UK's summer time. If you want to add other European timezones, remember that the offset from GMT should be negative. + +## Changes to _ctime.c_ + +First, I've added the day in the year that the UK changes from GMT to BST and back. I've done this using defines retaining the old code. + +Second, I considered installing the 'correct' code to determine whether a year is a leap year or not. However, a computational experiment shows that there is no need to worry about this until 2100. So the current algorithm breaks well after we run out of space in a 32bit word to store the time. So I left the routine alone. + +## Changes to _date.c_ + +The year in the date is set using two digits. I've installed a heuristic rule: if the two digit year is greater than or equal to 70 than the century is 1900, otherwise it's 2000. + +## How the kernel remembers the date + +When the system is booted, the system clock is set from the date stored in the superblock of the root filesystem. This is updated in all mounted devices by the _sync_ system call. + +I've used the DecTape reader to set the time in V7 at startup, see [../v7conf/3-settime](../v7conf/3-settime). + +## Programs that use the diff --git a/time/ctime.c b/time/ctime.c new file mode 100644 index 0000000..d34442d --- /dev/null +++ b/time/ctime.c @@ -0,0 +1,261 @@ +/* + * This routine converts time as follows. + * The epoch is 0000 Jan 1 1970 GMT. + * The argument time is in seconds since then. + * The localtime(t) entry returns a pointer to an array + * containing + * seconds (0-59) + * minutes (0-59) + * hours (0-23) + * day of month (1-31) + * month (0-11) + * year-1970 + * weekday (0-6, Sun is 0) + * day of the year + * daylight savings flag + * + * The routine calls the system to determine the local + * timezone and whether Daylight Saving Time is permitted locally. + * (DST is then determined by the current US standard rules) + * There is a table that accounts for the peculiarities + * undergone by daylight time in 1974-1975. + * + * The routine does not work + * in Saudi Arabia which runs on Solar time. + * + * asctime(tvec)) + * where tvec is produced by localtime + * returns a ptr to a character string + * that has the ascii time in the form + * Thu Jan 01 00:00:00 1970n0\\ + * 01234567890123456789012345 + * 0 1 2 + * + * ctime(t) just calls localtime, then asctime. + * + */ + +#include +#include +#include + +static char cbuf[26]; +static int dmsize[12] = +{ + 31, + 28, + 31, + 30, + 31, + 30, + 31, + 31, + 30, + 31, + 30, + 31 +}; + +/* Start/stop daylight saving time */ +#define UK + +/* + * The following table is used for 1974 and 1975 and + * gives the day number of the first day after the Sunday of the + * change. + */ +static struct { + int daylb; + int dayle; +} daytab[] = { + 5, 333, /* 1974: Jan 6 - last Sun. in Nov */ + 58, 303, /* 1975: Last Sun. in Feb - last Sun in Oct */ +}; + +struct tm *gmtime(); +char *ct_numb(); +struct tm *localtime(); +char *ctime(); +char *ct_num(); +char *asctime(); + +char * +ctime(t) +long *t; +{ + return(asctime(localtime(t))); +} + +struct tm * +localtime(tim) +long *tim; +{ + register int dayno; + register struct tm *ct; + register daylbegin, daylend; + long copyt; + struct timeb systime; + + ftime(&systime); + copyt = *tim - (long)systime.timezone*60; + ct = gmtime(©t); + dayno = ct->tm_yday; +#ifdef UK + daylbegin = 89; /* last Sun in Mar 31 + 28 + 31 -1 */ + daylend = 303; /* Last Sun in Oct */ +#else + daylbegin = 119; /* last Sun in Apr */ + daylend = 303; /* Last Sun in Oct */ + if (ct->tm_year==74 || ct->tm_year==75) { + daylbegin = daytab[ct->tm_year-74].daylb; + daylend = daytab[ct->tm_year-74].dayle; + } +#endif + daylbegin = sunday(ct, daylbegin); + daylend = sunday(ct, daylend); + if (systime.dstflag && + (dayno>daylbegin || (dayno==daylbegin && ct->tm_hour>=2)) && + (daynotm_hour<1))) { + copyt += 1*60*60; + ct = gmtime(©t); + ct->tm_isdst++; + } + return(ct); +} + +/* + * The argument is a 0-origin day number. + * The value is the day number of the first + * Sunday on or after the day. + */ +static +sunday(t, d) +register struct tm *t; +register int d; +{ + if (d >= 58) + d += dysize(t->tm_year) - 365; + return(d - (d - t->tm_yday + t->tm_wday + 700) % 7); +} + +struct tm * +gmtime(tim) +long *tim; +{ + register int d0, d1; + long hms, day; + register int *tp; + static struct tm xtime; + + /* + * break initial number into days + */ + hms = *tim % 86400; + day = *tim / 86400; + if (hms<0) { + hms += 86400; + day -= 1; + } + tp = (int *)&xtime; + + /* + * generate hours:minutes:seconds + */ + *tp++ = hms%60; + d1 = hms/60; + *tp++ = d1%60; + d1 /= 60; + *tp++ = d1; + + /* + * day is the day number. + * generate day of the week. + * The addend is 4 mod 7 (1/1/1970 was Thursday) + */ + xtime.tm_wday = (day+7340036)%7; + + /* + * year number + */ + if (day>=0) for(d1=70; day >= dysize(d1); d1++) + day -= dysize(d1); + else for (d1=70; day<0; d1--) + day += dysize(d1-1); + xtime.tm_year = d1; + xtime.tm_yday = d0 = day; + + /* + * generate month + */ + + if (dysize(d1)==366) + dmsize[1] = 29; + for(d1=0; d0 >= dmsize[d1]; d1++) + d0 -= dmsize[d1]; + dmsize[1] = 28; + *tp++ = d0+1; + *tp++ = d1; + + xtime.tm_isdst = 0; + return(&xtime); +} + +char * +asctime(t) +struct tm *t; +{ + register char *cp, *ncp; + register int *tp; + + cp = cbuf; + for (ncp = "Day Mon 00 00:00:00 1900\n"; *cp++ = *ncp++;); + ncp = &"SunMonTueWedThuFriSat"[3*t->tm_wday]; + cp = cbuf; + *cp++ = *ncp++; + *cp++ = *ncp++; + *cp++ = *ncp++; + cp++; + tp = &t->tm_mon; + ncp = &"JanFebMarAprMayJunJulAugSepOctNovDec"[(*tp)*3]; + *cp++ = *ncp++; + *cp++ = *ncp++; + *cp++ = *ncp++; + cp = ct_numb(cp, *--tp); + cp = ct_numb(cp, *--tp+100); + cp = ct_numb(cp, *--tp+100); + cp = ct_numb(cp, *--tp+100); + if (t->tm_year>=100) { + cp[1] = '2'; + cp[2] = '0'; + } + cp += 2; + cp = ct_numb(cp, t->tm_year+100); + return(cbuf); +} + +/* + * This is called with a year + * and also a year-1900 + * it will work until 2100 + * but 32 bits are exhausted in 2038 + * there seems little point in worrying + */ +dysize(y) +{ + if((y%4) == 0) + return(366); + return(365); +} + +static char * +ct_numb(cp, n) +register char *cp; +{ + cp++; + if (n>=10) + *cp++ = (n/10)%10 + '0'; + else + *cp++ = ' '; + *cp++ = n%10 + '0'; + return(cp); +} diff --git a/time/date.c b/time/date.c new file mode 100644 index 0000000..47c65a1 --- /dev/null +++ b/time/date.c @@ -0,0 +1,164 @@ +/* + * date : print date + * date YYMMDDHHMM[.SS] : set date, if allowed + * date -u ... : date in GMT + */ +#include +#include +#include +#include +long timbuf; +char *ap, *ep, *sp; +int uflag; + +char *timezone(); +static int dmsize[12] = +{ + 31, + 28, + 31, + 30, + 31, + 30, + 31, + 31, + 30, + 31, + 30, + 31 +}; + +struct utmp wtmp[2] = { {"|", "", 0}, {"{", "", 0}}; + +char *ctime(); +char *asctime(); +struct tm *localtime(); +struct tm *gmtime(); + +main(argc, argv) +char *argv[]; +{ + register char *tzn; + struct timeb info; + int wf, rc; + + rc = 0; + ftime(&info); + if (argc>1 && argv[1][0]=='-' && argv[1][1]=='u') { + argc--; + argv++; + uflag++; + } + if(argc > 1) { + ap = argv[1]; + if (gtime()) { + printf("expects yymmddhhmm[.ss]\n"); + printf("date: bad conversion\n"); + exit(1); + } + /* convert to GMT assuming local time */ + if (uflag==0) { + timbuf += (long)info.timezone*60; + /* now fix up local daylight time */ + if(localtime(&timbuf)->tm_isdst) + timbuf -= 60*60; + } + time(&wtmp[0].ut_time); + if(stime(&timbuf) < 0) { + rc++; + printf("date: no permission\n"); + } else if ((wf = open("/usr/adm/wtmp", 1)) >= 0) { + time(&wtmp[1].ut_time); + lseek(wf, 0L, 2); + write(wf, (char *)wtmp, sizeof(wtmp)); + close(wf); + } + } + if (rc==0) + time(&timbuf); + if(uflag) { + ap = asctime(gmtime(&timbuf)); + tzn = "GMT"; + } else { + struct tm *tp; + tp = localtime(&timbuf); + ap = asctime(tp); + tzn = timezone(info.timezone, tp->tm_isdst); + } + printf("%.20s", ap); + if (tzn) + printf("%s", tzn); + printf("%s", ap+19); + exit(rc); +} + +gtime() +{ + register int i, year, month; + int day, hour, mins, secs; + struct tm *L; + char x; + + ep=ap; + while(*ep) ep++; + sp=ap; + while(sptm_mday); + month = gp(L->tm_mon+1); + year = gp(L->tm_year); + if(*sp) + return(1); + if( month<1 || month>12 || + day<1 || day>31 || + mins<0 || mins>59 || + secs<0 || secs>59) + return(1); + if (hour==24) { + hour=0; day++; + } + if (hour<0 || hour>23) + return(1); + timbuf = 0; + if (year < 70) year += 2000; + else year += 1900; + for(i=1970; i= 3) + timbuf++; + while(--month) + timbuf += dmsize[month-1]; + timbuf += day-1; + timbuf = 24*timbuf + hour; + timbuf = 60*timbuf + mins; + timbuf = 60*timbuf + secs; + return(0); +} + +gp(dfault) +{ + register int c, d; + + if(*sp==0) + return(dfault); + c = (*sp++)-'0'; + d = (*sp ? (*sp++)-'0' : 0); + if(c<0 || c>9 || d<0 || d>9) + return(-1); + return(c+10*d); +} diff --git a/time/install b/time/install new file mode 100644 index 0000000..ffa4d7a --- /dev/null +++ b/time/install @@ -0,0 +1,26 @@ +: Install new date command +DEST=/bin +if [ ! -f date ]; then + make date +fi +if [ ! -d $DEST/orig ]; then + mkdir $DEST + if [ ! -d $DEST/orig ]; then + echo "Failed to create $DEST/orig" + exit + fi +fi +if [ ! -f $DEST/orig/date ]; then + echo "Backing up $DEST/date to $DEST/orig" + mv $DEST/date $DEST/orig/date + if [ ! -f $DEST/orig/date ]; then + echo "Failed to create $DEST/orig" + exit + fi +fi +echo "Installing date" +cp date $DEST +if [ ! -f $DEST/date ]; then + echo "Installation failed" +fi +exit diff --git a/time/makefile b/time/makefile new file mode 100644 index 0000000..deab7c1 --- /dev/null +++ b/time/makefile @@ -0,0 +1,19 @@ +all: date timezone.o ctime.o + +date: date.c timezone.o ctime.o + cc -s -n -O -o date date.c ctime.o timezone.o + +timezone.o: timezone.c + cc -c -O timezone.c + +ctime.o: ctime.c + cc -c -O ctime.c + +libinstall: ctime.o timezone.o + ar r /lib/libc.a timezone.o ctime.o + +install: /bin/date date + sh install + +clean: + rm date *.o diff --git a/time/orig/ctime.c b/time/orig/ctime.c new file mode 100644 index 0000000..cd8b9aa --- /dev/null +++ b/time/orig/ctime.c @@ -0,0 +1,245 @@ +/* + * This routine converts time as follows. + * The epoch is 0000 Jan 1 1970 GMT. + * The argument time is in seconds since then. + * The localtime(t) entry returns a pointer to an array + * containing + * seconds (0-59) + * minutes (0-59) + * hours (0-23) + * day of month (1-31) + * month (0-11) + * year-1970 + * weekday (0-6, Sun is 0) + * day of the year + * daylight savings flag + * + * The routine calls the system to determine the local + * timezone and whether Daylight Saving Time is permitted locally. + * (DST is then determined by the current US standard rules) + * There is a table that accounts for the peculiarities + * undergone by daylight time in 1974-1975. + * + * The routine does not work + * in Saudi Arabia which runs on Solar time. + * + * asctime(tvec)) + * where tvec is produced by localtime + * returns a ptr to a character string + * that has the ascii time in the form + * Thu Jan 01 00:00:00 1970n0\\ + * 01234567890123456789012345 + * 0 1 2 + * + * ctime(t) just calls localtime, then asctime. + */ + +#include +#include +#include + +static char cbuf[26]; +static int dmsize[12] = +{ + 31, + 28, + 31, + 30, + 31, + 30, + 31, + 31, + 30, + 31, + 30, + 31 +}; + +/* + * The following table is used for 1974 and 1975 and + * gives the day number of the first day after the Sunday of the + * change. + */ +static struct { + int daylb; + int dayle; +} daytab[] = { + 5, 333, /* 1974: Jan 6 - last Sun. in Nov */ + 58, 303, /* 1975: Last Sun. in Feb - last Sun in Oct */ +}; + +struct tm *gmtime(); +char *ct_numb(); +struct tm *localtime(); +char *ctime(); +char *ct_num(); +char *asctime(); + +char * +ctime(t) +long *t; +{ + return(asctime(localtime(t))); +} + +struct tm * +localtime(tim) +long *tim; +{ + register int dayno; + register struct tm *ct; + register daylbegin, daylend; + long copyt; + struct timeb systime; + + ftime(&systime); + copyt = *tim - (long)systime.timezone*60; + ct = gmtime(©t); + dayno = ct->tm_yday; + daylbegin = 119; /* last Sun in Apr */ + daylend = 303; /* Last Sun in Oct */ + if (ct->tm_year==74 || ct->tm_year==75) { + daylbegin = daytab[ct->tm_year-74].daylb; + daylend = daytab[ct->tm_year-74].dayle; + } + daylbegin = sunday(ct, daylbegin); + daylend = sunday(ct, daylend); + if (systime.dstflag && + (dayno>daylbegin || (dayno==daylbegin && ct->tm_hour>=2)) && + (daynotm_hour<1))) { + copyt += 1*60*60; + ct = gmtime(©t); + ct->tm_isdst++; + } + return(ct); +} + +/* + * The argument is a 0-origin day number. + * The value is the day number of the first + * Sunday on or after the day. + */ +static +sunday(t, d) +register struct tm *t; +register int d; +{ + if (d >= 58) + d += dysize(t->tm_year) - 365; + return(d - (d - t->tm_yday + t->tm_wday + 700) % 7); +} + +struct tm * +gmtime(tim) +long *tim; +{ + register int d0, d1; + long hms, day; + register int *tp; + static struct tm xtime; + + /* + * break initial number into days + */ + hms = *tim % 86400; + day = *tim / 86400; + if (hms<0) { + hms += 86400; + day -= 1; + } + tp = (int *)&xtime; + + /* + * generate hours:minutes:seconds + */ + *tp++ = hms%60; + d1 = hms/60; + *tp++ = d1%60; + d1 /= 60; + *tp++ = d1; + + /* + * day is the day number. + * generate day of the week. + * The addend is 4 mod 7 (1/1/1970 was Thursday) + */ + + xtime.tm_wday = (day+7340036)%7; + + /* + * year number + */ + if (day>=0) for(d1=70; day >= dysize(d1); d1++) + day -= dysize(d1); + else for (d1=70; day<0; d1--) + day += dysize(d1-1); + xtime.tm_year = d1; + xtime.tm_yday = d0 = day; + + /* + * generate month + */ + + if (dysize(d1)==366) + dmsize[1] = 29; + for(d1=0; d0 >= dmsize[d1]; d1++) + d0 -= dmsize[d1]; + dmsize[1] = 28; + *tp++ = d0+1; + *tp++ = d1; + xtime.tm_isdst = 0; + return(&xtime); +} + +char * +asctime(t) +struct tm *t; +{ + register char *cp, *ncp; + register int *tp; + + cp = cbuf; + for (ncp = "Day Mon 00 00:00:00 1900\n"; *cp++ = *ncp++;); + ncp = &"SunMonTueWedThuFriSat"[3*t->tm_wday]; + cp = cbuf; + *cp++ = *ncp++; + *cp++ = *ncp++; + *cp++ = *ncp++; + cp++; + tp = &t->tm_mon; + ncp = &"JanFebMarAprMayJunJulAugSepOctNovDec"[(*tp)*3]; + *cp++ = *ncp++; + *cp++ = *ncp++; + *cp++ = *ncp++; + cp = ct_numb(cp, *--tp); + cp = ct_numb(cp, *--tp+100); + cp = ct_numb(cp, *--tp+100); + cp = ct_numb(cp, *--tp+100); + if (t->tm_year>=100) { + cp[1] = '2'; + cp[2] = '0'; + } + cp += 2; + cp = ct_numb(cp, t->tm_year+100); + return(cbuf); +} + +dysize(y) +{ + if((y%4) == 0) + return(366); + return(365); +} + +static char * +ct_numb(cp, n) +register char *cp; +{ + cp++; + if (n>=10) + *cp++ = (n/10)%10 + '0'; + else + *cp++ = ' '; + *cp++ = n%10 + '0'; + return(cp); +} diff --git a/time/orig/date.c b/time/orig/date.c new file mode 100644 index 0000000..a005765 --- /dev/null +++ b/time/orig/date.c @@ -0,0 +1,163 @@ +/* + * date : print date + * date YYMMDDHHMM[.SS] : set date, if allowed + * date -u ... : date in GMT + */ +#include +#include +#include +#include +long timbuf; +char *ap, *ep, *sp; +int uflag; + +char *timezone(); +static int dmsize[12] = +{ + 31, + 28, + 31, + 30, + 31, + 30, + 31, + 31, + 30, + 31, + 30, + 31 +}; + +struct utmp wtmp[2] = { {"|", "", 0}, {"{", "", 0}}; + +char *ctime(); +char *asctime(); +struct tm *localtime(); +struct tm *gmtime(); + +main(argc, argv) +char *argv[]; +{ + register char *tzn; + struct timeb info; + int wf, rc; + + rc = 0; + ftime(&info); + if (argc>1 && argv[1][0]=='-' && argv[1][1]=='u') { + argc--; + argv++; + uflag++; + } + if(argc > 1) { + ap = argv[1]; + if (gtime()) { + printf("date: bad conversion\n"); + exit(1); + } + /* convert to GMT assuming local time */ + if (uflag==0) { + timbuf += (long)info.timezone*60; + /* now fix up local daylight time */ + if(localtime(&timbuf)->tm_isdst) + timbuf -= 60*60; + } + time(&wtmp[0].ut_time); + if(stime(&timbuf) < 0) { + rc++; + printf("date: no permission\n"); + } else if ((wf = open("/usr/adm/wtmp", 1)) >= 0) { + time(&wtmp[1].ut_time); + lseek(wf, 0L, 2); + write(wf, (char *)wtmp, sizeof(wtmp)); + close(wf); + } + } + if (rc==0) + time(&timbuf); + if(uflag) { + ap = asctime(gmtime(&timbuf)); + tzn = "GMT"; + } else { + struct tm *tp; + tp = localtime(&timbuf); + ap = asctime(tp); + tzn = timezone(info.timezone, tp->tm_isdst); + } + printf("%.20s", ap); + if (tzn) + printf("%s", tzn); + printf("%s", ap+19); + exit(rc); +} + +gtime() +{ + register int i, year, month; + int day, hour, mins, secs; + struct tm *L; + char x; + + ep=ap; + while(*ep) ep++; + sp=ap; + while(sptm_mday); + month = gp(L->tm_mon+1); + year = gp(L->tm_year); + if(*sp) + return(1); + if( month<1 || month>12 || + day<1 || day>31 || + mins<0 || mins>59 || + secs<0 || secs>59) + return(1); + if (hour==24) { + hour=0; day++; + } + if (hour<0 || hour>23) + return(1); + timbuf = 0; + year += 1900; + for(i=1970; i= 3) + timbuf++; + while(--month) + timbuf += dmsize[month-1]; + timbuf += day-1; + timbuf = 24*timbuf + hour; + timbuf = 60*timbuf + mins; + timbuf = 60*timbuf + secs; + return(0); + +} + +gp(dfault) +{ + register int c, d; + + if(*sp==0) + return(dfault); + c = (*sp++)-'0'; + d = (*sp ? (*sp++)-'0' : 0); + if(c<0 || c>9 || d<0 || d>9) + return(-1); + return(c+10*d); +} diff --git a/time/orig/timezone.c b/time/orig/timezone.c new file mode 100644 index 0000000..c80ddd8 --- /dev/null +++ b/time/orig/timezone.c @@ -0,0 +1,44 @@ +/* + * The arguments are the number of minutes of time + * you are westward from Greenwich and whether DST is in effect. + * It returns a string + * giving the name of the local timezone. + * + * Sorry, I don't know all the names. + */ + +static struct zone { + int offset; + char *stdzone; + char *dlzone; +} zonetab[] = { + 4*60, "AST", "ADT", /* Atlantic */ + 5*60, "EST", "EDT", /* Eastern */ + 6*60, "CST", "CDT", /* Central */ + 7*60, "MST", "MDT", /* Mountain */ + 8*60, "PST", "PDT", /* Pacific */ + 0, "GMT", 0, /* Greenwich */ + -1 +}; + +char *timezone(zone, dst) +{ + register struct zone *zp; + static char czone[10]; + char *sign; + + for (zp=zonetab; zp->offset!=-1; zp++) + if (zp->offset==zone) { + if (dst && zp->dlzone) + return(zp->dlzone); + if (!dst && zp->stdzone) + return(zp->stdzone); + } + if (zone<0) { + zone = -zone; + sign = "+"; + } else + sign = "-"; + sprintf(czone, "GMT%s%d:%02d", sign, zone/60, zone%60); + return(czone); +} diff --git a/time/test/README.md b/time/test/README.md new file mode 100644 index 0000000..fec5bc8 --- /dev/null +++ b/time/test/README.md @@ -0,0 +1,7 @@ +# Testing the _dysize_ routine + +I've been messing with installing the 'correct' code for the _dysize_ function that returns the number of days in a year. The routine is called with two possible arguments: the full four digit year and also the year as a two digit value. + +There are two possible algorithms. The original: take the value and if it's divisible by 4, then it's a leap year, and the 'correct' (see [Century Leap Year](https://en.wikipedia.org/wiki/Century_leap_year) algorithm: if the year is exactly divisible by 4 but not by 100 or it's exactly divisible by 400. + +The code in _leaptest.c_ generates a table from 1970 to diff --git a/time/test/leaptest.c b/time/test/leaptest.c new file mode 100644 index 0000000..49a9f64 --- /dev/null +++ b/time/test/leaptest.c @@ -0,0 +1,48 @@ +#include + +dysize(y) +int y; +{ + /* called internally */ + if (y < 500) y += 1900; + + if (((y%4) == 0 && (y%100) != 0) || (y%400) == 0) + return(366); + return(365); +} + +odysize(y) +int y; +{ + if((y%4) == 0) + return(366); + return(365); +} + +isleap(n) +int n; +{ + if (n == 366) return 'Y'; + return 'N'; +} + +main() +{ + int yr, havey; + int lp, lfp, olp, olfp; + + for (yr = 70; yr <= 170; yr++) { + havey = 0; + lp = isleap(dysize(yr)); + if (lp == 'Y') havey++; + lfp = isleap(dysize(yr + 1900)); + if (lfp == 'Y') havey++; + olp = isleap(odysize(yr)); + if (olp == 'Y') havey++; + olfp = isleap(odysize(yr + 1900)); + if (olfp == 'Y') havey++; + if (havey) + printf("%4d\t%d\t%c\t%c\t%c\t%c\n", + yr, yr+1900, lp, lfp, olp, olfp); + } +} diff --git a/time/test/makefile b/time/test/makefile new file mode 100644 index 0000000..c71b164 --- /dev/null +++ b/time/test/makefile @@ -0,0 +1,9 @@ +# Make leaptest + +all: leaptest + +leaptest: leaptest.c + cc -O -o leaptest leaptest.c + +clean: + rm leaptest diff --git a/time/timezone.c b/time/timezone.c new file mode 100644 index 0000000..de1ddb1 --- /dev/null +++ b/time/timezone.c @@ -0,0 +1,44 @@ +/* + * The arguments are the number of minutes of time + * you are westward from Greenwich and whether DST is in effect. + * It returns a string + * giving the name of the local timezone. + * + * Sorry, I don't know all the names. + */ + +static struct zone { + int offset; + char *stdzone; + char *dlzone; +} zonetab[] = { + 4*60, "AST", "ADT", /* Atlantic */ + 5*60, "EST", "EDT", /* Eastern */ + 6*60, "CST", "CDT", /* Central */ + 7*60, "MST", "MDT", /* Mountain */ + 8*60, "PST", "PDT", /* Pacific */ + 0, "GMT", "BST", /* Greenwich */ + -1 +}; + +char *timezone(zone, dst) +{ + register struct zone *zp; + static char czone[10]; + char *sign; + + for (zp=zonetab; zp->offset!=-1; zp++) + if (zp->offset==zone) { + if (dst && zp->dlzone) + return(zp->dlzone); + if (!dst && zp->stdzone) + return(zp->stdzone); + } + if (zone<0) { + zone = -zone; + sign = "+"; + } else + sign = "-"; + sprintf(czone, "GMT%s%d:%02d", sign, zone/60, zone%60); + return(czone); +} diff --git a/time/tp/README.md b/time/tp/README.md new file mode 100644 index 0000000..5754919 --- /dev/null +++ b/time/tp/README.md @@ -0,0 +1,9 @@ +# Small change to the _tp_ command + +The _tp_ command uses _localtime_ to get a formatted date string, but needs changing to understand that year values greater than or equal to 100 should be interpreted in the 21st century and should have 100 removed from the value. This change is in _tp3.c_. + +## Compiling + +This should be done after the library is updated. + +Type ```make``` to compile and ```make install``` to install. diff --git a/time/tp/install b/time/tp/install new file mode 100644 index 0000000..69b1aa4 --- /dev/null +++ b/time/tp/install @@ -0,0 +1,27 @@ +: Install new tp command +DEST=/bin +if [ ! -f tp ]; then + make tp +fi +if [ ! -d $DEST/orig ]; then + mkdir $DEST + if [ ! -d $DEST/orig ]; then + echo "Failed to create $DEST/orig" + exit 1 + fi +fi +if [ ! -f $DEST/orig/tp ]; then + echo "Backing up $DEST/tp to $DEST/orig" + mv $DEST/tp $DEST/orig/tp + if [ ! -f $DEST/orig/tp ]; then + echo "Failed to create $DEST/orig" + exit 1 + fi +fi +echo "Installing tp" +cp tp $DEST +if [ ! -f $DEST/tp ]; then + echo "Installation failed" + exit 1 +fi +exit 0 diff --git a/time/tp/makefile b/time/tp/makefile new file mode 100644 index 0000000..f9dd2e1 --- /dev/null +++ b/time/tp/makefile @@ -0,0 +1,15 @@ +CFLAGS=-s -O + +all: tp + rm *.o + +install: tp /bin/tp + sh install + +tp: tp0.c tp1.o tp2.o tp3.o + cc $(CFLAGS) -n tp0.c tp1.o tp2.o tp3.o -o tp + +tp0.c tp1.c tp2.c tp3.c: tp.h + +clean: + rm *.o diff --git a/time/tp/orig/makefile b/time/tp/orig/makefile new file mode 100644 index 0000000..296ef1e --- /dev/null +++ b/time/tp/orig/makefile @@ -0,0 +1,17 @@ +CFLAGS=-n -s -O + +all: tp + rm *.o + +cp: tp + cp tp /bin/tp + rm *.o tp + +cmp: tp + cmp tp /bin/tp + rm *.o tp + +tp: tp0.o tp1.o tp2.o tp3.o + cc $(CFLAGS) tp0.o tp1.o tp2.o tp3.o -o tp + +tp0.c tp1.c tp2.c tp3.c: tp.h diff --git a/time/tp/orig/tp.h b/time/tp/orig/tp.h new file mode 100644 index 0000000..a3a29d5 --- /dev/null +++ b/time/tp/orig/tp.h @@ -0,0 +1,85 @@ +/* c-version of tp?.s + * + * M. Ferentz + * August 1976 + * + * revised July 1977 BTL + */ + +#define MDIRENT 496 /* must be zero mod 8 */ +#define DIRSZ sizeof(struct dent) +#define MAPSIZE 4096 +#define MAPMASK 07777 +#define NAMELEN 32 +#define BSIZE 512 +#define TCSIZ 578 +#define TCDIRS 192 +#define MTSIZ 32767 +#define TPB (BSIZE/sizeof(struct tent)) +#define OK 0100000 +#define BRKINCR 512 + +#define tapeblk &tpentry[0] +#define tapeb &tpentry[0] + +struct tent { /* Structure of a tape directory block */ + char pathnam[NAMELEN]; + short mode; + char uid; + char gid; + char spare; + char size0; + unsigned short size1; + long time; + unsigned short tapea; /* tape address */ + short unused[8]; + short cksum; +} tpentry[TPB]; + +struct dent { /* in core version of tent with "unused" removed + * and pathname replaced by pointer to same in a + * packed area (nameblock). + */ + char *d_namep; + int d_mode; + int d_uid; + int d_gid; + long d_size; + long d_time; + int d_tapea; +} dir[MDIRENT]; + +char map[MAPSIZE]; +char name[NAMELEN]; +char name1[NAMELEN]; +extern char mt[]; +extern char tc[]; +char *tname; +extern char mheader[]; +extern char theader[]; + +int narg, rnarg; +char **parg; +int wseeka,rseeka; +int tapsiz; +int fio; +short ndirent, ndentb; +struct dent *edir; +struct dent *lastd; /* for improvement */ +char *sbrk(); +char *strcpy(); +long lseek(); +int (*command)(); + +char *nameblk; +char *top; +char *nptr; + +extern int flags; +#define flc 0001 +#define fli 0004 +#define flm 0010 +#define flu 0020 +#define flv 0040 +#define flw 0100 +#define fls 0200 diff --git a/time/tp/orig/tp0.c b/time/tp/orig/tp0.c new file mode 100644 index 0000000..04a5c21 --- /dev/null +++ b/time/tp/orig/tp0.c @@ -0,0 +1,2 @@ +#include "tp.h" +#include diff --git a/time/tp/orig/tp1.c b/time/tp/orig/tp1.c new file mode 100644 index 0000000..5a6c8fd --- /dev/null +++ b/time/tp/orig/tp1.c @@ -0,0 +1,195 @@ +#include "tp.h" + +main(argc,argv) +char **argv; +{ + register char c,*ptr; + extern cmd(), cmr(),cmx(), cmt(); + + tname = tc; + command = cmr; + if ((narg = rnarg = argc) < 2) narg = 2; + else { + ptr = argv[1]; /* get first argument */ + parg = &argv[2]; /* pointer to second argument */ + while (c = *ptr++) switch(c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + tc[8] = c; + mt[7] = c; + continue; + + case 'f': + tname = *parg++; + narg--; rnarg--; + continue; + case 'c': + flags |= flc; continue; + case 'd': + setcom(cmd); continue; + case 'i': + flags |= fli; continue; + case 'm': + tname = mt; + flags |= flm; + continue; + case 'r': + flags &= ~flu; setcom(cmr); continue; + case 's': + flags |= fls; continue; + case 't': + setcom(cmt); continue; + case 'u': + flags |= flu; setcom(cmr); continue; + case 'v': + flags |= flv; continue; + case 'w': + flags |= flw; continue; + case 'x': + setcom(cmx); continue; + default: + useerr(); + } + } + optap(); + top = nptr = nameblk = sbrk(0); + (*command)(); +} + +optap() +{ + extern cmr(); + + if ((flags & flm) == 0) { /* DECTAPE */ + tapsiz = TCSIZ; + ndirent = TCDIRS; + fio =open(tc,2); + } else { /* MAGTAPE */ + tapsiz = MTSIZ; + ndirent = MDIRENT; + if(command == cmr) + fio = open(tname,1); + else + fio = open(tname,0); + } + if (fio < 0) { + printf("Tape open error\n"); + done(); + } + ndentb = ndirent/TPB; + edir = &dir[ndirent]; +} + +setcom(newcom) +int (*newcom)(); +{ + extern cmr(); + + if (command != cmr) useerr(); + command = newcom; +} + +useerr() +{ + printf("Bad usage\n"); + done(); +} + +/* /* COMMANDS */ + +cmd() +{ + extern delete(); + + if (flags & (flm|flc)) useerr(); + if (narg <= 2) useerr(); + rddir(); + gettape(delete); + wrdir(); + check(); +} + +cmr() +{ + if (flags & (flc|flm)) clrdir(); + else rddir(); + getfiles(); + update(); + check(); +} + +cmt() +{ + extern taboc(); + + if (flags & (flc|flw)) useerr(); + rddir(); + if (flags & flv) + printf(" mode uid gid tapa size date time name\n"); + gettape(taboc); + check(); +} + +cmx() +{ + extern extract(); + + if (flags & (flc)) useerr(); + rddir(); + gettape(extract); + done(); +} + +check() +{ + usage(); + done(); +} + +done() +{ + printf("End\n"); + exit(0); +} + +encode(pname,dptr) /* pname points to the pathname + * nptr points to next location in nameblk + * dptr points to the dir entry */ +char *pname; +struct dent *dptr; +{ + register char *np; + register n; + + dptr->d_namep = np = nptr; + if (np > top - NAMELEN) { + if(sbrk(BRKINCR) == (char *)-1) { + printf("Out of core\n"); + done(); + } else + top += BRKINCR; + } + if((n=strlen(pname)) > NAMELEN) { + printf("Pathname too long - %s\nFile ignored\n",pname); + clrent(dptr); + } + else { + nptr += n+1; + strcpy(np, pname); + } +} + +decode(pname,dptr) /* dptr points to the dir entry + * name is placed in pname[] */ +char *pname; +struct dent *dptr; +{ + + strcpy(pname, dptr->d_namep); +} diff --git a/time/tp/orig/tp2.c b/time/tp/orig/tp2.c new file mode 100644 index 0000000..72b7090 --- /dev/null +++ b/time/tp/orig/tp2.c @@ -0,0 +1,347 @@ +#include "tp.h" +#include +#include +#include +#include + +struct direct direct; +struct stat statb; + +clrdir() +{ + register j, *p; + + j = ndirent * (DIRSZ/sizeof(int)); + p = (int *)dir; + do (*p++ = 0); while (--j); + lastd = 0; +} + +clrent(ptr) +struct dent *ptr; +{ + register *p, j; + + p = (int *)ptr; + j = DIRSZ/sizeof(int); + do *p++ = 0; + while (--j); + if (++ptr == lastd) do { + if (--lastd < dir) { + lastd = 0; + return; + } + } while (lastd->d_namep == 0); +} + + +rddir() +{ + register struct tent *tp; + register struct dent *p1; + struct dent *dptr; + struct tent *tptr; + int count, i, sum; + short reg, *sp; + + sum = 0; + clrdir(); + rseek(0); + tread(); /* Read the bootstrap block */ + if ((tpentry[TPB-1].cksum != 0) && (flags & flm)) { + ndirent = tpentry[TPB-1].cksum; + if(flags & fls) swab((char *)&ndirent, (char *)&ndirent, sizeof(ndirent)); + if(ndirent < 0 || ndirent > MDIRENT) ndirent = MDIRENT; + ndentb = ndirent/TPB; + } + dptr = &dir[0]; + count = ndirent; + do { + if ((count % TPB) == 0) { /* next block */ + tread(); + tptr = &tpentry[0]; + } + if(flags & fls) + swab((char *)tptr, (char *)tptr, sizeof(*tptr)); + sp = (short *)tptr; + reg = 0; + for(i=0;ipathnam[0] != '\0') { + lastd = p1; + encode(tp->pathnam,p1); + p1->d_mode = tp->mode; + p1->d_uid = tp->uid; + p1->d_gid = tp->gid; + p1->d_size = (((long)tp->size0&0377L)<<16)+(tp->size1&0177777L); + p1->d_time = tp->time; + p1->d_tapea = tp->tapea; + } + } + ++tptr; /* bump to next tent */ + (dptr++)->d_mode &= ~OK; + } while (--count); + if(sum != 0) + if(flags & (fls|fli)) { + printf("Directory checksum\n"); + if ((flags & fli) == 0) done(); + } else { + flags |= fls; + rddir(); + printf("Warning: swabbing required\n"); + return; + } + bitmap(); +} + + +wrdir() +{ + register struct tent *tp; + register struct dent *dp; + struct dent *dptr; + int count, i; + short reg, *sp; + + wseek(0); + if (flags & flm) + reg = open(mheader,0); + else reg = open(theader,0); + if (reg >= 0) { + read(reg,(char *)tapeb,BSIZE); + close(reg); + if(flags & fls) + swab((char *)&ndirent, (char *)&tpentry[TPB-1].cksum, sizeof(ndirent)); + else + tpentry[TPB-1].cksum = ndirent; + } + dptr = &dir[0]; + count = ndirent; + for (;;) { + twrite(); + if (count == 0) return; + tp = &tpentry[0]; + do { + dp = dptr++; /* dptr set to next entry */ + if (dp->d_namep) { + decode(tp->pathnam,dp); + tp->mode = dp->d_mode; + tp->uid = dp->d_uid; + tp->gid = dp->d_gid; + tp->time = dp->d_time; + tp->size0 = dp->d_size >> 16; + tp->size1 = dp->d_size; + tp->tapea = dp->d_tapea; + if(flags & fls) { + swabdir(tp); + swab((char *)tp, (char *)tp, sizeof(*tp)); + } + reg = 0; + sp = (short *)tp; + for(i=0;i 25 && b) { + lseek(fio, (long)(b-1)*BSIZE, 0); /* seek previous block */ + read(fio, (char *)&wseeka, 1); /* read next block */ + } + wseeka = b; + if (lseek(fio, (long)b*BSIZE, 0) < 0) seekerr(); +} + +seekerr() +{ + printf("Tape seek error\n"); + done(); +} + +verify(key) +{ + register c; + + if ((flags & (flw | flv)) == 0) + return(0); +repeat: printf("%c %s ", key, name); + if ((flags & flw) == 0) { + printf("\n"); + return(0); + } + c = getchar(); + if (c == 'n' && getchar() == '\n') + done(); + if (c == '\n') + return(-1); + if (c == 'y' && getchar() == '\n') + return(0); + while (getchar() != '\n'); + goto repeat; +} + +getfiles() +{ + + if ((narg -= 2) == 0) { + strcpy(name, "."); + callout(); + } else while (--narg >= 0) { + strcpy(name, *parg++); + callout(); + } +} + + +expand() +{ + register char *p0, *save0; + int n, fid; + + if ((fid = open(name,0)) < 0) fserr(); + for (;;) { + if ((n = read(fid, (char *)&direct, sizeof(direct))) != sizeof(direct)) { + if (n == 0) { + close(fid); + return; + } + fserr(); + } + if (direct.d_ino == 0) /* null entry */ + continue; + p0 = name; + if (direct.d_name[0] == '.') /* don't save .xxxx */ + continue; + while (*p0++); + save0 = --p0; /* save loc of \0 */ + if (p0[-1] != '/') + *p0++ = '/'; + strcpy(p0, direct.d_name); + callout(); + *save0 = 0; /* restore */ + } +} + +fserr() +{ + printf("%s -- Cannot open file\n", name); + done(); +} + +callout() +{ + register struct dent *d; + register char *ptr1, *ptr0; + struct dent *empty; + int mode; + + if (stat(name,&statb) < 0) fserr(); + mode = statb.st_mode; + if ((mode &= S_IFMT) != 0) { + if (mode == S_IFDIR) /* directory */ + expand(); + if(mode != S_IFREG) return; + } + /* when we reach here we have recursed until we found + * an ordinary file. Now we look for it in "dir". + */ + empty = 0; + d = &dir[0]; + do { + if (d->d_namep == 0) { /* empty directory slot */ + if (empty == 0) /* remember the first one */ + empty = d; + continue; + } + decode(name1,d); + ptr0 = name; + ptr1 = name1; + do if (*ptr0++ != *ptr1) goto cont; + while (*ptr1++); + /* veritably the same name */ + if (flags & flu) { /* check the times */ + if (d->d_time >= statb.st_mtime) + return; + } + if (verify('r') < 0) return; + goto copydir; +cont: continue; + } while (++d <= lastd); + /* name not found in directory */ + if ((d = empty) == 0) { + d = lastd +1; + if (d >= edir) { + printf("Directory overflow\n"); + done(); + } + } + if (verify('a') < 0) return; + if (d > lastd) lastd = d; + encode(name,d); +copydir: + d->d_mode = statb.st_mode | OK; + d->d_uid = statb.st_uid; + d->d_gid = statb.st_gid; + d->d_size = statb.st_size; + d->d_time = statb.st_mtime; +} + +swabdir(tp) +register struct tent *tp; +{ + swab((char *)tp, (char *)tp, sizeof(*tp)); + swab(tp->pathnam, tp->pathnam, NAMELEN); + swab((char *)&tp->uid, (char *)&tp->uid, 4); /* uid,gid,spare,size0 */ +} diff --git a/time/tp/orig/tp3.c b/time/tp/orig/tp3.c new file mode 100644 index 0000000..c754ae6 --- /dev/null +++ b/time/tp/orig/tp3.c @@ -0,0 +1,248 @@ +#include "tp.h" + +gettape(how) +int (*how)(); +{ + register char *ptr0, *ptr1; + register struct dent *d; + int count; + + do { + d = &dir[0]; + count = 0; + do { + if (d->d_namep == 0) continue; + decode(name,d); + if (rnarg > 2) { + ptr0 = name; + ptr1 = *parg; + while (*ptr1) + if (*ptr0++ != *ptr1++) goto cont; + if (*ptr0 && *ptr0 != '/') goto cont; + } + (*how)(d); /* delete, extract, or taboc */ + ++count; +cont: continue; + } while (++d <= lastd); + if (count == 0 && rnarg > 2) + printf("%s not found\n", *parg); + ++parg; + } while (--narg > 2); +} + +delete(dd) +struct dent *dd; +{ + if (verify('d') >= 0) + clrent(dd); +} + + +update() +{ + register struct dent *d; + register b, last; + int first, size; + + + bitmap(); + d = &dir[0]; + do { + if(d->d_namep == 0 || (d->d_mode&OK) == 0) continue; + if (d->d_size == 0) continue; +/* find a place on the tape for this file */ + size = (d->d_size+BSIZE-1)/BSIZE; + first = ndentb; +toosmall: ++first; + if ((last = first + size) >= tapsiz) maperr(); + for (b = first; b < last; ++b) + if (map[(b>>3) & MAPMASK] & (1<<(b&7))) { + first = b; + goto toosmall; + }; + d->d_tapea = first; + setmap(d); + } while (++d <= lastd); + wrdir(); + update1(); +} + + +update1() +{ + register struct dent *d, *id; + register index; + int f; + + for (;;) { + d = &dir[0]; + index = MTSIZ; + id = 0; + do { /* find new dent with lowest tape address */ + if(d->d_namep == 0 || (d->d_mode&OK) == 0) continue; + if (d->d_tapea < index) { + index = d->d_tapea; + id = d; + } + } while (++d <= lastd); + if ((d = id) == 0) return; + d->d_mode &= ~OK; /* change from new to old */ + if (d->d_size == 0) continue; + decode(name,d); + wseek(index); + if ((f = open(name,0)) < 0) { + printf("Can't open %s\n", name); + continue; + } + for (index = d->d_size/BSIZE; index != 0; --index) { + if (read(f,(char *)tapeb,BSIZE) != BSIZE) phserr(); + twrite(); + } + if (index = d->d_size % BSIZE) { + if (read(f,(char *)tapeb,index) != index) phserr(); + twrite(); + } + if (read(f,(char *)tapeb,1) != 0) phserr(); + close(f); + } +} + +phserr() +{ printf("%s -- Phase error \n", name); } + + +bitmap() /* place old files in the map */ +{ + register char *m; + register count; + register struct dent *d; + + for(m=map;m<&map[MAPSIZE];) *m++ = 0; + count = ndirent; + d = dir; + do { + if(d->d_namep != 0 && (d->d_mode&OK) == 0 + && d->d_size != 0) setmap(d); + d++; + } while (--count); +} + +setmap(d) +register struct dent *d; +{ + unsigned c, block; + char bit; + int i; + + c = d->d_size/BSIZE; + if (d->d_size % BSIZE) c++; + block = d->d_tapea; + if ((c += block) >= tapsiz) maperr(); + do { + bit = 1 << (block & 7); + i = (block>>3) & MAPMASK; + if (bit & map[i]) maperr(); + map[i] |= bit; + } while (++block < c); +} + +maperr() +{ + printf("Tape overflow\n"); + done(); +} + + +usage() +{ + register reg,count; + int nused, nentr, nfree; + static lused; + + bitmap(); + for(count=0,nentr=0;count= tapsiz) { + printf("Tape overflow\n"); + done(); + } + if (map[(reg>>3) & MAPMASK] & (1 << (reg&7))) { + nused++; + lused = reg; + } else { + if (flags & flm) break; + nfree++; + } + reg++; + } while (--count); + printf("%4d entries\n%4d used\n", nentr, nused); + if ((flags & flm)==0) + printf("%4d free\n", nfree); + printf("%4d last\n", lused); +} + + +taboc(dd) +struct dent *dd; +{ + register mode; + register *m; + register char *s; + int count, *localtime(); + char work[20]; + + if (flags & flv) { + mode = dd->d_mode; + s = &work[19]; + *s = 0; + for (count = 3; count; --count) { + if (mode&1) *--s = 'x'; + else *--s = '-'; + if (mode&2) *--s = 'w'; + else *--s = '-'; + if (mode&4) *--s = 'r'; + else *--s = '-'; + mode >>= 3; + } + if (mode&4) s[2] = 's'; + if (mode&2) s[5] = 's'; + printf("%s%4d%4d%5d%9D ",s,dd->d_uid, dd->d_gid,dd->d_tapea,dd->d_size); + m = localtime(&dd->d_time); + printf("%2d/%2d/%2d %2d:%2d ",m[5],m[4]+1,m[3],m[2],m[1]); + } + printf("%s\n", name); +} + + +extract(d) +register struct dent *d; +{ + register count, id; + + if (d->d_size==0) return; + if (verify('x') < 0) return; + rseek(d->d_tapea); + unlink(name); + if ((id = creat(name,d->d_mode)) < 0) + printf("%s -- create error\n", name); + count = d->d_size/BSIZE; + while (count--) { + tread(); + if (write(id, (char *)tapeb, BSIZE) != BSIZE) goto ng; + } + if (count = d->d_size % BSIZE) { + tread(); + if (write(id, (char *)tapeb, count) != count) { +ng: printf("%s -- write error\n", name); + close(id); + return; + } + } + close(id); + chown(name,d->d_uid & 0377, d->d_gid&0377); +} diff --git a/time/tp/tp.h b/time/tp/tp.h new file mode 100644 index 0000000..a3a29d5 --- /dev/null +++ b/time/tp/tp.h @@ -0,0 +1,85 @@ +/* c-version of tp?.s + * + * M. Ferentz + * August 1976 + * + * revised July 1977 BTL + */ + +#define MDIRENT 496 /* must be zero mod 8 */ +#define DIRSZ sizeof(struct dent) +#define MAPSIZE 4096 +#define MAPMASK 07777 +#define NAMELEN 32 +#define BSIZE 512 +#define TCSIZ 578 +#define TCDIRS 192 +#define MTSIZ 32767 +#define TPB (BSIZE/sizeof(struct tent)) +#define OK 0100000 +#define BRKINCR 512 + +#define tapeblk &tpentry[0] +#define tapeb &tpentry[0] + +struct tent { /* Structure of a tape directory block */ + char pathnam[NAMELEN]; + short mode; + char uid; + char gid; + char spare; + char size0; + unsigned short size1; + long time; + unsigned short tapea; /* tape address */ + short unused[8]; + short cksum; +} tpentry[TPB]; + +struct dent { /* in core version of tent with "unused" removed + * and pathname replaced by pointer to same in a + * packed area (nameblock). + */ + char *d_namep; + int d_mode; + int d_uid; + int d_gid; + long d_size; + long d_time; + int d_tapea; +} dir[MDIRENT]; + +char map[MAPSIZE]; +char name[NAMELEN]; +char name1[NAMELEN]; +extern char mt[]; +extern char tc[]; +char *tname; +extern char mheader[]; +extern char theader[]; + +int narg, rnarg; +char **parg; +int wseeka,rseeka; +int tapsiz; +int fio; +short ndirent, ndentb; +struct dent *edir; +struct dent *lastd; /* for improvement */ +char *sbrk(); +char *strcpy(); +long lseek(); +int (*command)(); + +char *nameblk; +char *top; +char *nptr; + +extern int flags; +#define flc 0001 +#define fli 0004 +#define flm 0010 +#define flu 0020 +#define flv 0040 +#define flw 0100 +#define fls 0200 diff --git a/time/tp/tp0.c b/time/tp/tp0.c new file mode 100644 index 0000000..04a5c21 --- /dev/null +++ b/time/tp/tp0.c @@ -0,0 +1,2 @@ +#include "tp.h" +#include diff --git a/time/tp/tp1.c b/time/tp/tp1.c new file mode 100644 index 0000000..5a6c8fd --- /dev/null +++ b/time/tp/tp1.c @@ -0,0 +1,195 @@ +#include "tp.h" + +main(argc,argv) +char **argv; +{ + register char c,*ptr; + extern cmd(), cmr(),cmx(), cmt(); + + tname = tc; + command = cmr; + if ((narg = rnarg = argc) < 2) narg = 2; + else { + ptr = argv[1]; /* get first argument */ + parg = &argv[2]; /* pointer to second argument */ + while (c = *ptr++) switch(c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + tc[8] = c; + mt[7] = c; + continue; + + case 'f': + tname = *parg++; + narg--; rnarg--; + continue; + case 'c': + flags |= flc; continue; + case 'd': + setcom(cmd); continue; + case 'i': + flags |= fli; continue; + case 'm': + tname = mt; + flags |= flm; + continue; + case 'r': + flags &= ~flu; setcom(cmr); continue; + case 's': + flags |= fls; continue; + case 't': + setcom(cmt); continue; + case 'u': + flags |= flu; setcom(cmr); continue; + case 'v': + flags |= flv; continue; + case 'w': + flags |= flw; continue; + case 'x': + setcom(cmx); continue; + default: + useerr(); + } + } + optap(); + top = nptr = nameblk = sbrk(0); + (*command)(); +} + +optap() +{ + extern cmr(); + + if ((flags & flm) == 0) { /* DECTAPE */ + tapsiz = TCSIZ; + ndirent = TCDIRS; + fio =open(tc,2); + } else { /* MAGTAPE */ + tapsiz = MTSIZ; + ndirent = MDIRENT; + if(command == cmr) + fio = open(tname,1); + else + fio = open(tname,0); + } + if (fio < 0) { + printf("Tape open error\n"); + done(); + } + ndentb = ndirent/TPB; + edir = &dir[ndirent]; +} + +setcom(newcom) +int (*newcom)(); +{ + extern cmr(); + + if (command != cmr) useerr(); + command = newcom; +} + +useerr() +{ + printf("Bad usage\n"); + done(); +} + +/* /* COMMANDS */ + +cmd() +{ + extern delete(); + + if (flags & (flm|flc)) useerr(); + if (narg <= 2) useerr(); + rddir(); + gettape(delete); + wrdir(); + check(); +} + +cmr() +{ + if (flags & (flc|flm)) clrdir(); + else rddir(); + getfiles(); + update(); + check(); +} + +cmt() +{ + extern taboc(); + + if (flags & (flc|flw)) useerr(); + rddir(); + if (flags & flv) + printf(" mode uid gid tapa size date time name\n"); + gettape(taboc); + check(); +} + +cmx() +{ + extern extract(); + + if (flags & (flc)) useerr(); + rddir(); + gettape(extract); + done(); +} + +check() +{ + usage(); + done(); +} + +done() +{ + printf("End\n"); + exit(0); +} + +encode(pname,dptr) /* pname points to the pathname + * nptr points to next location in nameblk + * dptr points to the dir entry */ +char *pname; +struct dent *dptr; +{ + register char *np; + register n; + + dptr->d_namep = np = nptr; + if (np > top - NAMELEN) { + if(sbrk(BRKINCR) == (char *)-1) { + printf("Out of core\n"); + done(); + } else + top += BRKINCR; + } + if((n=strlen(pname)) > NAMELEN) { + printf("Pathname too long - %s\nFile ignored\n",pname); + clrent(dptr); + } + else { + nptr += n+1; + strcpy(np, pname); + } +} + +decode(pname,dptr) /* dptr points to the dir entry + * name is placed in pname[] */ +char *pname; +struct dent *dptr; +{ + + strcpy(pname, dptr->d_namep); +} diff --git a/time/tp/tp2.c b/time/tp/tp2.c new file mode 100644 index 0000000..72b7090 --- /dev/null +++ b/time/tp/tp2.c @@ -0,0 +1,347 @@ +#include "tp.h" +#include +#include +#include +#include + +struct direct direct; +struct stat statb; + +clrdir() +{ + register j, *p; + + j = ndirent * (DIRSZ/sizeof(int)); + p = (int *)dir; + do (*p++ = 0); while (--j); + lastd = 0; +} + +clrent(ptr) +struct dent *ptr; +{ + register *p, j; + + p = (int *)ptr; + j = DIRSZ/sizeof(int); + do *p++ = 0; + while (--j); + if (++ptr == lastd) do { + if (--lastd < dir) { + lastd = 0; + return; + } + } while (lastd->d_namep == 0); +} + + +rddir() +{ + register struct tent *tp; + register struct dent *p1; + struct dent *dptr; + struct tent *tptr; + int count, i, sum; + short reg, *sp; + + sum = 0; + clrdir(); + rseek(0); + tread(); /* Read the bootstrap block */ + if ((tpentry[TPB-1].cksum != 0) && (flags & flm)) { + ndirent = tpentry[TPB-1].cksum; + if(flags & fls) swab((char *)&ndirent, (char *)&ndirent, sizeof(ndirent)); + if(ndirent < 0 || ndirent > MDIRENT) ndirent = MDIRENT; + ndentb = ndirent/TPB; + } + dptr = &dir[0]; + count = ndirent; + do { + if ((count % TPB) == 0) { /* next block */ + tread(); + tptr = &tpentry[0]; + } + if(flags & fls) + swab((char *)tptr, (char *)tptr, sizeof(*tptr)); + sp = (short *)tptr; + reg = 0; + for(i=0;ipathnam[0] != '\0') { + lastd = p1; + encode(tp->pathnam,p1); + p1->d_mode = tp->mode; + p1->d_uid = tp->uid; + p1->d_gid = tp->gid; + p1->d_size = (((long)tp->size0&0377L)<<16)+(tp->size1&0177777L); + p1->d_time = tp->time; + p1->d_tapea = tp->tapea; + } + } + ++tptr; /* bump to next tent */ + (dptr++)->d_mode &= ~OK; + } while (--count); + if(sum != 0) + if(flags & (fls|fli)) { + printf("Directory checksum\n"); + if ((flags & fli) == 0) done(); + } else { + flags |= fls; + rddir(); + printf("Warning: swabbing required\n"); + return; + } + bitmap(); +} + + +wrdir() +{ + register struct tent *tp; + register struct dent *dp; + struct dent *dptr; + int count, i; + short reg, *sp; + + wseek(0); + if (flags & flm) + reg = open(mheader,0); + else reg = open(theader,0); + if (reg >= 0) { + read(reg,(char *)tapeb,BSIZE); + close(reg); + if(flags & fls) + swab((char *)&ndirent, (char *)&tpentry[TPB-1].cksum, sizeof(ndirent)); + else + tpentry[TPB-1].cksum = ndirent; + } + dptr = &dir[0]; + count = ndirent; + for (;;) { + twrite(); + if (count == 0) return; + tp = &tpentry[0]; + do { + dp = dptr++; /* dptr set to next entry */ + if (dp->d_namep) { + decode(tp->pathnam,dp); + tp->mode = dp->d_mode; + tp->uid = dp->d_uid; + tp->gid = dp->d_gid; + tp->time = dp->d_time; + tp->size0 = dp->d_size >> 16; + tp->size1 = dp->d_size; + tp->tapea = dp->d_tapea; + if(flags & fls) { + swabdir(tp); + swab((char *)tp, (char *)tp, sizeof(*tp)); + } + reg = 0; + sp = (short *)tp; + for(i=0;i 25 && b) { + lseek(fio, (long)(b-1)*BSIZE, 0); /* seek previous block */ + read(fio, (char *)&wseeka, 1); /* read next block */ + } + wseeka = b; + if (lseek(fio, (long)b*BSIZE, 0) < 0) seekerr(); +} + +seekerr() +{ + printf("Tape seek error\n"); + done(); +} + +verify(key) +{ + register c; + + if ((flags & (flw | flv)) == 0) + return(0); +repeat: printf("%c %s ", key, name); + if ((flags & flw) == 0) { + printf("\n"); + return(0); + } + c = getchar(); + if (c == 'n' && getchar() == '\n') + done(); + if (c == '\n') + return(-1); + if (c == 'y' && getchar() == '\n') + return(0); + while (getchar() != '\n'); + goto repeat; +} + +getfiles() +{ + + if ((narg -= 2) == 0) { + strcpy(name, "."); + callout(); + } else while (--narg >= 0) { + strcpy(name, *parg++); + callout(); + } +} + + +expand() +{ + register char *p0, *save0; + int n, fid; + + if ((fid = open(name,0)) < 0) fserr(); + for (;;) { + if ((n = read(fid, (char *)&direct, sizeof(direct))) != sizeof(direct)) { + if (n == 0) { + close(fid); + return; + } + fserr(); + } + if (direct.d_ino == 0) /* null entry */ + continue; + p0 = name; + if (direct.d_name[0] == '.') /* don't save .xxxx */ + continue; + while (*p0++); + save0 = --p0; /* save loc of \0 */ + if (p0[-1] != '/') + *p0++ = '/'; + strcpy(p0, direct.d_name); + callout(); + *save0 = 0; /* restore */ + } +} + +fserr() +{ + printf("%s -- Cannot open file\n", name); + done(); +} + +callout() +{ + register struct dent *d; + register char *ptr1, *ptr0; + struct dent *empty; + int mode; + + if (stat(name,&statb) < 0) fserr(); + mode = statb.st_mode; + if ((mode &= S_IFMT) != 0) { + if (mode == S_IFDIR) /* directory */ + expand(); + if(mode != S_IFREG) return; + } + /* when we reach here we have recursed until we found + * an ordinary file. Now we look for it in "dir". + */ + empty = 0; + d = &dir[0]; + do { + if (d->d_namep == 0) { /* empty directory slot */ + if (empty == 0) /* remember the first one */ + empty = d; + continue; + } + decode(name1,d); + ptr0 = name; + ptr1 = name1; + do if (*ptr0++ != *ptr1) goto cont; + while (*ptr1++); + /* veritably the same name */ + if (flags & flu) { /* check the times */ + if (d->d_time >= statb.st_mtime) + return; + } + if (verify('r') < 0) return; + goto copydir; +cont: continue; + } while (++d <= lastd); + /* name not found in directory */ + if ((d = empty) == 0) { + d = lastd +1; + if (d >= edir) { + printf("Directory overflow\n"); + done(); + } + } + if (verify('a') < 0) return; + if (d > lastd) lastd = d; + encode(name,d); +copydir: + d->d_mode = statb.st_mode | OK; + d->d_uid = statb.st_uid; + d->d_gid = statb.st_gid; + d->d_size = statb.st_size; + d->d_time = statb.st_mtime; +} + +swabdir(tp) +register struct tent *tp; +{ + swab((char *)tp, (char *)tp, sizeof(*tp)); + swab(tp->pathnam, tp->pathnam, NAMELEN); + swab((char *)&tp->uid, (char *)&tp->uid, 4); /* uid,gid,spare,size0 */ +} diff --git a/time/tp/tp3.c b/time/tp/tp3.c new file mode 100644 index 0000000..5f66ff5 --- /dev/null +++ b/time/tp/tp3.c @@ -0,0 +1,255 @@ +#include "tp.h" +#include + +gettape(how) +int (*how)(); +{ + register char *ptr0, *ptr1; + register struct dent *d; + int count; + + do { + d = &dir[0]; + count = 0; + do { + if (d->d_namep == 0) continue; + decode(name,d); + if (rnarg > 2) { + ptr0 = name; + ptr1 = *parg; + while (*ptr1) + if (*ptr0++ != *ptr1++) goto cont; + if (*ptr0 && *ptr0 != '/') goto cont; + } + (*how)(d); /* delete, extract, or taboc */ + ++count; +cont: continue; + } while (++d <= lastd); + if (count == 0 && rnarg > 2) + printf("%s not found\n", *parg); + ++parg; + } while (--narg > 2); +} + +delete(dd) +struct dent *dd; +{ + if (verify('d') >= 0) + clrent(dd); +} + + +update() +{ + register struct dent *d; + register b, last; + int first, size; + + + bitmap(); + d = &dir[0]; + do { + if(d->d_namep == 0 || (d->d_mode&OK) == 0) continue; + if (d->d_size == 0) continue; +/* find a place on the tape for this file */ + size = (d->d_size+BSIZE-1)/BSIZE; + first = ndentb; +toosmall: ++first; + if ((last = first + size) >= tapsiz) maperr(); + for (b = first; b < last; ++b) + if (map[(b>>3) & MAPMASK] & (1<<(b&7))) { + first = b; + goto toosmall; + }; + d->d_tapea = first; + setmap(d); + } while (++d <= lastd); + wrdir(); + update1(); +} + + +update1() +{ + register struct dent *d, *id; + register index; + int f; + + for (;;) { + d = &dir[0]; + index = MTSIZ; + id = 0; + do { /* find new dent with lowest tape address */ + if(d->d_namep == 0 || (d->d_mode&OK) == 0) continue; + if (d->d_tapea < index) { + index = d->d_tapea; + id = d; + } + } while (++d <= lastd); + if ((d = id) == 0) return; + d->d_mode &= ~OK; /* change from new to old */ + if (d->d_size == 0) continue; + decode(name,d); + wseek(index); + if ((f = open(name,0)) < 0) { + printf("Can't open %s\n", name); + continue; + } + for (index = d->d_size/BSIZE; index != 0; --index) { + if (read(f,(char *)tapeb,BSIZE) != BSIZE) phserr(); + twrite(); + } + if (index = d->d_size % BSIZE) { + if (read(f,(char *)tapeb,index) != index) phserr(); + twrite(); + } + if (read(f,(char *)tapeb,1) != 0) phserr(); + close(f); + } +} + +phserr() +{ printf("%s -- Phase error \n", name); } + + +bitmap() /* place old files in the map */ +{ + register char *m; + register count; + register struct dent *d; + + for(m=map;m<&map[MAPSIZE];) *m++ = 0; + count = ndirent; + d = dir; + do { + if(d->d_namep != 0 && (d->d_mode&OK) == 0 + && d->d_size != 0) setmap(d); + d++; + } while (--count); +} + +setmap(d) +register struct dent *d; +{ + unsigned c, block; + char bit; + int i; + + c = d->d_size/BSIZE; + if (d->d_size % BSIZE) c++; + block = d->d_tapea; + if ((c += block) >= tapsiz) maperr(); + do { + bit = 1 << (block & 7); + i = (block>>3) & MAPMASK; + if (bit & map[i]) maperr(); + map[i] |= bit; + } while (++block < c); +} + +maperr() +{ + printf("Tape overflow\n"); + done(); +} + + +usage() +{ + register reg,count; + int nused, nentr, nfree; + static lused; + + bitmap(); + for(count=0,nentr=0;count= tapsiz) { + printf("Tape overflow\n"); + done(); + } + if (map[(reg>>3) & MAPMASK] & (1 << (reg&7))) { + nused++; + lused = reg; + } else { + if (flags & flm) break; + nfree++; + } + reg++; + } while (--count); + printf("%4d entries\n%4d used\n", nentr, nused); + if ((flags & flm)==0) + printf("%4d free\n", nfree); + printf("%4d last\n", lused); +} + + +taboc(dd) +struct dent *dd; +{ + register mode; + register struct tm *m; + register char *s; + int count; + struct tm *localtime(); + char work[20]; + + if (flags & flv) { + mode = dd->d_mode; + s = &work[19]; + *s = 0; + for (count = 3; count; --count) { + if (mode&1) *--s = 'x'; + else *--s = '-'; + if (mode&2) *--s = 'w'; + else *--s = '-'; + if (mode&4) *--s = 'r'; + else *--s = '-'; + mode >>= 3; + } + if (mode&4) s[2] = 's'; + if (mode&2) s[5] = 's'; + printf("%s%4d%4d%5d%9D ",s,dd->d_uid, dd->d_gid,dd->d_tapea,dd->d_size); + m = localtime(&dd->d_time); + printf("%02d/%02d/%02d %02d:%02d ", + (m->tm_year>=100? m->tm_year-100: m->tm_year), + (m->tm_mon+1), + m->tm_mday, + m->tm_hour, + m->tm_min); + } + printf("%s\n", name); +} + + +extract(d) +register struct dent *d; +{ + register count, id; + + if (d->d_size==0) return; + if (verify('x') < 0) return; + rseek(d->d_tapea); + unlink(name); + if ((id = creat(name,d->d_mode)) < 0) + printf("%s -- create error\n", name); + count = d->d_size/BSIZE; + while (count--) { + tread(); + if (write(id, (char *)tapeb, BSIZE) != BSIZE) goto ng; + } + if (count = d->d_size % BSIZE) { + tread(); + if (write(id, (char *)tapeb, count) != count) { +ng: printf("%s -- write error\n", name); + close(id); + return; + } + } + close(id); + chown(name,d->d_uid & 0377, d->d_gid&0377); +} diff --git a/tty/README.md b/tty/README.md new file mode 100644 index 0000000..e545c15 --- /dev/null +++ b/tty/README.md @@ -0,0 +1,27 @@ +# Unix V7 replacement terminal driver + +The original V7 terminal driver was designed to be used with printing devices and doesn't play too well with the 'glass terminals' that we all use today. Personally I find that I don't get on with the use of '#' to delete a character, and 'delete' to send the interrupt signal. This was as true in the 1980's as it is now. + +The _tty_ driver included here came from a distribution from Nijmegen University in Holland. It's similar to the driver I've adopted for UNIX V6. It came from a tape image [Torsten_Hippe_v7](https://www.tuhs.org/Archive/Distributions/Research/Torsten_Hippe_v7/) I found on the [www.tuhs.org archive](https://www.tuhs.org/Archive). There are a couple of versions of this code available on the Archive, one is from [Nijmegen](https://www.tuhs.org/Archive/Distributions/Research/Nijmegen_v7/), but I think it's later than the version I am using here. This version is somewhat simpler and does the job. + +## Installation steps + +* Install and build the new kernel code replacing the old _tty.c_, its header and drivers. This uses files in the _sys_ directory. For more on that see [sys/README](sys). + +* Run _make_ to compile the commands in this directory. When installing the system, define _LOCAL_ in the _makefile_ to make compilations uses header files from the _sys_ directory. This allows you to create the binaries that will work with the newly compiled kernel, but without having to replace the files in _/bin_ and _/etc_. + +* Once you've installed the kernel code, you'll be surprised that the new erase/kill characters you have installed in the kernel are not set up when you login. This will continue until you replace _/etc/getty_ and _/bin/login_. The _sett_ program can be compiled and installed to set the values to what are now the defaults. It's a temporary measure but makes your life easier. On the plus side, your Interrupt character (by default ^C) will work now. + +* Install _getty_ in _/etc/getty_, reboot and check that it works. You do need to reboot, because _getty_ will be open and running in a live system. I've made quite a few changes to _getty_ to make it work better with the system API. The program is forked by _init_ and sits on terminal lines waiting for people to login. It uses _/etc/ttys_ to choose a setting for a specific terminal connection. Set the first character to 1 to enable a terminal The second character in each line of the file selects a terminal setup value from a table compiled into _getty_. Use '4' for the console, and '3' if you have additional terminals using the DC interface. However, when you install _getty_, you'll still find that _sett_ or _stty_ will still be needed, you need to replace _login_ before the full control character settings will appear on login. + +* Install _login_ in _/bin/login_, reboot and check that it works. Now the default character controls will all be available to you on login. + +* Install _stty_ in _/bin/stty.c_. I've written this version of _stty_, because I couldn't find a version that fully supported the terminal driver. I've also created a version of the _man_ page for _stty_. + +* Once things are working, and the appropriate files are installed in _/usr/sys/h_ and _/usr/include_, you can recompile the binaries and re-install removing the _LOCAL_ declaration from the _makefile_. + +## Directory + +* [sys](sys) - Files for installing the new terminal interface and associated drivers into your kernel. + +* orig - the original files from the V7 distribution. diff --git a/tty/chktc.c b/tty/chktc.c new file mode 100644 index 0000000..70f550f --- /dev/null +++ b/tty/chktc.c @@ -0,0 +1,27 @@ +#include + +#include "sys/sgtty.h" +#include "sys/deftty.h" + +struct sgttyb sgttyb; /* stty/gtty - TIOCSETP/TIOCGETP */ +struct tchars tchars; /* extra characters - TIOCSETA/TIOCGETA */ + +main() +{ + int fd; + + fd = 1; + if (ioctl(fd, TIOCGETA, &sgttyb) < 0) { + printf("Cannot get stty information"); + } + if (ioctl(fd, TIOCGETC, &tchars) < 0) { + printf("Cannot get ttchars information"); + } + + printf("%s %o\n", "t_intrc", tchars.t_intrc); + printf("%s %o\n", "t_quitc", tchars.t_quitc); + printf("%s %o\n", "t_startc", tchars.t_startc); + printf("%s %o\n", "t_stopc", tchars.t_stopc); + printf("%s %o\n", "t_eofc", tchars.t_eofc); + printf("%s %o\n", "t_brkc", tchars.t_brkc); +} diff --git a/tty/getty.c b/tty/getty.c new file mode 100644 index 0000000..56d79bf --- /dev/null +++ b/tty/getty.c @@ -0,0 +1,251 @@ +# +/* + * getty -- adapt to terminal speed on dialup, and call login + * + * Delays removed from tables + * SCOPE and INDCTL added + * in /etc/ttys + * use 14console + * and 13tty00 for remaining terminals + */ + +#ifdef LOCAL +#include "sys/sgtty.h" +#include "sys/deftty.h" +#else +#include +#include +#endif + + +#include +struct sgttyb tmode; +struct tchars tchars = { CINTR, CQUIT, CSTART, CSTOP, CEOT, CBRK }; + + +struct tab { + char tname; /* this table name */ + char nname; /* successor table name */ + int iflags; /* initial flags */ + int fflags; /* final flags */ + int ispeed; /* input speed */ + int ospeed; /* output speed */ + char *message; /* login message */ +} itab[] = { + +/* table '0'-1-2-3 300,1200,150,110 */ + + '0', 1, + ANYP+RAW, ANYP+ECHO, + B300, B300, + "\n\r\033;\007login: ", + + 1, 2, + ANYP+RAW, ANYP+ECHO+CRMOD, + B1200, B1200, + "\n\r\033;login: ", + + 2, 3, + ANYP+RAW, EVENP+ECHO, + B150, B150, + "\n\r\033:\006\006\017login: ", + + 3, '0', + ANYP+RAW, ANYP+ECHO+CRMOD, + B110, B110, + "\n\rlogin: ", + +/* table '-' -- Console TTY 110 */ + '-', '-', + ANYP+RAW, ANYP+ECHO+CRMOD+LCASE, + B110, B110, + "\n\rlogin: ", + +/* table '1' -- 150 */ + '1', '1', + ANYP+RAW, EVENP+ECHO, + B150, B150, + "\n\r\033:\006\006\017login: ", + + /* table '2' -- 9600 */ + '2', '2', + ANYP+RAW, ANYP+ECHO+CRMOD, + B9600, B9600, + "\n\r\033;login: ", + +/* table '3'-'5' -- 1200,300 */ + '3', '5', + ANYP+RAW, ANYP+ECHO+CRMOD+SCOPE+INDCTL+XTABS, + B1200, B1200, + "\n\r\033;login: ", + +/* table '5'-'3' -- 300,1200 */ + '5', '3', + ANYP+RAW, ANYP+ECHO+CRMOD, + B300, B300, + "\n\r\033;\007login: ", + +/* table '4' -- Console Decwriter */ + '4', '4', + ANYP+RAW, ANYP+ECHO+CRMOD+SCOPE+INDCTL+XTABS, + B300, B300, + "\n\rlogin: ", + +/* table 'i' -- Interdata Console */ + 'i', 'i', + RAW+CRMOD, CRMOD+ECHO+LCASE, + 0, 0, + "\n\rlogin: ", + +/* table 'l' -- LSI Chess Terminal */ + 'l', 'l', + ANYP+RAW/*+HUPCL*/, ANYP+ECHO/*+HUPCL*/, + B300, B300, + "*", +/* table '6' -- 2400 11/23 line */ + '6', '6', + ANYP+RAW, ANYP+ECHO, + B2400, B2400, + "\n\rlogin: ", + +}; + +#define NITAB sizeof itab/sizeof itab[0] +#define EOT 04 /* EOT char */ + +char name[16]; +int crmod; +int upper; +int lower; + +char partab[] = { + 0001,0201,0201,0001,0201,0001,0001,0201, + 0202,0004,0003,0205,0005,0206,0201,0001, + 0201,0001,0001,0201,0001,0201,0201,0001, + 0001,0201,0201,0001,0201,0001,0001,0201, + 0200,0000,0000,0200,0000,0200,0200,0000, + 0000,0200,0200,0000,0200,0000,0000,0200, + 0000,0200,0200,0000,0200,0000,0000,0200, + 0200,0000,0000,0200,0000,0200,0200,0000, + 0200,0000,0000,0200,0000,0200,0200,0000, + 0000,0200,0200,0000,0200,0000,0000,0200, + 0000,0200,0200,0000,0200,0000,0000,0200, + 0200,0000,0000,0200,0000,0200,0200,0000, + 0000,0200,0200,0000,0200,0000,0000,0200, + 0200,0000,0000,0200,0000,0200,0200,0000, + 0200,0000,0000,0200,0000,0200,0200,0000, + 0000,0200,0200,0000,0200,0000,0000,0201 +}; + +main(argc, argv) +char **argv; +{ + register struct tab *tabp; + int tname; + + tname = '0'; + if (argc > 1) + tname = argv[1][0]; + switch (tname) { + + case '3': /* adapt to connect speed (212) */ + ioctl(0, TIOCGETA, &tmode); + if (tmode.sg_ispeed==B300) + tname = '0'; + else + tname = '3'; + break; + } + for (;;) { + for(tabp = itab; tabp < &itab[NITAB]; tabp++) + if(tabp->tname == tname) + break; + if(tabp >= &itab[NITAB]) + tabp = itab; + tmode.sg_flags = tabp->iflags; + tmode.sg_ispeed = tabp->ispeed; + tmode.sg_ospeed = tabp->ospeed; + ioctl(0, TIOCSETA, &tmode); + ioctl(0, TIOCSETC, &tchars); + puts(tabp->message); + if(getname()) { + tmode.sg_erase = CERASE; + tmode.sg_kill = CKILL; + tmode.sg_flags = tabp->fflags; + if(crmod) + tmode.sg_flags |= CRMOD; + if(upper) + tmode.sg_flags |= LCASE; + if(lower) + tmode.sg_flags &= ~LCASE; + ioctl(0, TIOCSETA, &tmode); + putchr('\n'); + execl("/bin/login", "login", name, 0); + exit(1); + } + tname = tabp->nname; + } +} + +getname() +{ + register char *np; + register c; + char cs; + + crmod = 0; + upper = 0; + lower = 0; + np = name; + for (;;) { + if (read(0, &cs, 1) <= 0) + exit(0); + if ((c = cs&0177) == 0) + return(0); + if (c==CEOT) + exit(1); + if (c=='\r' || c=='\n' || np >= &name[16]) + break; + putchr(cs); + if (c>='a' && c <='z') + lower++; + else if (c>='A' && c<='Z') { + upper++; + c += 'a'-'A'; + } else if (c==CERASE) { + if (np > name) + np--; + continue; + } else if (c==CKILL) { + putchr('\r'); + putchr('\n'); + np = name; + continue; + } else if(c == ' ') + c = '_'; + *np++ = c; + } + *np = 0; + if (c == '\r') + crmod++; + return(1); +} + + +puts(as) +char *as; +{ + register char *s; + + s = as; + while (*s) + putchr(*s++); +} + +putchr(cc) +{ + char c; + c = cc; + c |= partab[c&0177] & 0200; + write(1, &c, 1); +} diff --git a/tty/install b/tty/install new file mode 100755 index 0000000..375a116 --- /dev/null +++ b/tty/install @@ -0,0 +1,42 @@ +: +: Install system programs +: Assume that the three binaries are compiled +: Backing up the originals +BIN=/bin +ETC=/etc + +for name in login stty getty; do + if [ ! -f $name ]; then + echo "$name doesn't exist; Run make after changing its LOCAL value in makefile" + exit + fi +done + +for name in $BIN $ETC; do + if [ ! -d $name/orig ]; then + echo "Making $name/orig" + mkdir $name/orig + fi +done + +for name in login stty; do + if [ ! -f $BIN/orig/$name ]; then + cp $BIN/$name $BIN/orig/$name + fi + if [ ! -f $BIN/orig/$name ]; then + echo 'Backup of $BIN/$name failed - exiting' + exit + fi + cp $name $BIN/$name +done + +for name in getty; do + if [ ! -f $ETC/orig/$name ]; then + cp $ETC/$name $ETC/orig/$name + fi + if [ ! -f $ETC/orig/$name ]; then + echo 'Backup of $ETC/$name failed - exiting' + exit + fi + cp $name $ETC/$name +done diff --git a/tty/login.c b/tty/login.c new file mode 100644 index 0000000..a08bf9d --- /dev/null +++ b/tty/login.c @@ -0,0 +1,154 @@ +/* + * login [ name ] + */ +#include +#ifdef LOCAL +#include "sys/sgtty.h" +#include "sys/deftty.h" +#else +#include +#include +#endif +#include +#include +#include +#include +#include +#define SCPYN(a, b) strncpy(a, b, sizeof(a)) + +char maildir[30] = "/usr/spool/mail/"; +struct passwd nouser = {"", "nope"}; +struct sgttyb ttyb; +struct utmp utmp; +char minusnam[16] = "-"; +char homedir[64] = "HOME="; +char *envinit[] = {homedir, "PATH=:/bin:/usr/bin", 0}; +struct passwd *pwd; + +struct passwd *getpwnam(); +char *strcat(); +int setpwent(); +char *ttyname(); +char *crypt(); +char *getpass(); +char *rindex(), *index(); +extern char **environ; + +main(argc, argv) +char **argv; +{ + register char *namep; + int t, f, c; + char *ttyn; + + alarm(60); + signal(SIGQUIT, SIG_IGN); + signal(SIGINT, SIG_IGN); + nice(-100); + nice(20); + nice(0); + gtty(0, &ttyb); + ttyb.sg_erase = CERASE; + ttyb.sg_kill = CKILL; + stty(0, &ttyb); + for (t=3; t<20; t++) + close(t); + ttyn = ttyname(0); + if (ttyn==0) + ttyn = "/dev/tty??"; + + loop: + SCPYN(utmp.ut_name, ""); + if (argc>1) { + SCPYN(utmp.ut_name, argv[1]); + argc = 0; + } + while (utmp.ut_name[0] == '\0') { + namep = utmp.ut_name; + printf("login: "); + while ((c = getchar()) != '\n') { + if(c == ' ') + c = '_'; + if (c == EOF) + exit(0); + if (namep < utmp.ut_name+8) + *namep++ = c; + } + } + setpwent(); + if ((pwd = getpwnam(utmp.ut_name)) == NULL) + pwd = &nouser; + endpwent(); + if (*pwd->pw_passwd != '\0') { + namep = crypt(getpass("Password:"),pwd->pw_passwd); + if (strcmp(namep, pwd->pw_passwd)) { + printf("Login incorrect\n"); + goto loop; + } + } + if(chdir(pwd->pw_dir) < 0) { + printf("No directory\n"); + goto loop; + } + time(&utmp.ut_time); + t = ttyslot(); + if (t>0 && (f = open("/etc/utmp", 1)) >= 0) { + lseek(f, (long)(t*sizeof(utmp)), 0); + SCPYN(utmp.ut_line, index(ttyn+1, '/')+1); + write(f, (char *)&utmp, sizeof(utmp)); + close(f); + } + if (t>0 && (f = open("/usr/adm/wtmp", 1)) >= 0) { + lseek(f, 0L, 2); + write(f, (char *)&utmp, sizeof(utmp)); + close(f); + } + chown(ttyn, pwd->pw_uid, pwd->pw_gid); + setgid(pwd->pw_gid); + setuid(pwd->pw_uid); + if (*pwd->pw_shell == '\0') + pwd->pw_shell = "/bin/sh"; + environ = envinit; + strncat(homedir, pwd->pw_dir, sizeof(homedir)-6); + if ((namep = rindex(pwd->pw_shell, '/')) == NULL) + namep = pwd->pw_shell; + else + namep++; + strcat(minusnam, namep); + alarm(0); + umask(02); + showmotd(); + strcat(maildir, pwd->pw_name); + if(access(maildir,4)==0) { + struct stat statb; + stat(maildir, &statb); + if (statb.st_size) + printf("You have mail.\n"); + } + signal(SIGQUIT, SIG_DFL); + signal(SIGINT, SIG_DFL); + execlp(pwd->pw_shell, minusnam, 0); + printf("No shell\n"); + exit(0); +} + +int stopmotd; +catch() +{ + signal(SIGINT, SIG_IGN); + stopmotd++; +} + +showmotd() +{ + FILE *mf; + register c; + + signal(SIGINT, catch); + if((mf = fopen("/etc/motd","r")) != NULL) { + while((c = getc(mf)) != EOF && stopmotd == 0) + putchar(c); + fclose(mf); + } + signal(SIGINT, SIG_IGN); +} diff --git a/tty/makefile b/tty/makefile new file mode 100644 index 0000000..876f77b --- /dev/null +++ b/tty/makefile @@ -0,0 +1,24 @@ +CFLAGS=-O -s -n + +# Change this to LOCAL=-DLOCAL +# to compile versions using the .h files installed in +# sys used during installation +LOCAL= + +all: stty sett getty login + +stty: stty.c + cc $(CFLAGS) $(LOCAL) -o stty stty.c + +sett: sett.c + cc $(CFLAGS) $(LOCAL) -o sett sett.c + +getty: getty.c + cc $(CFLAGS) $(LOCAL) -o getty getty.c + +login: login.c + cc $(CFLAGS) $(LOCAL) -o login login.c + + +clean: + rm stty sett getty login diff --git a/tty/orig/getty.c b/tty/orig/getty.c new file mode 100644 index 0000000..35ee772 --- /dev/null +++ b/tty/orig/getty.c @@ -0,0 +1,238 @@ +# +/* + * getty -- adapt to terminal speed on dialup, and call login + */ + +#include +#include +#define ERASE '#' +#define KILL '@' + +struct sgttyb tmode; +struct tchars tchars = { '\177', '\034', '\021', '\023', '\004', '\377' }; + +struct tab { + char tname; /* this table name */ + char nname; /* successor table name */ + int iflags; /* initial flags */ + int fflags; /* final flags */ + int ispeed; /* input speed */ + int ospeed; /* output speed */ + char *message; /* login message */ +} itab[] = { + +/* table '0'-1-2-3 300,1200,150,110 */ + + '0', 1, + ANYP+RAW+NL1+CR1, ANYP+ECHO+CR1, + B300, B300, + "\n\r\033;\007login: ", + + 1, 2, + ANYP+RAW+NL1+CR1, ANYP+XTABS+ECHO+CRMOD+FF1, + B1200, B1200, + "\n\r\033;login: ", + + 2, 3, + ANYP+RAW+NL1+CR1, EVENP+ECHO+FF1+CR2+TAB1+NL1, + B150, B150, + "\n\r\033:\006\006\017login: ", + + 3, '0', + ANYP+RAW+NL1+CR1, ANYP+ECHO+CRMOD+XTABS+LCASE+CR1, + B110, B110, + "\n\rlogin: ", + +/* table '-' -- Console TTY 110 */ + '-', '-', + ANYP+RAW+NL1+CR1, ANYP+ECHO+CRMOD+XTABS+LCASE+CR1, + B110, B110, + "\n\rlogin: ", + +/* table '1' -- 150 */ + '1', '1', + ANYP+RAW+NL1+CR1, EVENP+ECHO+FF1+CR2+TAB1+NL1, + B150, B150, + "\n\r\033:\006\006\017login: ", + +/* table '2' -- 9600 */ + '2', '2', + ANYP+RAW+NL1+CR1, ANYP+XTABS+ECHO+CRMOD+FF1, + B9600, B9600, + "\n\r\033;login: ", + +/* table '3'-'5' -- 1200,300 */ + '3', '5', + ANYP+RAW+NL1+CR1, ANYP+XTABS+ECHO+CRMOD+FF1, + B1200, B1200, + "\n\r\033;login: ", + +/* table '5'-'3' -- 300,1200 */ + '5', '3', + ANYP+RAW+NL1+CR1, ANYP+ECHO+CR1, + B300, B300, + "\n\r\033;\007login: ", + +/* table '4' -- Console Decwriter */ + '4', '4', + ANYP+RAW, ANYP+ECHO+CRMOD+XTABS, + B300, B300, + "\n\rlogin: ", + +/* table 'i' -- Interdata Console */ + 'i', 'i', + RAW+CRMOD, CRMOD+ECHO+LCASE, + 0, 0, + "\n\rlogin: ", + +/* table 'l' -- LSI Chess Terminal */ + 'l', 'l', + ANYP+RAW/*+HUPCL*/, ANYP+ECHO/*+HUPCL*/, + B300, B300, + "*", +/* table '6' -- 2400 11/23 line */ + '6', '6', + ANYP+RAW+NL1+CR1, ANYP+ECHO, + B2400, B2400, + "\n\rlogin: ", + +}; + +#define NITAB sizeof itab/sizeof itab[0] +#define EOT 04 /* EOT char */ + +char name[16]; +int crmod; +int upper; +int lower; + +char partab[] = { + 0001,0201,0201,0001,0201,0001,0001,0201, + 0202,0004,0003,0205,0005,0206,0201,0001, + 0201,0001,0001,0201,0001,0201,0201,0001, + 0001,0201,0201,0001,0201,0001,0001,0201, + 0200,0000,0000,0200,0000,0200,0200,0000, + 0000,0200,0200,0000,0200,0000,0000,0200, + 0000,0200,0200,0000,0200,0000,0000,0200, + 0200,0000,0000,0200,0000,0200,0200,0000, + 0200,0000,0000,0200,0000,0200,0200,0000, + 0000,0200,0200,0000,0200,0000,0000,0200, + 0000,0200,0200,0000,0200,0000,0000,0200, + 0200,0000,0000,0200,0000,0200,0200,0000, + 0000,0200,0200,0000,0200,0000,0000,0200, + 0200,0000,0000,0200,0000,0200,0200,0000, + 0200,0000,0000,0200,0000,0200,0200,0000, + 0000,0200,0200,0000,0200,0000,0000,0201 +}; + +main(argc, argv) +char **argv; +{ + register struct tab *tabp; + int tname; + + tname = '0'; + if (argc > 1) + tname = argv[1][0]; + switch (tname) { + + case '3': /* adapt to connect speed (212) */ + ioctl(0, TIOCGETP, &tmode); + if (tmode.sg_ispeed==B300) + tname = '0'; + else + tname = '3'; + break; + } + for (;;) { + for(tabp = itab; tabp < &itab[NITAB]; tabp++) + if(tabp->tname == tname) + break; + if(tabp >= &itab[NITAB]) + tabp = itab; + tmode.sg_flags = tabp->iflags; + tmode.sg_ispeed = tabp->ispeed; + tmode.sg_ospeed = tabp->ospeed; + ioctl(0, TIOCSETP, &tmode); + ioctl(0, TIOCSETC, &tchars); + puts(tabp->message); + if(getname()) { + tmode.sg_erase = ERASE; + tmode.sg_kill = KILL; + tmode.sg_flags = tabp->fflags; + if(crmod) + tmode.sg_flags |= CRMOD; + if(upper) + tmode.sg_flags |= LCASE; + if(lower) + tmode.sg_flags &= ~LCASE; + stty(0, &tmode); + putchr('\n'); + execl("/bin/login", "login", name, 0); + exit(1); + } + tname = tabp->nname; + } +} + +getname() +{ + register char *np; + register c; + char cs; + + crmod = 0; + upper = 0; + lower = 0; + np = name; + for (;;) { + if (read(0, &cs, 1) <= 0) + exit(0); + if ((c = cs&0177) == 0) + return(0); + if (c==EOT) + exit(1); + if (c=='\r' || c=='\n' || np >= &name[16]) + break; + putchr(cs); + if (c>='a' && c <='z') + lower++; + else if (c>='A' && c<='Z') { + upper++; + c += 'a'-'A'; + } else if (c==ERASE) { + if (np > name) + np--; + continue; + } else if (c==KILL) { + putchr('\r'); + putchr('\n'); + np = name; + continue; + } else if(c == ' ') + c = '_'; + *np++ = c; + } + *np = 0; + if (c == '\r') + crmod++; + return(1); +} + +puts(as) +char *as; +{ + register char *s; + + s = as; + while (*s) + putchr(*s++); +} + +putchr(cc) +{ + char c; + c = cc; + c |= partab[c&0177] & 0200; + write(1, &c, 1); +} diff --git a/tty/orig/login.c b/tty/orig/login.c new file mode 100644 index 0000000..b09a404 --- /dev/null +++ b/tty/orig/login.c @@ -0,0 +1,149 @@ +/* + * login [ name ] + */ + +#include +#include +#include +#include +#include +#include +#include +#define SCPYN(a, b) strncpy(a, b, sizeof(a)) + +char maildir[30] = "/usr/spool/mail/"; +struct passwd nouser = {"", "nope"}; +struct sgttyb ttyb; +struct utmp utmp; +char minusnam[16] = "-"; +char homedir[64] = "HOME="; +char *envinit[] = {homedir, "PATH=:/bin:/usr/bin", 0}; +struct passwd *pwd; + +struct passwd *getpwnam(); +char *strcat(); +int setpwent(); +char *ttyname(); +char *crypt(); +char *getpass(); +char *rindex(), *index(); +extern char **environ; + +main(argc, argv) +char **argv; +{ + register char *namep; + int t, f, c; + char *ttyn; + + alarm(60); + signal(SIGQUIT, SIG_IGN); + signal(SIGINT, SIG_IGN); + nice(-100); + nice(20); + nice(0); + gtty(0, &ttyb); + ttyb.sg_erase = '#'; + ttyb.sg_kill = '@'; + stty(0, &ttyb); + for (t=3; t<20; t++) + close(t); + ttyn = ttyname(0); + if (ttyn==0) + ttyn = "/dev/tty??"; + + loop: + SCPYN(utmp.ut_name, ""); + if (argc>1) { + SCPYN(utmp.ut_name, argv[1]); + argc = 0; + } + while (utmp.ut_name[0] == '\0') { + namep = utmp.ut_name; + printf("login: "); + while ((c = getchar()) != '\n') { + if(c == ' ') + c = '_'; + if (c == EOF) + exit(0); + if (namep < utmp.ut_name+8) + *namep++ = c; + } + } + setpwent(); + if ((pwd = getpwnam(utmp.ut_name)) == NULL) + pwd = &nouser; + endpwent(); + if (*pwd->pw_passwd != '\0') { + namep = crypt(getpass("Password:"),pwd->pw_passwd); + if (strcmp(namep, pwd->pw_passwd)) { + printf("Login incorrect\n"); + goto loop; + } + } + if(chdir(pwd->pw_dir) < 0) { + printf("No directory\n"); + goto loop; + } + time(&utmp.ut_time); + t = ttyslot(); + if (t>0 && (f = open("/etc/utmp", 1)) >= 0) { + lseek(f, (long)(t*sizeof(utmp)), 0); + SCPYN(utmp.ut_line, index(ttyn+1, '/')+1); + write(f, (char *)&utmp, sizeof(utmp)); + close(f); + } + if (t>0 && (f = open("/usr/adm/wtmp", 1)) >= 0) { + lseek(f, 0L, 2); + write(f, (char *)&utmp, sizeof(utmp)); + close(f); + } + chown(ttyn, pwd->pw_uid, pwd->pw_gid); + setgid(pwd->pw_gid); + setuid(pwd->pw_uid); + if (*pwd->pw_shell == '\0') + pwd->pw_shell = "/bin/sh"; + environ = envinit; + strncat(homedir, pwd->pw_dir, sizeof(homedir)-6); + if ((namep = rindex(pwd->pw_shell, '/')) == NULL) + namep = pwd->pw_shell; + else + namep++; + strcat(minusnam, namep); + alarm(0); + umask(02); + showmotd(); + strcat(maildir, pwd->pw_name); + if(access(maildir,4)==0) { + struct stat statb; + stat(maildir, &statb); + if (statb.st_size) + printf("You have mail.\n"); + } + signal(SIGQUIT, SIG_DFL); + signal(SIGINT, SIG_DFL); + execlp(pwd->pw_shell, minusnam, 0); + printf("No shell\n"); + exit(0); +} + +int stopmotd; +catch() +{ + signal(SIGINT, SIG_IGN); + stopmotd++; +} + +showmotd() +{ + FILE *mf; + register c; + + signal(SIGINT, catch); + if((mf = fopen("/etc/motd","r")) != NULL) { + while((c = getc(mf)) != EOF && stopmotd == 0) + putchar(c); + fclose(mf); + } + signal(SIGINT, SIG_IGN); +} diff --git a/tty/orig/stty.c b/tty/orig/stty.c new file mode 100644 index 0000000..a5da235 --- /dev/null +++ b/tty/orig/stty.c @@ -0,0 +1,301 @@ +/* + * set teletype modes + */ + +#include +#include + +struct +{ + char *string; + int speed; +} speeds[] = { + "0", B0, + "50", B50, + "75", B75, + "110", B110, + "134", B134, + "134.5",B134, + "150", B150, + "200", B200, + "300", B300, + "600", B600, + "1200", B1200, + "1800", B1800, + "2400", B2400, + "4800", B4800, + "9600", B9600, + "exta", EXTA, + "extb", EXTB, + 0, +}; +struct +{ + char *string; + int set; + int reset; +} modes[] = { + "even", + EVENP, 0, + + "-even", + 0, EVENP, + + "odd", + ODDP, 0, + + "-odd", + 0, ODDP, + + "raw", + RAW, 0, + + "-raw", + 0, RAW, + + "cooked", + 0, RAW, + + "-nl", + CRMOD, 0, + + "nl", + 0, CRMOD, + + "echo", + ECHO, 0, + + "-echo", + 0, ECHO, + + "LCASE", + LCASE, 0, + + "lcase", + LCASE, 0, + + "-LCASE", + 0, LCASE, + + "-lcase", + 0, LCASE, + + "-tabs", + XTABS, 0, + + "tabs", + 0, XTABS, + + + "cbreak", + CBREAK, 0, + + "-cbreak", + 0, CBREAK, + + "cr0", + CR0, CR3, + + "cr1", + CR1, CR3, + + "cr2", + CR2, CR3, + + "cr3", + CR3, CR3, + + "tab0", + TAB0, XTABS, + + "tab1", + TAB1, XTABS, + + "tab2", + TAB2, XTABS, + + "nl0", + NL0, NL3, + + "nl1", + NL1, NL3, + + "nl2", + NL2, NL3, + + "nl3", + NL3, NL3, + + "ff0", + FF0, FF1, + + "ff1", + FF1, FF1, + + "bs0", + BS0, BS1, + + "bs1", + BS1, BS1, + + "33", + CR1, ALLDELAY, + + "tty33", + CR1, ALLDELAY, + + "37", + FF1+CR2+TAB1+NL1, ALLDELAY, + + "tty37", + FF1+CR2+TAB1+NL1, ALLDELAY, + + "05", + NL2, ALLDELAY, + + "vt05", + NL2, ALLDELAY, + + "tn", + CR1, ALLDELAY, + + "tn300", + CR1, ALLDELAY, + + "ti", + CR2, ALLDELAY, + + "ti700", + CR2, ALLDELAY, + + "tek", + FF1, ALLDELAY, + + 0, + }; + +char *arg; +struct sgttyb mode; + +main(argc, argv) +char *argv[]; +{ + int i; + + gtty(1, &mode); + if(argc == 1) { + prmodes(); + exit(0); + } + while(--argc > 0) { + + arg = *++argv; + if (eq("ek")){ + mode.sg_erase = '#'; + mode.sg_kill = '@'; + } + if (eq("erase")) { + if (**++argv == '^') + mode.sg_erase = (*argv)[1] & 037; + else + mode.sg_erase = **argv; + argc--; + } + if (eq("kill")) { + if (**++argv == '^') + mode.sg_kill = (*argv)[1] & 037; + else + mode.sg_kill = **argv; + argc--; + } + if (eq("gspeed")) { + mode.sg_ispeed = B300; + mode.sg_ospeed = B9600; + } + if (eq("hup")) { + ioctl(1, TIOCHPCL, NULL); + } else + for(i=0; speeds[i].string; i++) + if(eq(speeds[i].string)) + mode.sg_ispeed = mode.sg_ospeed = speeds[i].speed; + for(i=0; modes[i].string; i++) + if(eq(modes[i].string)) { + mode.sg_flags &= ~modes[i].reset; + mode.sg_flags |= modes[i].set; + } + if(arg) + fprintf(stderr,"unknown mode: %s\n", arg); + } + stty(1,&mode); +} + +eq(string) +char *string; +{ + int i; + + if(!arg) + return(0); + i = 0; +loop: + if(arg[i] != string[i]) + return(0); + if(arg[i++] != '\0') + goto loop; + arg = 0; + return(1); +} + +prmodes() +{ + register m; + + if(mode.sg_ispeed != mode.sg_ospeed) { + prspeed("input speed ", mode.sg_ispeed); + prspeed("output speed ", mode.sg_ospeed); + } else + prspeed("speed ", mode.sg_ispeed); + if (mode.sg_erase < ' ') + fprintf(stderr, "erase = '^%c'; ", '@' + mode.sg_erase); + else + fprintf(stderr, "erase = '%c'; ", mode.sg_erase); + if (mode.sg_kill < ' ') + fprintf(stderr, "kill = '^%c'\n", '@' + mode.sg_kill); + else + fprintf(stderr, "kill = '%c'\n", mode.sg_kill); + m = mode.sg_flags; + if(m & EVENP) fprintf(stderr,"even "); + if(m & ODDP) fprintf(stderr,"odd "); + if(m & RAW) fprintf(stderr,"raw "); + if(m & CRMOD) fprintf(stderr,"-nl "); + if(m & ECHO) fprintf(stderr,"echo "); + if(m & LCASE) fprintf(stderr,"lcase "); + if((m & XTABS)==XTABS) fprintf(stderr,"-tabs "); + if (m & CBREAK) fprintf(stderr,"cbreak "); + delay((m&NLDELAY)/NL1, "nl"); + if ((m&TBDELAY)!=XTABS) + delay((m&TBDELAY)/TAB1, "tab"); + delay((m&CRDELAY)/CR1, "cr"); + delay((m&VTDELAY)/FF1, "ff"); + delay((m&BSDELAY)/BS1, "bs"); + fprintf(stderr,"\n"); +} + +delay(m, s) +char *s; +{ + + if(m) + fprintf(stderr,"%s%d ", s, m); +} + +int speed[] = { + 0,50,75,110,134,150,200,300,600,1200,1800,2400,4800,9600,0,0 +}; + +prspeed(c, s) +char *c; +{ + + fprintf(stderr,"%s%d baud\n", c, speed[s]); +} diff --git a/tty/sett.c b/tty/sett.c new file mode 100644 index 0000000..3962ce1 --- /dev/null +++ b/tty/sett.c @@ -0,0 +1,44 @@ +/* + * Code to set the terminal to operational VDU + * workings before installation of working system + * with new tty driver + */ +#include "sys/sgtty.h" +#include "sys/deftty.h" + +struct sgttyb sgttyb; /* stty/gtty - TIOCSETA/TIOCGETA */ +struct tchars tchars; /* extra characters - TIOCSETC/TIOCGETC */ + +main() +{ + int fd = 1; + + if (ioctl(fd, TIOCGETA, &sgttyb) < 0) { + perror("Cannot get stty information"); + } + if (ioctl(fd, TIOCGETC, &tchars) < 0) { + perror("Cannot get ttchars information"); + } + sgttyb.sg_erase = CERASE; + sgttyb.sg_kill = CKILL; + sgttyb.sg_width = 0; + sgttyb.sg_length = 0; + sgttyb.sg_nldly = 0; + sgttyb.sg_crdly = 0; + sgttyb.sg_htdly = 0; + sgttyb.sg_vtdly = 0; + sgttyb.sg_flags = ECHO|CRMOD|ODDP|EVENP|ANYP|SCOPE|INDCTL; + tchars.t_intrc = CINTR; + tchars.t_quitc = CQUIT; + tchars.t_startc = CSTART; + tchars.t_stopc = CSTOP; + tchars.t_eofc = CEOT; + tchars.t_brkc = CBRK; + + if (ioctl(fd, TIOCSETA, &sgttyb) < 0) { + perror("Cannot set stty information"); + } + if (ioctl(fd, TIOCSETC, &tchars) < 0) { + perror("Cannot get ttchars information"); + } +} diff --git a/tty/stty.1 b/tty/stty.1 new file mode 100644 index 0000000..25fe928 --- /dev/null +++ b/tty/stty.1 @@ -0,0 +1,232 @@ +.TH STTY 1 +.SH NAME +stty \- set terminal options +.SH SYNOPSIS +.B stty +[ option ... ] +.SH DESCRIPTION +.I Stty +sets certain I/O options on the current output terminal. +With no argument, it reports the current settings of the options. +.SS Flags +Flags can be turned off by adding a '-' character before the option. +.TP 8n +.B even +allow even parity +.br +.ns +.TP +.B odd +allow odd parity +.br +.ns +.TP +.B anyp +allow any parity +.br +.ns +.TP +.B raw +raw mode input +(no erase, kill, interrupt, quit, EOT; parity bit passed back) +.br +.ns +.TP +.B cooked +same as `\-raw' +.br +.ns +.TP +.B cbreak +make each character available to +.IR read \ (2) +as received; no erase and kill. +.br +.ns +.TP +.B \-nl +allow carriage return for new-line, +and output CR-LF for carriage return or new-line +When 'on', accept only new-line to end lines. +.br +.ns +.TP +.B echo +echo back every character typed +.br +.ns +.TP +.B lcase +map upper case to lower case +.br +.ns +.TP +.B tabs +delete tabs from screen +.br +.ns +.TP +.B vdu +When deleting remove characters from the screen using backspace. When +off deleted characters are shown in square brackets. +.br +.ns +.TP +.B scope +Synonym for vdu. +.br +.sp 2 +.SS Control character settings +Control characters can be set using '^' and an upper case letter, +so '^C' will be interpreted as Control-C. +.TP +.BI erase \ c\fR +set erase character to +.IR c . +Default: ^C. +.br +.ns +.TP +.BI kill \ c\fR +set kill character to +.IR c . +All current input is deleted. +Default: ^U +.br +.ns +.TP +.BI intr \ c\fR +set interrupt character to +.IR c . +Default: ^C +.br +.ns +.TP +.BI quit \ c\fR +set quit character to +.IR c . +.br +Default: ^\ (Control-backslash) +.br +.ns +.TP +.BI stop \ c\fR +set stop output character to +.IR c . +Default: ^S +.br +.ns +.TP +.BI start \ c\fR +set start output character to +.IR c . +Default: ^Q +.br +.ns +.TP +.BI eof \ c\fR +set end of file character +.IR c . +Default: ^D +.br +.ns +.TP +.BI brk \ c\fR +Set brk character +.IR c . +Default: brk (0377) +.br +.LP +Other control characters that cannot be altered: +.TP +^P +flip paging on and off +.br +.ns +.TP +^R +retype current line +.br +.ns +.TP +^W +delete last word +.br +.ns +.TP +^V +Next character is entered with no processing +.br +.sp 2 +.SS Delays +Delays are set by a control word immediately followed by a number, e.g +nl0. The number is approximately the number kernel 'ticks' - 0-254. Mostly for +screen use zero is used. +.TP +.B cr +.br +select style of delay for carriage return. +.br +.ns +.TP +.B nl +.br +select style of delay for linefeed +.br +.ns +.TP +.B tab +.br +select style of delay for tab, +.br +.ns +.TP +.B ff +select style of delay for form feed +.br +.sp 2 +.SS Speeds +.ns +.TP +.B "50 75 110 134 150 200 300 600 1200 1800 2400 4800 9600 exta extb" +.br +Set terminal baud rate to the number given, if possible. +(These are the speeds supported by the DH-11 interface). +.TP +.B 0 +Hang up the line immediately. +.ns +.br +.TP +.BI ispeed \ n\fR +Set the input speed, the argument is a number or 'exta' or 'extb'. +.ns +.br +.TP +.BI ospeed \ n\fR +Set the output speed, the argument is a number or 'exta' or 'extb'. +.sp 2 +.SS "Other Options" +.TP +.B hup +Set the flag that forces a hangup on last close. Can be prefixed by '-'. +.br +.ns +.TP +.BI cols \ n\fR +Set the width of the screen in characters +.br +.ns +.TP +.BI rows \ n\fR +Set the number of lines on the screen +.br +.ns +.TP +.B sane +Set all settings to default +.br +.SH "AUTHOR" +Peter Collinson, July 2023 +.SH "SEE ALSO" +ioctl(2), tabs(1) diff --git a/tty/stty.c b/tty/stty.c new file mode 100644 index 0000000..bbdeb94 --- /dev/null +++ b/tty/stty.c @@ -0,0 +1,597 @@ +/* + * Replacement for stty.c that deals with the + * new values supported by the new terminal driver + * + * Peter Collinson + * July 2023 + */ +#include + +#ifdef LOCAL +#include "sys/sgtty.h" +#include "sys/deftty.h" +#else +#include +#include +#endif + +/* Speed definitions */ +struct speeds +{ + char *string; + int speed; +} speeds[] = { + "0", B0, "50", B50, "75", B75, "110", B110, + "134", B134, "134.5", B134, "150", B150, "200", B200, + "300", B300, "600", B600, "1200", B1200, "1800", B1800, + "2400", B2400, "4800", B4800, "9600", B9600, + "exta", EXTA, "extb", EXTB, + 0, +}; + +/* + * ioctl targets + */ +struct sgttyb sgttyb; /* stty/gtty - TIOCSETA/TIOCGETA */ +struct tchars tchars; /* extra characters - TIOCSETC/TIOCGETC */ + +/* External string code */ + +/* + * source data groups + */ +#define GSPEED 1 /* Speeds */ +#define GCHAR 2 /* Special characters */ +#define GFLAGS 3 /* Flags */ +#define GDELAY 4 /* Delays */ +#define GSCREEN 5 /* Screen sizes */ +#define GCOMBO 6 /* Value sets other values */ +#define GALIAS 7 /* Alias for another value */ +#define GFN 8 /* call a function */ + +/* Data modes */ +#define AINVT 01 /* reverse sense of initial - */ + +/* + * Service functions + */ +int dosane(); +int dohup(); + +/* + * Argument decode and print + */ +struct alist { + char *key; /* lookup value */ + short group; /* data groups */ + short mode; /* Modes */ + union { + char *target; /* where to update */ + short mask; /* mask to use for flags */ + int (*serv)(); /* function to call */ + } u; +} alist[] = { + "erase", GCHAR, 0, &sgttyb.sg_erase, + "kill", GCHAR, 0, &sgttyb.sg_kill, + "intr", GCHAR, 0, &tchars.t_intrc, + "quit", GCHAR, 0, &tchars.t_quitc, + "stop", GCHAR, 0, &tchars.t_stopc, + "start", GCHAR, 0, &tchars.t_startc, + "eof", GCHAR, 0, &tchars.t_eofc, + "brk", GCHAR, 0, &tchars.t_brkc, + + /* speeds - need to look for numbers separately */ + "ispeed", GSPEED, 0, &sgttyb.sg_ispeed, + "ospeed", GSPEED, 0, &sgttyb.sg_ospeed, + "exta", GCOMBO, 0, "ispeed exta;ospeed exta", + "extb", GCOMBO, 0, "ispeed extb;ospeed extb", + + /* screen sizes */ + "rows", GSCREEN, 0, &sgttyb.sg_width, + "cols", GSCREEN, 0, &sgttyb.sg_length, + "col", GALIAS, 0, "cols", + + /* Delays */ + "nl", GDELAY, 0, &sgttyb.sg_nldly, + "cr", GDELAY, 0, &sgttyb.sg_crdly, + "tab", GDELAY, 0, &sgttyb.sg_htdly, + "ff", GDELAY, 0, &sgttyb.sg_vtdly, + + /* Flags */ + "tandem", GFLAGS, 0, TANDEM, + "cbreak", GFLAGS, 0, CBREAK, + "lcase", GFLAGS, 0, LCASE, + "echo", GFLAGS, 0, ECHO, + "nl", GFLAGS, AINVT, CRMOD, + "raw", GFLAGS, 0, RAW, + "cooked", GCOMBO, 0, "-raw", + "oddp", GFLAGS, 0, ODDP, + "evenp", GFLAGS, 0, EVENP, + "anyp", GFLAGS, 0, ANYP, + "vdu", GFLAGS, 0, SCOPE, + "scope", GALIAS, 0, "vdu", + "ctrl", GFLAGS, 0, INDCTL, + "tabs", GFLAGS, 0, XTABS, + "sane", GFN, 0, &dosane, + "hup", GFN, 0, &dohup, + 0 +}; + +main(argc, argv) +int argc; +char *argv[]; +{ + int fd = 1; + + /* First look for -f /dev/tty?? to use a different tty */ + argv++; + if (--argc >= 2) { + if (strcmp(argv[0], "-f") == 0) { + if ((fd = open(argv[1], "r")) < 0) + fatal("Cannot open: %s\n", argv[1]); + argc -= 2; + argv += 2; + } + } + /* get the values */ + if (ioctl(fd, TIOCGETA, &sgttyb) < 0) { + fatal("Cannot get stty information"); + } + if (ioctl(fd, TIOCGETC, &tchars) < 0) { + fatal("Cannot get ttchars information"); + } + if (argc == 0) { + prmodes(); + exit(0); + } + while (*argv) { + argv = argparse(argv); + } + if (ioctl(fd, TIOCSETA, &sgttyb) < 0) { + fatal("Cannot set stty information"); + } + if (ioctl(fd, TIOCSETC, &tchars) < 0) { + fatal("Cannot set ttchars information"); + } + prmodes(); + exit(0); +} + +fatal(fmt, arg) +char *fmt; +char *arg; +{ + fprintf(stderr, fmt, arg); + exit(1); +} + +argparse(argv) +char **argv; +{ + register char *arg; + register struct alist *ap; + int haveminus; + short suffix; + char buf[64]; + + arg = *argv; + /* if the argument starts with a number, then it's a speed */ + if (*arg >= '0' && *arg <= '9') { + if (findspeed(arg) < 0) { + fatal("Cannot recognise speed: %s\n", arg); + } + sprintf(buf, "ispeed %s;ospeed %s", arg, arg); + docombo(buf); + return ++argv; + } + /* check for initial turn off */ + haveminus = 0; + if (*arg == '-') { + haveminus = 1; + arg++; + } + strtolower(arg); + /* + * now see if this is some text, followed immediately by a number + * side effect if value is not -1, arg is now a string + */ + strncpy(buf, arg, sizeof(buf)); + suffix = argnumber(buf); + /* search for a match */ + ap = findoption(buf, haveminus, suffix); + if (ap == NULL) + fatal("Cannot find a match for %s\n", *argv); + /* + * This is looking hopeful + * now apply the values + */ + argv++; + argv = applyvals(argv, ap, haveminus, suffix); + return argv; +} + +applyvals(argv, ap, haveminus, suffix) +char **argv; +register struct alist *ap; +int haveminus; +short suffix; +{ + short mask; + short value; + + switch (ap->group) { + case GSPEED: + value = findspeed(*argv); + if (value < 0) + fatal("Unknown speed %s\n", *argv); + argv++; + *ap->u.target = value; + break; + case GCHAR: + value = parsechar(*argv); + argv++; + *ap->u.target = value; + break; + case GFLAGS: + mask = ap->u.mask; + if (haveminus || (ap->mode&AINVT)) { + mask = ~mask; + sgttyb.sg_flags &= mask; + } else if (!haveminus || ((ap->mode&AINVT)) == 0) + sgttyb.sg_flags |= mask; + break; + case GDELAY: + if (suffix < 0) + fatal("%s needs a number between 0 and 127\n", ap->key); + *ap->u.target = suffix; + break; + case GSCREEN: + value = parseint(*argv); + if (value == -1) + fatal("Unknown argument for %s\n", ap->key); + if (value == -2) + fatal("Argument for %s not in range\n", ap->key); + argv++; + *ap->u.target = value; + break; + case GCOMBO: + docombo(ap->u.target); + break; + case GFN: + (*ap->u.serv)(); + break; + } + return argv; +} + +char +parseint(arg) +register char *arg; +{ + register int v; + + v = 0; + while (*arg >= '0' && *arg <= '9') + v = v*10 + *arg++ - '0'; + if (*arg != '\0') + return -1; + if (v > 0177) { + return -2; + } + return v; +} + +parsechar(arg) +register char *arg; +{ + if (strcmp(arg, "del") == 0) + return 0177; + if (strcmp(arg, "esc") == 0) + return 033; + if (strcmp(arg, "brk") == 0) + return -1; + if (arg[0] == '^' && arg[1] != '\0') { + return arg[1]&037; + } + return arg[0]; +} + +dosane() +{ + tchars.t_intrc = CINTR; + tchars.t_quitc = CQUIT; + tchars.t_startc = CSTART; + tchars.t_stopc = CSTOP; + tchars.t_eofc = CEOT; + tchars.t_brkc = CBRK; + sgttyb.sg_erase = CERASE; + sgttyb.sg_kill = CKILL; + sgttyb.sg_width = 0; + sgttyb.sg_length = 0; + sgttyb.sg_flags = ECHO+CRMOD+ODDP+EVENP+SCOPE+INDCTL; + sgttyb.sg_nldly = 0; + sgttyb.sg_crdly = 0; + sgttyb.sg_htdly = 0; + sgttyb.sg_vtdly = 0; +} + +dohup() +{ + ioctl(1, TIOCHPCL, NULL); +} + +findoption(arg, haveminus, suffix) +register char *arg; +int haveminus; +short suffix; +{ + register struct alist *ap; + + for (ap = alist; ap->key; ap++) { + if (haveminus && (ap->group != GFLAGS && ap->group != GALIAS)) { + continue; + } + if (suffix >= 0 && (ap->group != GDELAY)) { + continue; + } + if (strcmp(arg, ap->key) == 0) { + if (ap->group == GALIAS) { + return findoption(ap->u.target, haveminus, suffix); + } + return ap; + } + } + return NULL; +} + +strtolower(*p) +register char *p; +{ + for (;*p; p++) + if (*p >= 'A' && *p <= 'Z') + *p += 040; +} + +/* + * parse an argument getting an embedded number + * and make any preceding text into a string + */ +argnumber(*p) +register char *p; +{ + int retval = -1; + + /* first char cannot be a number */ + if (*p >= '0' && *p <= '9') + return retval; + /* look for number start */ + for (;*p; p++) + if (*p >= '0' && *p <= '9') + break; + /* found - make the number into binary */ + if (*p) { + retval = parseint(p); + *p = '\0'; + } + return retval; +} + +/* + * Generate separate arguments from a string + * separators are space and ; + * also ignore any leading space + */ +comboscan(src, avec, stbuf) +register char *src; +char *avec[]; +register char *stbuf; +{ + register int i; + + i = 0; + while (*src) { + avec[i++] = stbuf; + avec[i] = NULL; + while (*src == ' ') src++; + while (*src && *src != ' ' && *src != ';') { + *stbuf++ = *src++; + } + if (*src) + src++; + *stbuf++ = '\0'; + } + return avec; +} + +docombo(arg) +char *arg; +{ + register char **ap; + char *avec[20]; + char stbuf[128]; + + /* Parse the arguments */ + ap = comboscan(arg, avec, stbuf); + /* Action them */ + while (*ap) { + ap = argparse(ap); + } +} + +findspeed(arg) +char *arg; +{ + register i; + + for (i = 0; speeds[i].string; i++) { + if (strcmp(arg, speeds[i].string) == 0) { + return speeds[i].speed; + } + } + return -1; +} + +/* buffering for output */ +#define LINELEN 80 + +/* Buffer to amass the line */ +char line[LINELEN]; +int linelen; +/* buffer for value creation */ +char valbuf[LINELEN]; + +prmodes() +{ + register struct alist *ap; + + linelen = 0; + /* Speeds */ + if (sgttyb.sg_ispeed != sgttyb.sg_ospeed) { + addto(prspeed("input speed", sgttyb.sg_ispeed), NULL); + addto(prspeed("output speed", sgttyb.sg_ospeed), "; "); + } else + addto(prspeed("speed", sgttyb.sg_ispeed), NULL); + + /* screen size */ + for (ap = alist; ap->key; ap++) { + if (ap->group == GSCREEN) { + sprintf(valbuf, "%s %d", ap->key, *ap->u.target); + addto(valbuf, "; "); + } + } + fprintf(stderr, "%s\n", line); + linelen = 0; + + /* characters */ + for (ap = alist; ap->key; ap++) + if (ap->group == GCHAR) + addto(prchar(ap->key, *ap->u.target), "; "); + fprintf(stderr, "%s\n", line); + linelen = 0; + + /* flags */ + for (ap = alist; ap->key; ap++) + if (ap->group == GFLAGS) + addto(visflag(ap->key, ap->u.mask, ap->mode&AINVT), " "); + fprintf(stderr, "%s\n", line); + linelen = 0; + + /* delays */ + for (ap = alist; ap->key; ap++) + if (ap->group == GDELAY) { + sprintf(valbuf, "%s%d", ap->key, *ap->u.target); + addto(valbuf, "; "); + } + fprintf(stderr, "%s\n", line); + linelen = 0; +} + +/* + * Add a string to the linebuffer + * if there no space, print the line + * and reset the buffer counts + * prefix the text with a separator, unless there's + * just been a newline + */ +addto(str, sep) +register char *str; +char *sep; +{ + int slen; + int seplen; + + slen = strlen(str); + seplen = 0; + if (sep) + seplen = strlen(sep); + if (linelen > 0 && linelen + slen + seplen >= LINELEN) { + fprintf(stderr, "%s\n", line); + linelen = 0; + } + if (linelen != 0 && sep) { + strcpy(&line[linelen], sep); + linelen += seplen; + } + strcpy(&line[linelen], str); + linelen += slen; + return linelen; +} + +/* + * print speeds + */ +prspeed(c, s) +char *c; +{ + register struct speeds *sp; + register char *thissp = "??"; + + for (sp = speeds; sp->string; sp++) { + if (sp->speed == s) { + thissp = sp->string; + break; + } + } + + sprintf(valbuf, "%s %s baud", c, thissp); + return valbuf; +} + +/* + * print name = 'ch' + */ +prchar(key, ch) +char *key; +char ch; +{ + char chbuf[4]; + + sprintf(valbuf, "%s = '%s'", key, vischar(chbuf, ch)); + return valbuf; +} + +/* + * return characters as a printable string + */ +vischar(chbuf, c) +register char *chbuf; +char c; +{ + + if (c > 0 && c < 040) { + if (c == 033) + return "esc"; + chbuf[0] = '^'; + chbuf[1] = c | 0100; + chbuf[2] = '\0'; + return chbuf; + } + if (c == 0177) + return "del"; + if (c == -1) + return "brk"; + chbuf[0] = c; + chbuf[1] = '\0'; + return chbuf; +} + +/* + * Return flags as name or -name + */ +visflag(name, mask, invert) +char *name; +short mask; +int invert; +{ + register char *p; + + strcpy(valbuf, "-"); + p = valbuf; + if (sgttyb.sg_flags&mask) { + if (invert) p++; + } else { + if (!invert) p++; + } + strcpy(p, name); + return valbuf; +} diff --git a/tty/sys/README.md b/tty/sys/README.md new file mode 100644 index 0000000..b1ff564 --- /dev/null +++ b/tty/sys/README.md @@ -0,0 +1,44 @@ +# Install new tty driver & device interfaces + +This version of tty.c came from a distribution from the University of Nijmegen. + +## Summary of changes + +The original tty driver for V7 used three character queues: one for output, and two for input. Characters from the devices were placed on an input queue and processed to the 'canonical' queue when the user pressed return. When return was entered, the canonical queue was passed into user space processing all the delete characters and other editing characters. The new code uses a single input queue, and the characters used to modify the input data operate directly on the queue. There is a new routine _zapc_ that removes characters from the end of the queue to assist with the editing. + +The change meant that the _tty_ structure in _tty.h_ was changed significantly. I've also moved all the special character definitions into _deftty.h_ to allow other programs to get at the values. The change also removes support for the Packet Driver and multiplexer which are controlled by defines in _tty.h_. + +As well as _tty.c_ essentially being replaced, the drivers that use it need slight alterations. + +## Files + +* _dc.c_: DC11 driver code. Small change to support the full range of _ioctl_ commands. +* _deftty.h_: new file with control characters and modes abstracted from _tty.h_. +* _dh.c_: DH11 driver, changes for initial terminal setup, and support for paging. +* _kl.c_: KL11/DL11 driver. +* _dz.c_: A reworked driver from the Nijmegen distribution. +* _pk.p_: The old code uses one of the terminal queues and clears the other. Now only one queue exists, and the code clears it and then uses it. +* _sgtty.h_: This header file contains public versions of the various tty setting values, it needs revision for the new _tty.c_. +* _tty.c_: New terminal driver. +* _tty.h_: New header file. + +Original V7 files can be found in the _orig_ directory. + +## Installation + +These scripts all need an argument which is the destination in the filesystem you are changing. This is to stop running a script without intent and then wondering how to get back to where you were. You'll get the same error message for each script that is called with no argument, or the target directory cannot be opened. + +Before you start this, I do recommend that you stop the simulator and take a copy of the _rp06-0.disk_. + +You will need to run these scripts as root. They must to be run from this directory changing files in _/usr/sys_ or anywhere else. They are all as defensive as possible and can be run more than once. + +* __step1.sh__ +provides backup files in various _orig_ directories. It creates directories called _orig_ in the various directories that contain files that will be overwritten or removed. Files in any _orig_ directory are not overwritten. The script needs the _/bin/[_ command, and tests for it (see [sh-test](../../sh-test)). + +* __step2.sh__ +installs new files in the various system directories, files are not overwritten unless a backup exists in an _orig_ directory. + +* __step3.sh__ +is a verification step comparing the files in this directory with the versions in the destination tree. + +You can now rebuild your new kernel. Scripts to assist with this can be found on [_../../v7conf_](../../v7conf). Make sure you keep a copy of your running kernel so you can revert in case of problems. diff --git a/tty/sys/dc.c b/tty/sys/dc.c new file mode 100644 index 0000000..777ac5d --- /dev/null +++ b/tty/sys/dc.c @@ -0,0 +1,240 @@ +#include "../h/param.h" +#include "../h/conf.h" +#include "../h/dir.h" +#include "../h/user.h" +#include "../h/tty.h" +#include "../h/systm.h" + +/* + * Base address of DC-11's. Minor device i is at + * DCADDR + 10*i. + */ +#define DCADDR (struct device *)0174000 + +/* + * Number of DC's for which table space is allocated. + */ +#define NDC11 4 + +/* + * Control bits in device registers + */ +#define CDLEAD 01 +#define CARRIER 04 +#define SPEED1 010 +#define STOP1 0400 +#define RQSEND 01 +#define PARITY 040 +#define ERROR 0100000 +#define CTRANS 040000 +#define RINGIND 020000 + + +struct tty dc11[NDC11]; + +struct device { + int dcrcsr; + int dcrbuf; + int dctcsr; + int dctbuf; +}; + +/* + * Input-side speed and control bit table. + * Each DC11 has 4 speeds which correspond to the 4 non-zero entries. + * The table index is the same as the speed-selector + * number for the DH11. + * Attempts to set the speed to a zero entry are ignored. + */ +int dcrstab[] = { + 0, /* 0 baud */ + 0, /* 50 baud */ + 0, /* 75 baud */ + 0, /* 110 baud */ + 01101, /* 134.5 baud: 7b/ch, speed 0 */ + 0111, /* 150 baud: 8b/ch, speed 1 */ + 0, /* 200 baud */ + 0121, /* 300 baud: 8b/ch, speed 2 */ + 0, /* 600 baud */ + 0131, /* 1200 baud */ + 0, /* 1800 baud */ + 0, /* 2400 baud */ + 0, /* 4800 baud */ + 0, /* 9600 baud */ + 0, /* X0 */ + 0, /* X1 */ +}; + +/* + * Transmitter speed table + */ +int dctstab[] = { + 0, /* 0 baud */ + 0, /* 50 baud */ + 0, /* 75 baud */ + 0, /* 110 baud */ + 0501, /* 134.5 baud: stop 1 */ + 0511, /* 150 baud */ + 0, /* 200 baud */ + 0521, /* 300 baud */ + 0, /* 600 baud */ + 0531, /* 1200 baud */ + 0, /* 1800 baud */ + 0, /* 2400 baud */ + 0, /* 4800 baud */ + 0, /* 9600 baud */ + 0, /* X0 */ + 0, /* X1 */ +}; + +/* + * Open a DC11, waiting until carrier is established. + * Default initial conditions are set up on the first open. + * t_state's CARR_ON bit is a pure copy of the hardware + * CARRIER bit, and is only used to regularize + * carrier tests in general tty routines. + */ +dcopen(dev, flag) +dev_t dev; +{ + register struct tty *tp; + register struct device *addr; + extern int klstart(); + int s; + + if (minor(dev) >= NDC11) { + u.u_error = ENXIO; + return; + } + tp = &dc11[minor(dev)]; + addr = DCADDR + minor(dev); + tp->t_addr = (caddr_t)addr; + tp->t_state |= WOPEN; + s = spl5(); + addr->dcrcsr |= IENABLE|CDLEAD; + if ((tp->t_state&ISOPEN) == 0) { + tp->t_erase = CERASE; + tp->t_kill = CKILL; + addr->dcrcsr = IENABLE|CDLEAD|SPEED1; + addr->dctcsr = IENABLE|SPEED1|STOP1|RQSEND; + tp->t_state = ISOPEN | WOPEN; + tp->t_flags = ODDP|EVENP|ECHO; + tp->t_oproc = klstart; + } + if (addr->dcrcsr & CARRIER) + tp->t_state |= CARR_ON; + splx(s); + while ((tp->t_state & CARR_ON) == 0) + sleep((caddr_t)&tp->t_rawq, TTIPRI); + ttyopen(dev, tp); +} + +/* + * Close a dc11 + */ +dcclose(dev) +dev_t dev; +{ + register struct tty *tp; + + tp = &dc11[minor(dev)]; + if (tp->t_state&HUPCLS) + ((struct device *)(tp->t_addr))->dcrcsr &= ~CDLEAD; + ttyclose(tp); +} + +/* + * Read a DC11 + */ +dcread(dev) +dev_t dev; +{ + ttread(&dc11[minor(dev)]); +} + +/* + * Write a DC11 + */ +dcwrite(dev) +dev_t dev; +{ + ttwrite(&dc11[minor(dev)]); +} + +/* + * DC11 transmitter interrupt. + */ +dcxint(dev) +dev_t dev; +{ + register struct tty *tp; + + tp = &dc11[minor(dev)]; + ttstart(tp); + if (tp->t_outq.c_cc == 0 || tp->t_outq.c_cc == TTLOWAT) + wakeup((caddr_t)&tp->t_outq); +} + +/* + * DC11 receiver interrupt. + */ +dcrint(dev) +dev_t dev; +{ + register struct tty *tp; + register int c, csr; + + tp = &dc11[minor(dev)]; + c = ((struct device *)(tp->t_addr))->dcrbuf; + /* + * If carrier is off, and an open is not in progress, + * knock down the CD lead to hang up the local dataset + * and signal a hangup. + */ + if (((csr = ((struct device *)(tp->t_addr))->dcrcsr) & CARRIER) == 0) { + if ((tp->t_state&WOPEN) == 0) { + ((struct device *)(tp->t_addr))->dcrcsr &= ~CDLEAD; + if (tp->t_state & CARR_ON) + signal(tp->t_pgrp, SIGHUP); + flushtty(tp); + } + tp->t_state &= ~CARR_ON; + return; + } + if (csr&ERROR || (tp->t_state&ISOPEN)==0) { + if (tp->t_state&WOPEN && csr&CARRIER) + tp->t_state |= CARR_ON; + wakeup((caddr_t)tp); + return; + } + csr &= PARITY; + if (csr&&(tp->t_flags&ODDP) || !csr&&(tp->t_flags&EVENP)) + ttyinput(c, tp); +} + +/* + * DC11 stty/gtty. + * Perform general functions and set speeds. + */ +dcioctl(dev, cmd, addr, flag) +dev_t dev; +caddr_t addr; +{ + register struct tty *tp; + register r; + + tp = &dc11[minor(dev)]; + if (ttioccom(cmd, &dc11[minor(dev)], addr, dev) == 0) { + u.u_error = ENOTTY; + return; + } + if (cmd == TIOCSETP || cmd == TIOCSETN || cmd == TIOCSETA) { + r = dcrstab[tp->t_ispeed]; + if (r) + ((struct device *)(tp->t_addr))->dcrcsr = r; + else + ((struct device *)(tp->t_addr))->dcrcsr &= ~CDLEAD; + r = dctstab[tp->t_ospeed]; + ((struct device *)(tp->t_addr))->dctcsr = r; + } +} diff --git a/tty/sys/deftty.h b/tty/sys/deftty.h new file mode 100644 index 0000000..9f232e7 --- /dev/null +++ b/tty/sys/deftty.h @@ -0,0 +1,30 @@ +/* + * Put default terminal control characters into + * one file, so they are more accessible from + * other code that needs them + */ +/* + * Default special characters. + */ +#define CERASE 0177 /* DEL */ +#define CEOT 04 /* ^D */ +#define CKILL 025 /* ^U */ +#define CQUIT 034 /* ^\ */ +#define CINTR 03 /* ^C */ +#define CSTOP 023 /* ^S */ +#define CSTART 021 /* ^Q */ +#define CBRK 0377 /* -1 */ + +/* + * Other control characters. These + * are not redefinable by the user. + */ +#define CPAGE 020 /* ^P - flip paging off/on for this process */ +#define CRETYPE 022 /* ^R - retype current line */ +#define CWORD 027 /* ^W - delete word */ +#define CLITERAL 026 /* ^V, the literal next char */ + +/* + * Miscellaneous junk + */ +#define CBELL 07 /* ^G - (ding-dong) */ diff --git a/tty/sys/dh.c b/tty/sys/dh.c new file mode 100644 index 0000000..1a54053 --- /dev/null +++ b/tty/sys/dh.c @@ -0,0 +1,406 @@ +/* + * DH-11 driver + * This driver calls on the DHDM driver. + * If the DH has no DM11-BB, then the latter will + * be fake. To insure loading of the correct DM code, + * lib2 should have dhdm.o, dh.o and dhfdm.o in that order. + */ +/* + * Saves code if only one DH-11 +#define ONLYONE + */ + +#include "../h/param.h" +#include "../h/conf.h" +#include "../h/dir.h" +#include "../h/user.h" +#include "../h/tty.h" + +#define q3 tp->t_outq +#define DHADDR ((struct device *)0160020) +#define NDH11 16 /* number of lines */ + +struct tty dh11[NDH11]; +char dhcc[NDH11]; +int dhchars[(NDH11+15)/16]; +int ndh11 = NDH11; +int dhstart(); +int ttrstrt(); + +/* + * Hardware control bits + */ +#define BITS6 01 +#define BITS7 02 +#define BITS8 03 +#define TWOSB 04 +#define PENABLE 020 +/* DEC manuals incorrectly say this bit causes generation of even parity. */ +#define OPAR 040 +#define HDUPLX 040000 + +#define IENAB 030100 +#define PERROR 010000 +#define FRERROR 020000 +#define OVERRUN 040000 +#define XINT 0100000 +#define SSPEED 7 /* standard speed: 300 baud */ +#define NSILO 16 +#define DHTIME 6 +extern int dhtimer(); + +/* + * DM control bits + */ +#define TURNON 03 /* CD lead + line enable */ +#define TURNOFF 01 /* line enable */ +#define RQS 04 /* request to send */ + +/* + * Software copy of last dhbar + */ +int dhsar[(NDH11+15)/16]; + +struct device +{ + union { + int dhcsr; + char dhcsrl; + } un; + int dhnxch; + int dhlpr; + char *dhcar; + int dhbcr; + int dhbar; + int dhbreak; + int dhsilo; +}; + +/* + * Open a DH11 line. + */ +dhopen(dev, flag) +{ + register struct tty *tp; + register d; + register struct device *addr; + static timer_on; + int s; + + d = minor(dev); + if (d >= NDH11) { + u.u_error = ENXIO; + return; + } + tp = &dh11[d]; + addr = DHADDR; + addr += d>>4; + tp->t_addr = (caddr_t)addr; + tp->t_oproc = dhstart; + tp->t_iproc = NULL; + tp->t_state |= WOPEN; + s = spl6(); + if (!timer_on) { + timer_on++; + timeout(dhtimer, (caddr_t)0, DHTIME); + } + splx(s); + addr->un.dhcsr |= IENAB; + if ((tp->t_state&ISOPEN) == 0) { + ttychars(tp); + tp->t_ispeed = SSPEED; + tp->t_ospeed = SSPEED; + tp->t_flags = ODDP|EVENP|ECHO|CRMOD; + dhparam(d); + } + if (tp->t_state&XCLUDE && u.u_uid!=0) { + u.u_error = EBUSY; + return; + } + dmopen(d); + (*linesw[tp->t_line].l_open)(dev,tp); +} + +/* + * Close a DH11 line. + */ +dhclose(dev, flag) +dev_t dev; +int flag; +{ + register struct tty *tp; + register d; + + d = minor(dev); + tp = &dh11[d]; + (*linesw[tp->t_line].l_close)(tp); + if (tp->t_state&HUPCLS) + dmctl(d, TURNOFF); + ttyclose(tp); +} + +/* + * Read from a DH11 line. + */ +dhread(dev) +{ +register struct tty *tp; + + tp = &dh11[minor(dev)]; + (*linesw[tp->t_line].l_read)(tp); +} + +/* + * write on a DH11 line + */ +dhwrite(dev) +{ +register struct tty *tp; + + tp = &dh11[minor(dev)]; + (*linesw[tp->t_line].l_write)(tp); +} + +/* + * DH11 receiver interrupt. + */ +dhrint(dev) +{ + register struct tty *tp; + register int c; + register struct device *addr; + + addr = DHADDR; + addr += minor(dev); + while ((c = addr->dhnxch) < 0) { /* char. present */ + tp = &dh11[(minor(dev)<<4) + ((c>>8)&017)]; + dhchars[minor(dev)]++; + if (tp >= &dh11[NDH11]) + continue; + if((tp->t_state&ISOPEN)==0) { + wakeup((caddr_t)tp); + continue; + } + if (c&PERROR) + if ((tp->t_flags&(EVENP|ODDP))==EVENP + || (tp->t_flags&(EVENP|ODDP))==ODDP ) + continue; + if (c&FRERROR) /* break */ + if (tp->t_flags&RAW) + c = 0; /* null (for getty) */ + else + c = CINTR; /* (intr) */ + (*linesw[tp->t_line].l_rint)(c,tp); + } +} + +/* + * stty/gtty for DH11 + */ +dhioctl(dev, cmd, addr, flag) +caddr_t addr; +{ + register struct tty *tp; + + tp = &dh11[minor(dev)]; + if (ttioccomm(cmd, tp, addr, dev)) { + if (cmd == TIOCSETP || cmd == TIOCSETN || cmd == TIOCSETA) + dhparam(dev); + } else + u.u_error = ENOTTY; +} + +/* + * Set parameters from open or stty into the DH hardware + * registers. + */ +dhparam(dev) +{ + register struct tty *tp; + register struct device *addr; + register d; + + d = minor(dev); + tp = &dh11[d]; + addr = (struct device *)tp->t_addr; + spl5(); + addr->un.dhcsrl = (d&017) | IENAB; + /* + * Hang up line? + */ + if ((tp->t_ispeed)==0) { + tp->t_state |= HUPCLS; + dmctl(d, TURNOFF); + return; + } + d = ((tp->t_ospeed)<<10) | ((tp->t_ispeed)<<6); + if ((tp->t_ispeed) == 4) /* 134.5 baud */ + d |= BITS6|PENABLE|HDUPLX; + else if (tp->t_flags&RAW) + d |= BITS8; + else + d |= BITS7|PENABLE; + if ((tp->t_flags&EVENP) == 0) + d |= OPAR; + if ((tp->t_ospeed) == 3) /* 110 baud */ + d |= TWOSB; + addr->dhlpr = d; + spl0(); +} + +/* + * DH11 transmitter interrupt. + * Restart each line which used to be active but has + * terminated transmission since the last interrupt. + */ +dhxint(dev) +{ + register struct tty *tp; + register struct device *addr; + register d; + int ttybit, bar, *sbar; + + d = minor(dev); + addr = DHADDR + d; + addr->un.dhcsr &= ~XINT; + sbar = &dhsar[d]; + bar = *sbar & ~addr->dhbar; + d <<= 4; ttybit = 1; + + for(; bar; d++, ttybit <<= 1) { + if(bar&ttybit) { + *sbar &= ~ttybit; + bar &= ~ttybit; + tp = &dh11[d]; + if (tp->t_line) { + (*linesw[tp->t_line].l_start)(tp); + } else { + addr->un.dhcsrl = (d&017)|IENAB; + if (tp->t_state&FLUSH) + tp->t_state &= ~FLUSH; + else { + ndflush(&q3, addr->dhcar-q3.c_cf); + } + tp->t_state &= ~BUSY; + dhstart(tp); + } + } + } +} + +/* + * Start (restart) transmission on the given DH11 line. + */ +dhstart(tp) +register struct tty *tp; +{ + register struct device *addr; + register nch; + int s, d; + + /* + * If it's currently active, or delaying, + * no need to do anything. + */ + s = spl5(); + d = tp-dh11; + addr = (struct device *)tp->t_addr; + if (tp->t_state&(TIMEOUT|BUSY|TTSTOP) || (tp->t_xstate&XPAGE1)) + goto out; + + + /* + * If the writer was sleeping on output overflow, + * wake him when low tide is reached. + */ + if (tp->t_state&ASLEEP && tp->t_outq.c_cc<=TTLOWAT) { + tp->t_state &= ~ASLEEP; + if (tp->t_chan) + mcstart(tp->t_chan, (caddr_t)&tp->t_outq); else + wakeup((caddr_t)&tp->t_outq); + } + + if (tp->t_outq.c_cc == 0) + goto out; + + + + /* + * Find number of characters to transfer. + */ + if (tp->t_flags & RAW) { + nch = ndqb(&tp->t_outq, 0); + } else { + nch = ndqb(&tp->t_outq, 0200); + if (nch == 0) { + nch = getc(&tp->t_outq); + if(nch == 0200) + tp->t_xstate |= XPAGE1; + else + { + timeout(ttrstrt, (caddr_t)tp, (nch&0177)+6); + tp->t_state |= TIMEOUT; + } + goto out; + } + } + /* + * If any characters were set up, start transmission; + */ + if (nch) { + addr->un.dhcsrl = (d&017)|IENAB; + addr->dhcar = tp->t_outq.c_cf; + addr->dhbcr = -nch; + dhcc[d] = nch; + nch = 1<<(d&017); + addr->dhbar |= nch; + dhsar[d>>4] |= nch; + tp->t_state |= BUSY; + } + out: + splx(s); +} + + +/* + * Stop output on a line. + */ +dhstop(tp, flag) +register struct tty *tp; +{ + register struct device *addr; + register d, s; + + addr = (struct device *)tp->t_addr; + s = spl6(); + if (tp->t_state & BUSY) { + d = minor(tp->t_dev); + addr->un.dhcsrl = (d&017) | IENAB; + if ((tp->t_state&TTSTOP)==0) { + tp->t_state |= FLUSH; + } + addr->dhbcr = -1; + } + splx(s); +} + +dhtimer(dev) +{ +register d,cc; +register struct device *addr; + + addr = DHADDR; d = 0; + do { + cc = dhchars[d]; + dhchars[d] = 0; + if (cc > 50) + cc = 32; else + if (cc > 16) + cc = 16; else + cc = 0; + addr->dhsilo = cc; + addr += 1; + dhrint(d++); + } while (d < (NDH11+15)/16); + timeout(dhtimer, (caddr_t)0, DHTIME); +} diff --git a/tty/sys/dz.c b/tty/sys/dz.c new file mode 100644 index 0000000..2895db1 --- /dev/null +++ b/tty/sys/dz.c @@ -0,0 +1,397 @@ +/* + * DZ-11 driver + * + * The code here is quite different in spots from + * the released and untested dz driver + * If you want to use CARRIER on the device + * define CARRIER + * Peter Collinson + * July 2023 + */ + +#include "../h/param.h" +#include "../h/dir.h" +#include "../h/user.h" +#include "../h/tty.h" + +#define NDZ11 1 +#define NDZLINE NDZ11*8 + +/* + * Hardware bits + */ +#define CSRDFLT 050140 + +#define PRTYERR 010000 +#define FRAMERR 020000 + +#define _8BITS 030 +#define _7BITS 0120 +#define OPARITY 0200 +#define LPRDFLT 010040 + +#define DTROFF 0 +#define DTRON 1 + +#define SSPEED 7 + +#define DZADDR ((struct device *)0160100) + +struct tty dz11[NDZLINE]; + +char dzstab[] = { + 0, /* 0 baud */ + 020, /* 50 baud */ + 021, /* 75 baud */ + 022, /* 110 baud */ + 023, /* 134.5 baud */ + 024, /* 150 baud */ + 0, /* 200 baud - XXX */ + 025, /* 300 baud */ + 026, /* 600 baud */ + 027, /* 1200 baud */ + 030, /* 1800 baud */ + 032, /* 2400 baud */ + 034, /* 4800 baud */ + 036, /* 9600 baud */ + 0, /* EXTA */ + 0 /* EXTB */ +}; + +struct device { + int dzcsr; + int dzrbuf; + char dztcr; + char dzdtr; + char dztbuf; + char dzbrk; +}; + +#define dzlpr dzrbuf +#define dzmsr dzbrk + +#define SCANRATE 2 +#define SCANON 01 +#define SCANLOCK 02 + +char dzto[NDZLINE]; +char dzstat; +int ndz11 = NDZLINE; + +/* + * open a DZ-11 line + */ +dzopen(dev, flag) +{ + register struct tty *tp; + register line; + extern dzstart(); + + if((line = minor(dev)) >= NDZLINE){ + u.u_error = ENXIO; + return; + } + tp = &dz11[line]; + if((tp->t_state&(ISOPEN|WOPEN)) == 0){ + tp->t_oproc = dzstart; + tp->t_iproc = NULL; + ttychars(tp); + tp->t_ispeed = SSPEED; + tp->t_ospeed = SSPEED; + tp->t_flags = XTABS|CRMOD|ECHO|LCASE; + dzparam(line); + } + else if((tp->t_state&XCLUDE) && u.u_uid != 0){ + u.u_error = EBUSY; + return; + } + dzmodem(line, DTRON); +#ifdef CARRIER + spl6(); + while((tp->t_state&CARR_ON) == 0){ + tp->t_state |= WOPEN; + sleep((caddr_t)&tp->t_rawq, TTIPRI); + } + ttyopen(dev, tp); + spl0(); +#else + tp->t_state |= CARR_ON; + ttyopen(dev, tp); +#endif CARRIER +} + +/* + * close a DZ-11 line + */ +dzclose(dev) +{ + register struct tty *tp; + register line; + + line = minor(dev); + tp = &dz11[line]; + tp->t_pgrp = 0; + wflushtty(tp); + if(tp->t_state&HUPCLS){ + dzmodem(line, DTROFF); + } + tp->t_state &= CARR_ON; + tp->t_xstate = 0; +} + +/* + * read from a DZ-11 line + */ +dzread(dev) +{ + ttread(&dz11[minor(dev)]); +} + +/* + * write on a DZ-11 line + */ +dzwrite(dev) +{ + ttwrite(&dz11[minor(dev)]); +} + +/* + * ioctl for DZ-11 + */ +dzioctl(dev, cmd, addr, flag) +{ + register struct tty *tp; + register line; + + line = minor(dev); + tp = &dz11[line]; + if(ttioccomm(cmd, tp, (caddr_t)addr, line)){ + if(cmd == TIOCSETP || cmd == TIOCSETN || cmd == TIOCSETA) + dzparam(line); + } + else + u.u_error = ENOTTY; +} + +/* + * set DZ-11 hardware registers + */ +dzparam(dev) +{ + register struct tty *tp; + register struct device *dzp; + register lpr; + + tp = &dz11[dev]; + dzp = &DZADDR[dev>>3]; + dzp->dzcsr = CSRDFLT; + if(dzstat == 0){ + dzscan(); + dzstat |= SCANON; + } + if(tp->t_ispeed == 0){ + dzmodem(dev, DTROFF); + return; + } + lpr = (dzstab[tp->t_ispeed] << 8)|LPRDFLT|(dev&07); + if((tp->t_flags&(ODDP|EVENP)) == 0 || (tp->t_flags&RAW)) + lpr |= _8BITS; + else { + lpr |= _7BITS; + if((tp->t_flags&EVENP) == 0) + lpr |= OPARITY; + } + dzp->dzlpr = lpr; +} + +/* + * DZ-11 receiver interrupt + */ +dzrint(dev) +{ + register struct tty *tp; + register struct device *dzp; + register c, i, n; + + dzstat |= SCANLOCK; + spl5(); + n = minor(dev); + for(i = 0; i < NDZ11; i++){ + dzp = &DZADDR[n]; + while((c = dzp->dzrbuf) < 0){ + tp = &dz11[((c >> 8)&07)|(n << 3)]; + if(tp >= &dz11[NDZLINE]) + continue; + if((tp->t_state&ISOPEN) == 0){ + wakeup((caddr_t)&tp->t_rawq); + continue; + } + if(c&FRAMERR){ + if(tp->t_flags&RAW) + c = 0; + else + c = CINTR; + } + if(c&PRTYERR){ + if((tp->t_flags&(EVENP|ODDP)) == EVENP || (tp->t_flags&(EVENP|ODDP)) == ODDP) + continue; + } + ttyinput(c, tp); + } + if(++n >= NDZ11) + n = 0; + } + dzstat &= ~SCANLOCK; +} + +/* + * DZ-11 transmitter interrupt + */ +dzxint(dev) +{ + register struct tty *tp; + register struct device *dzp; + register i, n; + + n = minor(dev); + for(i = 0; i < NDZ11; i++){ + dzp = &DZADDR[n]; + while(dzp->dzcsr < 0){ + tp = &dz11[((n << 3)|(dzp->dzcsr >> 8)&07)]; + dzp->dztbuf = tp->t_char; + tp->t_state &= ~BUSY; + dzstart(tp); + } + if(++n >= NDZ11) + n = 0; + } +} + +/* + * start routine for DZ-11 + */ +dzstart(tp) +register struct tty *tp; +{ + register struct device *dzp; + register unit, c; + int s; + + unit = tp - dz11; + dzp = &DZADDR[unit>>3]; + unit = (1 << (unit&07)); + s = spl5(); + if((tp->t_state&(TIMEOUT|BUSY)) || (tp->t_xstate&XPAGE1)){ + splx(s); + return; + } + if(tp->t_state&TTSTOP){ + dzp->dztcr &= ~unit; + splx(s); + return; + } + if((c = getc(&tp->t_outq)) >= 0){ + if(c >= 0200 && (tp->t_flags&RAW) == 0){ + dzp->dztcr &= ~unit; + if(c == 0200){ + tp->t_xstate |= XPAGE1; + } else { + tp->t_state |= TIMEOUT; + dzto[tp - dz11] = (((c&0177)+6+(SCANRATE-1))/SCANRATE)+1; + } + } else { + tp->t_char = c; + tp->t_state |= BUSY; + dzp->dztcr |= unit; + } + if((tp->t_outq.c_cc <= TTLOWAT) && (tp->t_state&ASLEEP)){ + tp->t_state &= ~ASLEEP; +#ifdef MX + if(tp->t_chan) + mcstart(tp->t_chan, (caddr_t)&tp->t_outq); + else +#endif MX + wakeup((caddr_t)&tp->t_outq); + } + } + else + dzp->dztcr &= ~unit; + splx(s); +} + +/* + * set/reset DTR + */ +dzmodem(dev, flag) +{ + register struct device *dzp; + register bit; + + dzp = &DZADDR[dev>>3]; + bit = (1 << (dev&07)); + if(flag == DTROFF) + dzp->dzdtr &= ~bit; + else + dzp->dzdtr |= bit; +} + +/* + * scan lines for input, and process any timeouts + */ +dzscan() +{ + register n; +#ifdef CARRIER + static cscan; + + if(cscan <= 0){ + dzcarrier(); + cscan = HZ*2; + } + else + cscan -= SCANRATE; +#endif CARRIER + for(n = 0; n < NDZLINE; n++){ + if((dzto[n] > 0) && (--dzto[n] <= 0)){ + dz11[n].t_state &= ~TIMEOUT; + dzstart(&dz11[n]); + } + } + if((dzstat&SCANLOCK) == 0) + dzrint(0); + timeout(dzscan, (caddr_t)0, SCANRATE); +} + +#ifdef CARRIER +/* + * scan DZ-11 lines for carrier transitions + */ +dzcarrier() +{ + register struct tty *tp; + register struct device *dzp; + register i; + char bit; + + for(i = 0; i < NDZLINE; i++){ + dzp = &DZADDR[i>>3]; + tp = &dz11[i]; + bit = (1 << (i&07)); + if(dzp->dzmsr&bit){ + if((tp->t_state&CARR_ON) == 0){ + wakeup((caddr_t)&tp->t_rawq); + tp->t_state |= CARR_ON; + } + } else { + if(tp->t_state&CARR_ON){ + if(tp->t_state&ISOPEN){ + signal(tp->t_pgrp, SIGHUP); + dzp->dzdtr &= ~bit; + flushtty(tp); + } + tp->t_state &= ~CARR_ON; + } + } + } +} +#endif CARRIER diff --git a/tty/sys/kl.c b/tty/sys/kl.c new file mode 100644 index 0000000..a1831da --- /dev/null +++ b/tty/sys/kl.c @@ -0,0 +1,224 @@ +/* + * KL/DL-11 driver + * + * Defines are + * MX to include calls to the MX code, it's not clear that + * this works. + * Peter Collinson + * July 2023 + + */ +#include "../h/param.h" +#include "../h/conf.h" +#include "../h/dir.h" +#include "../h/user.h" +#include "../h/tty.h" +#include "../h/systm.h" + +/* base address */ +#define KLADDR ((struct device *)0177560) /* console */ +#define KLBASE ((struct device *)0176500) /* kl and dl11-a */ +#define DLBASE ((struct device *)0175610) /* dl-e */ +#define NKL11 1 +#define NDL11 0 +#define DSRDY 02 +#define RDRENB 01 +#define DLDELAY 4 /* Extra delay for DL's (double buff) */ + +struct tty kl11[NKL11+NDL11]; +int klstart(); +int ttrstrt(); + +char maptab[]; + +struct device { + int rcsr; + int rbuf; + int tcsr; + int tbuf; +}; + +klopen(dev, flag) +dev_t dev; +{ + register struct device *addr; + register struct tty *tp; + register d; + + d = minor(dev); + if(d >= NKL11+NDL11){ + u.u_error = ENXIO; + return; + } + tp = &kl11[d]; + /* + * set up minor 0 to address KLADDR + * set up minor 1 thru NKL11-1 to address from KLBASE + * set up minor NKL11 on to address from DLBASE + */ + if(d == 0) + addr = KLADDR; + else if(d < NKL11) + addr = KLBASE + (d-1); + else + addr = DLBASE + (d-NKL11); + tp->t_addr = (caddr_t)addr; + tp->t_oproc = klstart; + if((tp->t_state&ISOPEN) == 0){ + tp->t_state = ISOPEN|CARR_ON; + tp->t_flags = LCASE|ECHO|XTABS|CRMOD; + ttychars(tp); + } + else + if((tp->t_state&XCLUDE) && u.u_uid != 0) { + u.u_error = EBUSY; + return; + } + addr->rcsr |= IENABLE|DSRDY|RDRENB; + addr->tcsr |= IENABLE; + ttyopen(dev, tp); +} + +klclose(dev, flag) +dev_t dev; +int flag; +{ + register struct tty *tp; + + tp = &kl11[minor(dev)]; + ttyclose(tp); +} + +klread(dev) +dev_t dev; +{ + ttread(&kl11[minor(dev)]); +} + +klwrite(dev) +dev_t dev; +{ + ttwrite(&kl11[minor(dev)]); +} + +klxint(dev) +dev_t dev; +{ + register struct tty *tp; + + tp = &kl11[minor(dev)]; + ttstart(tp); + if(tp->t_outq.c_cc == 0 || tp->t_outq.c_cc == TTLOWAT){ +#ifdef MX + if(tp->t_chan) + mcstart(tp->t_chan, (caddr_t)&tp->t_outq); + else +#endif MX + wakeup((caddr_t)&tp->t_outq); + } +} + +klrint(dev) +dev_t dev; +{ + register int c; + register struct device *addr; + register struct tty *tp; + + tp = &kl11[minor(dev)]; + addr = (struct device *)tp->t_addr; + c = addr->rbuf; + addr->rcsr |= RDRENB; + ttyinput(c, tp); +} + +klioctl(dev, cmd, addr, flag) +caddr_t addr; +dev_t dev; +{ + if(ttioccom(cmd, &kl11[minor(dev)], addr, dev) == 0) + u.u_error = ENOTTY; +} + +klstart(tp) +register struct tty *tp; +{ + register c; + register struct device *addr; + + addr = (struct device *)tp->t_addr; + if(((addr->tcsr&DONE) == 0) + || (tp->t_state&TTSTOP) + || (tp->t_xstate&XPAGE1)) + return; + if((c = getc(&tp->t_outq)) >= 0){ + if(tp->t_flags&RAW) + addr->tbuf = c; + else if(c <= 0177) + addr->tbuf = c | (maptab[c]&0200); + else if(c == 0200) + tp->t_xstate |= XPAGE1; + else { + timeout(ttrstrt, (caddr_t)tp, (c&0177) + DLDELAY); + tp->t_state |= TIMEOUT; + } + } + else if(tp->t_state&ASLEEP){ + tp->t_state &= ~ASLEEP; +#ifdef MX + if(tp->t_chan) + mcstart(tp->t_chan, (caddr_t)&tp->t_outq); + else +#endif MX + wakeup((caddr_t)&tp->t_outq); + } +} + +char *msgbufp = msgbuf; /* Next saved printf character */ +/* + * Print a character on console. + * Attempts to save and restore device + * status. + * If the switches are 0, all + * printing is inhibited. + * + * Whether or not printing is inhibited, + * the last MSGBUFS characters + * are saved in msgbuf for inspection later. + */ +putchar(c) +register c; +{ + register s, timo; + + if(c != '\0' && c != '\r' && c != 0177){ + *msgbufp++ = c; + if(msgbufp >= &msgbuf[MSGBUFS]) + msgbufp = msgbuf; + } + /* + * If last char was a break or null, don't print + */ + if((KLADDR->rbuf&0177) == 0) + return; + timo = 30000; + /* + * Try waiting for the console tty to come ready, + * otherwise give up after a reasonable time. + */ + while((KLADDR->tcsr&0200) == 0) + if(--timo == 0) + break; + if(c == 0) + return; + s = KLADDR->tcsr; + KLADDR->tcsr = 0; + KLADDR->tbuf = c; + if(c == '\n'){ + putchar('\r'); + putchar(0177); + putchar(0177); + } + putchar(0); + KLADDR->tcsr = s; +} diff --git a/tty/sys/orig/dc.c b/tty/sys/orig/dc.c new file mode 100644 index 0000000..cc43b4a --- /dev/null +++ b/tty/sys/orig/dc.c @@ -0,0 +1,240 @@ +#include "../h/param.h" +#include "../h/conf.h" +#include "../h/dir.h" +#include "../h/user.h" +#include "../h/tty.h" +#include "../h/systm.h" + +/* + * Base address of DC-11's. Minor device i is at + * DCADDR + 10*i. + */ +#define DCADDR (struct device *)0174000 + +/* + * Number of DC's for which table space is allocated. + */ +#define NDC11 4 + +/* + * Control bits in device registers + */ +#define CDLEAD 01 +#define CARRIER 04 +#define SPEED1 010 +#define STOP1 0400 +#define RQSEND 01 +#define PARITY 040 +#define ERROR 0100000 +#define CTRANS 040000 +#define RINGIND 020000 + + +struct tty dc11[NDC11]; + +struct device { + int dcrcsr; + int dcrbuf; + int dctcsr; + int dctbuf; +}; + +/* + * Input-side speed and control bit table. + * Each DC11 has 4 speeds which correspond to the 4 non-zero entries. + * The table index is the same as the speed-selector + * number for the DH11. + * Attempts to set the speed to a zero entry are ignored. + */ +int dcrstab[] = { + 0, /* 0 baud */ + 0, /* 50 baud */ + 0, /* 75 baud */ + 0, /* 110 baud */ + 01101, /* 134.5 baud: 7b/ch, speed 0 */ + 0111, /* 150 baud: 8b/ch, speed 1 */ + 0, /* 200 baud */ + 0121, /* 300 baud: 8b/ch, speed 2 */ + 0, /* 600 baud */ + 0131, /* 1200 baud */ + 0, /* 1800 baud */ + 0, /* 2400 baud */ + 0, /* 4800 baud */ + 0, /* 9600 baud */ + 0, /* X0 */ + 0, /* X1 */ +}; + +/* + * Transmitter speed table + */ +int dctstab[] = { + 0, /* 0 baud */ + 0, /* 50 baud */ + 0, /* 75 baud */ + 0, /* 110 baud */ + 0501, /* 134.5 baud: stop 1 */ + 0511, /* 150 baud */ + 0, /* 200 baud */ + 0521, /* 300 baud */ + 0, /* 600 baud */ + 0531, /* 1200 baud */ + 0, /* 1800 baud */ + 0, /* 2400 baud */ + 0, /* 4800 baud */ + 0, /* 9600 baud */ + 0, /* X0 */ + 0, /* X1 */ +}; + +/* + * Open a DC11, waiting until carrier is established. + * Default initial conditions are set up on the first open. + * t_state's CARR_ON bit is a pure copy of the hardware + * CARRIER bit, and is only used to regularize + * carrier tests in general tty routines. + */ +dcopen(dev, flag) +dev_t dev; +{ + register struct tty *tp; + register struct device *addr; + extern int klstart(); + int s; + + if (minor(dev) >= NDC11) { + u.u_error = ENXIO; + return; + } + tp = &dc11[minor(dev)]; + addr = DCADDR + minor(dev); + tp->t_addr = (caddr_t)addr; + tp->t_state |= WOPEN; + s = spl5(); + addr->dcrcsr |= IENABLE|CDLEAD; + if ((tp->t_state&ISOPEN) == 0) { + tp->t_erase = CERASE; + tp->t_kill = CKILL; + addr->dcrcsr = IENABLE|CDLEAD|SPEED1; + addr->dctcsr = IENABLE|SPEED1|STOP1|RQSEND; + tp->t_state = ISOPEN | WOPEN; + tp->t_flags = ODDP|EVENP|ECHO; + tp->t_oproc = klstart; + } + if (addr->dcrcsr & CARRIER) + tp->t_state |= CARR_ON; + splx(s); + while ((tp->t_state & CARR_ON) == 0) + sleep((caddr_t)&tp->t_rawq, TTIPRI); + ttyopen(dev, tp); +} + +/* + * Close a dc11 + */ +dcclose(dev) +dev_t dev; +{ + register struct tty *tp; + + tp = &dc11[minor(dev)]; + if (tp->t_state&HUPCLS) + ((struct device *)(tp->t_addr))->dcrcsr &= ~CDLEAD; + ttyclose(tp); +} + +/* + * Read a DC11 + */ +dcread(dev) +dev_t dev; +{ + ttread(&dc11[minor(dev)]); +} + +/* + * Write a DC11 + */ +dcwrite(dev) +dev_t dev; +{ + ttwrite(&dc11[minor(dev)]); +} + +/* + * DC11 transmitter interrupt. + */ +dcxint(dev) +dev_t dev; +{ + register struct tty *tp; + + tp = &dc11[minor(dev)]; + ttstart(tp); + if (tp->t_outq.c_cc == 0 || tp->t_outq.c_cc == TTLOWAT) + wakeup((caddr_t)&tp->t_outq); +} + +/* + * DC11 receiver interrupt. + */ +dcrint(dev) +dev_t dev; +{ + register struct tty *tp; + register int c, csr; + + tp = &dc11[minor(dev)]; + c = ((struct device *)(tp->t_addr))->dcrbuf; + /* + * If carrier is off, and an open is not in progress, + * knock down the CD lead to hang up the local dataset + * and signal a hangup. + */ + if (((csr = ((struct device *)(tp->t_addr))->dcrcsr) & CARRIER) == 0) { + if ((tp->t_state&WOPEN) == 0) { + ((struct device *)(tp->t_addr))->dcrcsr &= ~CDLEAD; + if (tp->t_state & CARR_ON) + signal(tp->t_pgrp, SIGHUP); + flushtty(tp); + } + tp->t_state &= ~CARR_ON; + return; + } + if (csr&ERROR || (tp->t_state&ISOPEN)==0) { + if (tp->t_state&WOPEN && csr&CARRIER) + tp->t_state |= CARR_ON; + wakeup((caddr_t)tp); + return; + } + csr &= PARITY; + if (csr&&(tp->t_flags&ODDP) || !csr&&(tp->t_flags&EVENP)) + ttyinput(c, tp); +} + +/* + * DC11 stty/gtty. + * Perform general functions and set speeds. + */ +dcioctl(dev, cmd, addr, flag) +dev_t dev; +caddr_t addr; +{ + register struct tty *tp; + register r; + + tp = &dc11[minor(dev)]; + if (ttioccom(cmd, &dc11[minor(dev)], addr, dev) == 0) { + u.u_error = ENOTTY; + return; + } + if (cmd == TIOCSETP) { + r = dcrstab[tp->t_ispeed]; + if (r) + ((struct device *)(tp->t_addr))->dcrcsr = r; + else + ((struct device *)(tp->t_addr))->dcrcsr &= ~CDLEAD; + r = dctstab[tp->t_ospeed]; + ((struct device *)(tp->t_addr))->dctcsr = r; + } +} diff --git a/tty/sys/orig/dh.c b/tty/sys/orig/dh.c new file mode 100644 index 0000000..4988dc7 --- /dev/null +++ b/tty/sys/orig/dh.c @@ -0,0 +1,398 @@ +/* + * DH-11 driver + * This driver calls on the DHDM driver. + * If the DH has no DM11-BB, then the latter will + * be fake. To insure loading of the correct DM code, + * lib2 should have dhdm.o, dh.o and dhfdm.o in that order. + */ + +#include "../h/param.h" +#include "../h/conf.h" +#include "../h/dir.h" +#include "../h/user.h" +#include "../h/tty.h" + +#define q3 tp->t_outq +#define DHADDR ((struct device *)0160020) +#define NDH11 16 /* number of lines */ + +struct tty dh11[NDH11]; +char dhcc[NDH11]; +int dhchars[(NDH11+15)/16]; +int ndh11 = NDH11; +int dhstart(); +int ttrstrt(); + +/* + * Hardware control bits + */ +#define BITS6 01 +#define BITS7 02 +#define BITS8 03 +#define TWOSB 04 +#define PENABLE 020 +/* DEC manuals incorrectly say this bit causes generation of even parity. */ +#define OPAR 040 +#define HDUPLX 040000 + +#define IENAB 030100 +#define PERROR 010000 +#define FRERROR 020000 +#define OVERRUN 040000 +#define XINT 0100000 +#define SSPEED 7 /* standard speed: 300 baud */ +#define NSILO 16 +#define DHTIME 6 +extern int dhtimer(); + +/* + * DM control bits + */ +#define TURNON 03 /* CD lead + line enable */ +#define TURNOFF 01 /* line enable */ +#define RQS 04 /* request to send */ + +/* + * Software copy of last dhbar + */ +int dhsar[(NDH11+15)/16]; + +struct device +{ + union { + int dhcsr; + char dhcsrl; + } un; + int dhnxch; + int dhlpr; + char *dhcar; + int dhbcr; + int dhbar; + int dhbreak; + int dhsilo; +}; + +/* + * Open a DH11 line. + */ +dhopen(dev, flag) +{ + register struct tty *tp; + register d; + register struct device *addr; + static timer_on; + int s; + + d = minor(dev); + if (d >= NDH11) { + u.u_error = ENXIO; + return; + } + tp = &dh11[d]; + addr = DHADDR; + addr += d>>4; + tp->t_addr = (caddr_t)addr; + tp->t_oproc = dhstart; + tp->t_iproc = NULL; + tp->t_state |= WOPEN; + s = spl6(); + if (!timer_on) { + timer_on++; + timeout(dhtimer, (caddr_t)0, DHTIME); + } + splx(s); + addr->un.dhcsr |= IENAB; + if ((tp->t_state&ISOPEN) == 0) { + ttychars(tp); + tp->t_ispeed = SSPEED; + tp->t_ospeed = SSPEED; + tp->t_flags = ODDP|EVENP|ECHO; + dhparam(d); + } + if (tp->t_state&XCLUDE && u.u_uid!=0) { + u.u_error = EBUSY; + return; + } + dmopen(d); + (*linesw[tp->t_line].l_open)(dev,tp); +} + +/* + * Close a DH11 line. + */ +dhclose(dev, flag) +dev_t dev; +int flag; +{ + register struct tty *tp; + register d; + + d = minor(dev); + tp = &dh11[d]; + (*linesw[tp->t_line].l_close)(tp); + if (tp->t_state&HUPCLS) + dmctl(d, TURNOFF); + ttyclose(tp); +} + +/* + * Read from a DH11 line. + */ +dhread(dev) +{ +register struct tty *tp; + + tp = &dh11[minor(dev)]; + (*linesw[tp->t_line].l_read)(tp); +} + +/* + * write on a DH11 line + */ +dhwrite(dev) +{ +register struct tty *tp; + + tp = &dh11[minor(dev)]; + (*linesw[tp->t_line].l_write)(tp); +} + +/* + * DH11 receiver interrupt. + */ +dhrint(dev) +{ + register struct tty *tp; + register int c; + register struct device *addr; + + addr = DHADDR; + addr += minor(dev); + while ((c = addr->dhnxch) < 0) { /* char. present */ + tp = &dh11[(minor(dev)<<4) + ((c>>8)&017)]; + dhchars[minor(dev)]++; + if (tp >= &dh11[NDH11]) + continue; + if((tp->t_state&ISOPEN)==0) { + wakeup((caddr_t)tp); + continue; + } + if (c&PERROR) + if ((tp->t_flags&(EVENP|ODDP))==EVENP + || (tp->t_flags&(EVENP|ODDP))==ODDP ) + continue; + if (c&FRERROR) /* break */ + if (tp->t_flags&RAW) + c = 0; /* null (for getty) */ + else + c = 0177; /* DEL (intr) */ + (*linesw[tp->t_line].l_rint)(c,tp); + } +} + +/* + * stty/gtty for DH11 + */ +dhioctl(dev, cmd, addr, flag) +caddr_t addr; +{ + register struct tty *tp; + + tp = &dh11[minor(dev)]; + if (ttioccomm(cmd, tp, addr, dev)) { + if (cmd==TIOCSETP||cmd==TIOCSETN) + dhparam(dev); + } else + u.u_error = ENOTTY; +} + +/* + * Set parameters from open or stty into the DH hardware + * registers. + */ +dhparam(dev) +{ + register struct tty *tp; + register struct device *addr; + register d; + + d = minor(dev); + tp = &dh11[d]; + addr = (struct device *)tp->t_addr; + spl5(); + addr->un.dhcsrl = (d&017) | IENAB; + /* + * Hang up line? + */ + if ((tp->t_ispeed)==0) { + tp->t_state |= HUPCLS; + dmctl(d, TURNOFF); + return; + } + d = ((tp->t_ospeed)<<10) | ((tp->t_ispeed)<<6); + if ((tp->t_ispeed) == 4) /* 134.5 baud */ + d |= BITS6|PENABLE|HDUPLX; + else if (tp->t_flags&RAW) + d |= BITS8; + else + d |= BITS7|PENABLE; + if ((tp->t_flags&EVENP) == 0) + d |= OPAR; + if ((tp->t_ospeed) == 3) /* 110 baud */ + d |= TWOSB; + addr->dhlpr = d; + spl0(); +} + +/* + * DH11 transmitter interrupt. + * Restart each line which used to be active but has + * terminated transmission since the last interrupt. + */ +dhxint(dev) +{ + register struct tty *tp; + register struct device *addr; + register d; + int ttybit, bar, *sbar; + + d = minor(dev); + addr = DHADDR + d; + addr->un.dhcsr &= ~XINT; + sbar = &dhsar[d]; + bar = *sbar & ~addr->dhbar; + d <<= 4; ttybit = 1; + + for(; bar; d++, ttybit <<= 1) { + if(bar&ttybit) { + *sbar &= ~ttybit; + bar &= ~ttybit; + tp = &dh11[d]; + if (tp->t_line) { + (*linesw[tp->t_line].l_start)(tp); + } else { + addr->un.dhcsrl = (d&017)|IENAB; + if (tp->t_state&FLUSH) + tp->t_state &= ~FLUSH; + else { + ndflush(&q3, addr->dhcar-q3.c_cf); + } + tp->t_state &= ~BUSY; + dhstart(tp); + } + } + } +} + +/* + * Start (restart) transmission on the given DH11 line. + */ +dhstart(tp) +register struct tty *tp; +{ + register struct device *addr; + register nch; + int s, d; + + /* + * If it's currently active, or delaying, + * no need to do anything. + */ + s = spl5(); + d = tp-dh11; + addr = (struct device *)tp->t_addr; + if (tp->t_state&(TIMEOUT|BUSY|TTSTOP)) + goto out; + + + /* + * If the writer was sleeping on output overflow, + * wake him when low tide is reached. + */ + if (tp->t_state&ASLEEP && tp->t_outq.c_cc<=TTLOWAT) { + tp->t_state &= ~ASLEEP; + if (tp->t_chan) + mcstart(tp->t_chan, (caddr_t)&tp->t_outq); else + wakeup((caddr_t)&tp->t_outq); + } + + if (tp->t_outq.c_cc == 0) + goto out; + + + + /* + * Find number of characters to transfer. + */ + if (tp->t_flags & RAW) { + nch = ndqb(&tp->t_outq, 0); + } else { + nch = ndqb(&tp->t_outq, 0200); + if (nch == 0) { + nch = getc(&tp->t_outq); + timeout(ttrstrt, (caddr_t)tp, (nch&0177)+6); + tp->t_state |= TIMEOUT; + goto out; + } + } + /* + * If any characters were set up, start transmission; + */ + if (nch) { + addr->un.dhcsrl = (d&017)|IENAB; + addr->dhcar = tp->t_outq.c_cf; + addr->dhbcr = -nch; + dhcc[d] = nch; + nch = 1<<(d&017); + addr->dhbar |= nch; + dhsar[d>>4] |= nch; + tp->t_state |= BUSY; + } + out: + splx(s); +} + + +/* + * Stop output on a line. + */ +dhstop(tp, flag) +register struct tty *tp; +{ + register struct device *addr; + register d, s; + + addr = (struct device *)tp->t_addr; + s = spl6(); + if (tp->t_state & BUSY) { + d = minor(tp->t_dev); + addr->un.dhcsrl = (d&017) | IENAB; + if ((tp->t_state&TTSTOP)==0) { + tp->t_state |= FLUSH; + } + addr->dhbcr = -1; + } + splx(s); +} + +dhtimer(dev) +{ +register d,cc; +register struct device *addr; + + addr = DHADDR; d = 0; + do { + cc = dhchars[d]; + dhchars[d] = 0; + if (cc > 50) + cc = 32; else + if (cc > 16) + cc = 16; else + cc = 0; + addr->dhsilo = cc; + addr += 1; + dhrint(d++); + } while (d < (NDH11+15)/16); + timeout(dhtimer, (caddr_t)0, DHTIME); +} + diff --git a/tty/sys/orig/dz.c b/tty/sys/orig/dz.c new file mode 100644 index 0000000..f28b536 --- /dev/null +++ b/tty/sys/orig/dz.c @@ -0,0 +1,267 @@ +/* + * DZ11 driver + * CAUTION -- MODIFIED FROM WORKING VERSION BUT NEVER PROPERLY TESTED + */ + +#include "../h/param.h" +#include "../h/dir.h" +#include "../h/user.h" +#include "../h/tty.h" + +struct device *dz_addr[] = { (struct device *)0160100, (struct device *)0160110}; +int dz_cnt = 16; +struct tty dz_tty[16]; +char dz_stat; + +char dz_speeds[] = { + 0, 020, 021, 022, 023, 024, 0, 025, + 026, 027, 030, 032, 034, 036, 0, 0, + }; + +#define BITS7 020 +#define BITS8 030 +#define TWOSB 040 +#define PENABLE 0100 +#define OPAR 0200 +#define RCVENA 010000 + +#define IE 040140 +#define PERROR 010000 +#define FRERROR 020000 +#define SSPEED 7 /* standard speed: 300 baud */ + +struct device { + int dzcsr, dzrbuf; + char dztcr, dzdtr; + char dztbuf, dzbrk; +}; +#define dzlpr dzrbuf +#define dzmsr dzbrk + +#define ON 1 +#define OFF 0 + + +dzopen(dev, flag) +{ + register struct tty *tp; + int x; + extern dzstart(), dzscan(); + + x = dev; + dev = minor(dev); + if (dev >= dz_cnt) { + u.u_error = ENXIO; + return; + } + tp = &dz_tty[dev]; + if ((tp->t_state&(ISOPEN|WOPEN)) == 0) { + tp->t_oproc = dzstart; + tp->t_iproc = NULL; + ttychars(tp); + tp->t_ispeed = SSPEED; + tp->t_ospeed = SSPEED; + tp->t_flags = ODDP|EVENP|ECHO; + dzparam(dev); + } + dzmodem(dev, ON); + spl6(); + while ((tp->t_state&CARR_ON)==0) { + tp->t_state |= WOPEN; + sleep((caddr_t)&tp->t_rawq, TTIPRI); + } + ttyopen(x,tp); + spl0(); +} + +dzclose(dev) +{ + register struct tty *tp; + + dev = minor(dev); + tp = &dz_tty[dev]; + wflushtty(tp); + if (tp->t_state&HUPCLS) { + dzmodem(dev, OFF); + } + tp->t_state &= CARR_ON; +} + +dzread(dev) +{ + dev = minor(dev); + ttread(&dz_tty[dev]); +} + +dzwrite(dev) +{ + dev = minor(dev); + ttwrite(&dz_tty[dev]); +} + +dzioctl(dev, cmd, addr, flag) +{ + register struct tty *tp; + + dev = minor(dev); + tp = &dz_tty[dev]; + if (ttioccomm(cmd, tp, (caddr_t)addr, dev)) { + if (cmd==TIOCSETP||cmd==TIOCSETN) + dzparam(dev); + } else { + u.u_error = ENOTTY; + } +} + +dzparam(dev) +{ + register struct tty *tp; + register struct device *dzaddr; + register lpr; + + tp = &dz_tty[dev]; + dzaddr= dz_addr[dev>>3]; + dzaddr->dzcsr = IE; + if (dz_stat==0) { + dzscan(); + dz_stat++; + } + if (tp->t_ispeed==0) { /* Hang up line */ + dzmodem(dev, OFF); + return; + } + lpr = (dz_speeds[tp->t_ispeed]<<8)|(dev&07); + if (tp->t_flags&RAW) + lpr |= BITS8; + else + lpr |= BITS7|PENABLE; + if ((tp->t_flags&EVENP)==0) + lpr |= OPAR; + if (tp->t_ispeed == 3) /* 110 baud */ + lpr |= TWOSB; + dzaddr->dzlpr = lpr; +} + +dzrint(dev) +{ + register struct tty *tp; + register c; + register struct device *dzaddr; + + dzaddr = dz_addr[dev]; + while ((c = dzaddr->dzrbuf) < 0) { /* char. present */ + tp = &dz_tty[((c>>8)&07)|(dev<<3)]; + if (tp >= &dz_tty[dz_cnt]) + continue; + if((tp->t_state&ISOPEN)==0) { + wakeup((caddr_t)&tp->t_rawq); + continue; + } + if (c&FRERROR) /* break */ + if (tp->t_flags&RAW) + c = 0; /* null (for getty) */ + else + c = 0177; /* DEL (intr) */ + if (c&PERROR) + if ((tp->t_flags&(EVENP|ODDP))==EVENP + || (tp->t_flags&(EVENP|ODDP))==ODDP ) + continue; + ttyinput(c, tp); + } +} + +dzxint(dev) +{ + register struct tty *tp; + register struct device *dzaddr; + + dzaddr = dz_addr[dev]; + while(dzaddr->dzcsr<0) { /* TX rdy */ + tp = &dz_tty[((dev<<3)|(dzaddr->dzcsr>>8)&07)]; + dzaddr->dztbuf = tp->t_char; + tp->t_state &= ~BUSY; + dzstart(tp); + } +} + +dzstart(tp) +register struct tty *tp; +{ + register unit, c; + int s; + struct device *dzaddr; + extern ttrstrt(); + + unit = tp - dz_tty; + dzaddr = dz_addr[unit>>3]; + unit = 1<<(unit&07); + s = spl5(); + if (tp->t_state&(TIMEOUT|BUSY)) { + splx(s); + return; + } + if (tp->t_state&TTSTOP) { + dzaddr->dztcr &= ~unit; + splx(s); + return; + } + if ((c=getc(&tp->t_outq)) >= 0) { + if (c>=0200 && (tp->t_flags&RAW)==0) { + dzaddr->dztcr &= ~unit; + tp->t_state |= TIMEOUT; + timeout(ttrstrt, (caddr_t)tp, (c&0177)+6); + } else { + tp->t_char = c; + tp->t_state |= BUSY; + dzaddr->dztcr |= unit; + } + if (tp->t_outq.c_cc<=TTLOWAT && tp->t_state&ASLEEP) { + tp->t_state &= ~ASLEEP; + wakeup((caddr_t)&tp->t_outq); + } + } else + dzaddr->dztcr &= ~unit; + splx(s); +} + +dzmodem(dev, flag) +{ + register struct device *dzaddr; + register bit; + + dzaddr = dz_addr[dev>>3]; + bit = 1<<(dev&07); + if (flag==OFF) + dzaddr->dzdtr &= ~bit; + else dzaddr->dzdtr |= bit; +} + +dzscan() +{ + register i; + register struct device *dzaddr; + register struct tty *tp; + char bit; + + for (i=0; i>3]; + tp = &dz_tty[i]; + bit = 1<<(i&07); + if (dzaddr->dzmsr&bit) { + if ((tp->t_state&CARR_ON)==0) { + wakeup((caddr_t)&tp->t_rawq); + tp->t_state |= CARR_ON; + } + } else { + if (tp->t_state&CARR_ON) { + if (tp->t_state&ISOPEN) { + signal(tp->t_pgrp, SIGHUP); + dzaddr->dzdtr &= ~bit; + flushtty(tp); + } + tp->t_state &= ~CARR_ON; + } + } + } + timeout(dzscan, (caddr_t)0, 120); +} diff --git a/tty/sys/orig/kl.c b/tty/sys/orig/kl.c new file mode 100644 index 0000000..07d83f8 --- /dev/null +++ b/tty/sys/orig/kl.c @@ -0,0 +1,201 @@ +/* + * KL/DL-11 driver + */ +#include "../h/param.h" +#include "../h/conf.h" +#include "../h/dir.h" +#include "../h/user.h" +#include "../h/tty.h" +#include "../h/systm.h" + +/* base address */ +#define KLADDR ((struct device *)0177560) /* console */ +#define KLBASE ((struct device *)0176500) /* kl and dl11-a */ +#define DLBASE ((struct device *)0175610) /* dl-e */ +#define NKL11 1 +#define NDL11 0 +#define DSRDY 02 +#define RDRENB 01 +#define DLDELAY 4 /* Extra delay for DL's (double buff) */ + +#define NL1 000400 +#define NL2 001000 +#define CR2 020000 +#define FF1 040000 +#define TAB1 002000 + +struct tty kl11[NKL11+NDL11]; +int klstart(); +int ttrstrt(); +char partab[]; + +struct device { + int rcsr; + int rbuf; + int tcsr; + int tbuf; +}; + +klopen(dev, flag) +dev_t dev; +{ + register struct device *addr; + register struct tty *tp; + register d; + + d = minor(dev); + if(d >= NKL11+NDL11) { + u.u_error = ENXIO; + return; + } + tp = &kl11[d]; + /* + * set up minor 0 to address KLADDR + * set up minor 1 thru NKL11-1 to address from KLBASE + * set up minor NKL11 on to address from DLBASE + */ + if(d == 0) + addr = KLADDR; + else if(d < NKL11) + addr = KLBASE + (d-1); + else + addr = DLBASE + (d-NKL11); + tp->t_addr = (caddr_t)addr; + tp->t_oproc = klstart; + if ((tp->t_state&ISOPEN) == 0) { + tp->t_state = ISOPEN|CARR_ON; + tp->t_flags = EVENP|LCASE|ECHO|XTABS|CRMOD|CR2; + ttychars(tp); + } + addr->rcsr |= IENABLE|DSRDY|RDRENB; + addr->tcsr |= IENABLE; + ttyopen(dev, tp); +} + +klclose(dev, flag) +dev_t dev; +int flag; +{ + register struct tty *tp; + + tp = &kl11[minor(dev)]; + ttyclose(tp); +} + +klread(dev) +dev_t dev; +{ + ttread(&kl11[minor(dev)]); +} + +klwrite(dev) +dev_t dev; +{ + ttwrite(&kl11[minor(dev)]); +} + +klxint(dev) +dev_t dev; +{ + register struct tty *tp; + + tp = &kl11[minor(dev)]; + ttstart(tp); + if (tp->t_state&ASLEEP && tp->t_outq.c_cc<=TTLOWAT) + if (tp->t_chan) + mcstart(tp->t_chan, (caddr_t)&tp->t_outq); + else + wakeup((caddr_t)&tp->t_outq); +} + +klrint(dev) +dev_t dev; +{ + register int c; + register struct device *addr; + register struct tty *tp; + + tp = &kl11[minor(dev)]; + addr = (struct device *)tp->t_addr; + c = addr->rbuf; + addr->rcsr |= RDRENB; + ttyinput(c, tp); +} + +klioctl(dev, cmd, addr, flag) +caddr_t addr; +dev_t dev; +{ + if (ttioccom(cmd, &kl11[minor(dev)], addr, dev)==0) + u.u_error = ENOTTY; +} + +klstart(tp) +register struct tty *tp; +{ + register c; + register struct device *addr; + + addr = (struct device *)tp->t_addr; + if((addr->tcsr&DONE) == 0) + return; + if ((c=getc(&tp->t_outq)) >= 0) { + if (tp->t_flags&RAW) + addr->tbuf = c; + else if (c<=0177) + addr->tbuf = c | (partab[c]&0200); + else { + timeout(ttrstrt, (caddr_t)tp, (c&0177) + DLDELAY); + tp->t_state |= TIMEOUT; + } + } +} + +char *msgbufp = msgbuf; /* Next saved printf character */ +/* + * Print a character on console. + * Attempts to save and restore device + * status. + * If the switches are 0, all + * printing is inhibited. + * + * Whether or not printing is inhibited, + * the last MSGBUFS characters + * are saved in msgbuf for inspection later. + */ +putchar(c) +register c; +{ + register s, timo; + + if (c != '\0' && c != '\r' && c != 0177) { + *msgbufp++ = c; + if(msgbufp >= &msgbuf[MSGBUFS]) + msgbufp = msgbuf; + } + /* + * If last char was a break or null, don't print + */ + if ((KLADDR->rbuf&0177) == 0) + return; + timo = 30000; + /* + * Try waiting for the console tty to come ready, + * otherwise give up after a reasonable time. + */ + while((KLADDR->tcsr&0200) == 0) + if(--timo == 0) + break; + if(c == 0) + return; + s = KLADDR->tcsr; + KLADDR->tcsr = 0; + KLADDR->tbuf = c; + if(c == '\n') { + putchar('\r'); + putchar(0177); + putchar(0177); + } + putchar(0); + KLADDR->tcsr = s; +} diff --git a/tty/sys/orig/pk.p b/tty/sys/orig/pk.p new file mode 100644 index 0000000..5c21eed --- /dev/null +++ b/tty/sys/orig/pk.p @@ -0,0 +1,78 @@ +/* + * kernel level + */ +#ifdef KERNEL + +#define PADDR ((struct pack *)tp->t_linep) +#define TURNOFF pkturnoff(tp) +#define UCOUNT u.u_count +#define S tp +#define P pk->p_ttyp +#define SDEF struct tty *tp +#define FS , tp + +#define SIGNAL signal(pk->p_ttyp->t_pgrp, SIGPIPE) +#define TERROR pk->p_istate == R_ERROR +#define SETERROR u.u_error = EIO +#define OBUSY tp->t_state&BUSY +#define ODEAD ((tp->t_state&CARR_ON)==0) +char *getepack(); +#define GETEPACK getepack(pk->p_bits) +#define FREEPACK(a,b) freepack(a, b) + + +#define q1 tp->t_rawq +#define q2 tp->t_canq +#define q3 tp->t_outq + +#define LOCK s = spl6() +#define UNLOCK splx(s) +#define DSYSTEM struct tty *p_ttyp +#define ISYSTEM tp = pk->p_ttyp +#define SLEEP(a, b) sleep((caddr_t)a, b) +#define SLEEPNO (tp->t_chan!=NULL) +#define WAKEUP(a) wakeup((caddr_t)a) +#define IOMOVE(p, c, f) iomove(p, c, f) +#define PKGETPKT(p) +#define DTOM(a) dtom(a) +#include "../h/param.h" +#include "../h/dir.h" +#include "../h/user.h" +#include "../h/pk.h" +#include "../h/tty.h" +#include "../h/buf.h" +#include "../h/proc.h" + +#endif +/* + * user level + */ +#ifdef USER +#define SLEEP(a, b) +#define SIGNAL +#define WAKEUP(a) +#define DSYSTEM int p_ifn, p_ofn +#define ISYSTEM +#define GETEPACK malloc(pk->p_xsize) +#define FREEPACK(a, b) free(a) +#define OBUSY 0 +#define PKGETPKT(p) pkgetpack(p); +#define DTOM(a) 1; +#define S ipk, ibuf, icount +#define SDEF int icount; char *ibuf; struct pack *ipk +#define UCOUNT icount +#define IOMOVE(p, c, f) pkmove(p, ibuf, c, f) ; ibuf += c; UCOUNT -= c +#define PADDR ipk +#define TURNOFF +#define LOCK +#define UNLOCK +#define SETERROR +#define GENERROR(p, s) +#define PACKSIZE 64 +#define WINDOWS 3 +#define PKDEBUG(l, f, s) { extern Debug; if (Debug >= l) fprintf(stderr, f, s);} +#define PKASSERT(e, f, v) if (!(e)) {\ +fprintf(stderr, "AERROR - (%s) ", "e");\ +fprintf(stderr, f, v);\ +pkfail();}; +#endif diff --git a/tty/sys/orig/sgtty.h b/tty/sys/orig/sgtty.h new file mode 100644 index 0000000..be6b89f --- /dev/null +++ b/tty/sys/orig/sgtty.h @@ -0,0 +1,116 @@ +/* + * Structure for stty and gtty system calls. + */ + +struct sgttyb { + char sg_ispeed; /* input speed */ + char sg_ospeed; /* output speed */ + char sg_erase; /* erase character */ + char sg_kill; /* kill character */ + int sg_flags; /* mode flags */ +}; + +/* + * List of special characters + */ +struct tchars { + char t_intrc; /* interrupt */ + char t_quitc; /* quit */ + char t_startc; /* start output */ + char t_stopc; /* stop output */ + char t_eofc; /* end-of-file */ + char t_brkc; /* input delimiter (like nl) */ +}; + +/* + * Modes + */ +#define TANDEM 01 +#define CBREAK 02 +#define LCASE 04 +#define ECHO 010 +#define CRMOD 020 +#define RAW 040 +#define ODDP 0100 +#define EVENP 0200 +#define ANYP 0300 +#define NLDELAY 001400 +#define TBDELAY 006000 +#define XTABS 06000 +#define CRDELAY 030000 +#define VTDELAY 040000 +#define BSDELAY 0100000 +#define ALLDELAY 0177400 + +/* + * Delay algorithms + */ +#define CR0 0 +#define CR1 010000 +#define CR2 020000 +#define CR3 030000 +#define NL0 0 +#define NL1 000400 +#define NL2 001000 +#define NL3 001400 +#define TAB0 0 +#define TAB1 002000 +#define TAB2 004000 +#define FF0 0 +#define FF1 040000 +#define BS0 0 +#define BS1 0100000 + +/* + * Speeds + */ +#define B0 0 +#define B50 1 +#define B75 2 +#define B110 3 +#define B134 4 +#define B150 5 +#define B200 6 +#define B300 7 +#define B600 8 +#define B1200 9 +#define B1800 10 +#define B2400 11 +#define B4800 12 +#define B9600 13 +#define EXTA 14 +#define EXTB 15 + +/* + * tty ioctl commands + */ +#define TIOCGETD (('t'<<8)|0) +#define TIOCSETD (('t'<<8)|1) +#define TIOCHPCL (('t'<<8)|2) +#define TIOCMODG (('t'<<8)|3) +#define TIOCMODS (('t'<<8)|4) +#define TIOCGETP (('t'<<8)|8) +#define TIOCSETP (('t'<<8)|9) +#define TIOCSETN (('t'<<8)|10) +#define TIOCEXCL (('t'<<8)|13) +#define TIOCNXCL (('t'<<8)|14) +#define TIOHMODE (('t'<<8)|15) +#define TIOCTSTP (('t'<<8)|16) +#define TIOCSETC (('t'<<8)|17) +#define TIOCGETC (('t'<<8)|18) +#define DIOCLSTN (('d'<<8)|1) +#define DIOCNTRL (('d'<<8)|2) +#define DIOCMPX (('d'<<8)|3) +#define DIOCNMPX (('d'<<8)|4) +#define DIOCSCALL (('d'<<8)|5) +#define DIOCRCALL (('d'<<8)|6) +#define DIOCPGRP (('d'<<8)|7) +#define DIOCGETP (('d'<<8)|8) +#define DIOCSETP (('d'<<8)|9) +#define DIOCLOSE (('d'<<8)|10) +#define DIOCTIME (('d'<<8)|11) +#define DIOCRESET (('d'<<8)|12) +#define FIOCLEX (('f'<<8)|1) +#define FIONCLEX (('f'<<8)|2) +#define MXLSTN (('x'<<8)|1) +#define MXNBLK (('x'<<8)|2) diff --git a/tty/sys/orig/tty.c b/tty/sys/orig/tty.c new file mode 100644 index 0000000..e48f3f0 --- /dev/null +++ b/tty/sys/orig/tty.c @@ -0,0 +1,728 @@ +# +/* + * general TTY subroutines + */ +#include "../h/param.h" +#include "../h/systm.h" +#include "../h/dir.h" +#include "../h/user.h" +#include "../h/tty.h" +#include "../h/proc.h" +#include "../h/mx.h" +#include "../h/inode.h" +#include "../h/file.h" +#include "../h/reg.h" +#include "../h/conf.h" + +char partab[]; + + +/* + * Input mapping table-- if an entry is non-zero, when the + * corresponding character is typed preceded by "\" the escape + * sequence is replaced by the table value. Mostly used for + * upper-case only terminals. + */ + +char maptab[] ={ + 000,000,000,000,000,000,000,000, + 000,000,000,000,000,000,000,000, + 000,000,000,000,000,000,000,000, + 000,000,000,000,000,000,000,000, + 000,'|',000,000,000,000,000,'`', + '{','}',000,000,000,000,000,000, + 000,000,000,000,000,000,000,000, + 000,000,000,000,000,000,000,000, + 000,000,000,000,000,000,000,000, + 000,000,000,000,000,000,000,000, + 000,000,000,000,000,000,000,000, + 000,000,000,000,000,000,'~',000, + 000,'A','B','C','D','E','F','G', + 'H','I','J','K','L','M','N','O', + 'P','Q','R','S','T','U','V','W', + 'X','Y','Z',000,000,000,000,000, +}; + + +/* + * shorthand + */ +#define q1 tp->t_rawq +#define q2 tp->t_canq +#define q3 tp->t_outq +#define q4 tp->t_un.t_ctlq + + +/* + * routine called on first teletype open. + * establishes a process group for distribution + * of quits and interrupts from the tty. + */ +ttyopen(dev, tp) +dev_t dev; +register struct tty *tp; +{ + register struct proc *pp; + + pp = u.u_procp; + tp->t_dev = dev; + if(pp->p_pgrp == 0) { + u.u_ttyp = tp; + u.u_ttyd = dev; + if (tp->t_pgrp==0) + tp->t_pgrp = pp->p_pid; + pp->p_pgrp = tp->t_pgrp; + } + tp->t_state &= ~WOPEN; + tp->t_state |= ISOPEN; +} + + +/* + * set default control characters. + */ +ttychars(tp) +register struct tty *tp; +{ + tun.t_intrc = CINTR; + tun.t_quitc = CQUIT; + tun.t_startc = CSTART; + tun.t_stopc = CSTOP; + tun.t_eofc = CEOT; + tun.t_brkc = CBRK; + tp->t_erase = CERASE; + tp->t_kill = CKILL; +} + +/* + * clean tp on last close + */ +ttyclose(tp) +register struct tty *tp; +{ + + tp->t_pgrp = 0; + wflushtty(tp); + tp->t_state = 0; +} + +/* + * stty/gtty writearound + */ +stty() +{ + u.u_arg[2] = u.u_arg[1]; + u.u_arg[1] = TIOCSETP; + ioctl(); +} + +gtty() +{ + u.u_arg[2] = u.u_arg[1]; + u.u_arg[1] = TIOCGETP; + ioctl(); +} + +/* + * ioctl system call + * Check legality, execute common code, and switch out to individual + * device routine. + */ +ioctl() +{ + register struct file *fp; + register struct inode *ip; + register struct a { + int fdes; + int cmd; + caddr_t cmarg; + } *uap; + register dev_t dev; + register fmt; + + uap = (struct a *)u.u_ap; + if ((fp = getf(uap->fdes)) == NULL) + return; + if (uap->cmd==FIOCLEX) { + u.u_pofile[uap->fdes] |= EXCLOSE; + return; + } + if (uap->cmd==FIONCLEX) { + u.u_pofile[uap->fdes] &= ~EXCLOSE; + return; + } + ip = fp->f_inode; + fmt = ip->i_mode & IFMT; + if (fmt != IFCHR && fmt != IFMPC) { + u.u_error = ENOTTY; + return; + } + dev = (dev_t)ip->i_un.i_rdev; + (*cdevsw[major(dev)].d_ioctl)(dev, uap->cmd, uap->cmarg, fp->f_flag); +} + +/* + * Common code for several tty ioctl commands + */ +ttioccomm(com, tp, addr, dev) +register struct tty *tp; +caddr_t addr; +{ + unsigned t; + struct ttiocb iocb; + extern int nldisp; + + switch(com) { + + /* + * get discipline number + */ + case TIOCGETD: + t = tp->t_line; + if (copyout((caddr_t)&t, addr, sizeof(t))) + u.u_error = EFAULT; + break; + + /* + * set line discipline + */ + case TIOCSETD: + if (copyin(addr, (caddr_t)&t, sizeof(t))) { + u.u_error = EFAULT; + break; + } + if (t >= nldisp) { + u.u_error = ENXIO; + break; + } + if (tp->t_line) + (*linesw[tp->t_line].l_close)(tp); + if (t) + (*linesw[t].l_open)(dev, tp, addr); + if (u.u_error==0) + tp->t_line = t; + break; + + /* + * prevent more opens on channel + */ + case TIOCEXCL: + tp->t_state |= XCLUDE; + break; + case TIOCNXCL: + tp->t_state &= ~XCLUDE; + break; + + /* + * Set new parameters + */ + case TIOCSETP: + wflushtty(tp); + case TIOCSETN: + if (copyin(addr, (caddr_t)&iocb, sizeof(iocb))) { + u.u_error = EFAULT; + return(1); + } + tp->t_ispeed = iocb.ioc_ispeed; + tp->t_ospeed = iocb.ioc_ospeed; + tp->t_erase = iocb.ioc_erase; + tp->t_kill = iocb.ioc_kill; + tp->t_flags = iocb.ioc_flags; + break; + + /* + * send current parameters to user + */ + case TIOCGETP: + iocb.ioc_ispeed = tp->t_ispeed; + iocb.ioc_ospeed = tp->t_ospeed; + iocb.ioc_erase = tp->t_erase; + iocb.ioc_kill = tp->t_kill; + iocb.ioc_flags = tp->t_flags; + if (copyout((caddr_t)&iocb, addr, sizeof(iocb))) + u.u_error = EFAULT; + break; + + /* + * Hang up line on last close + */ + + case TIOCHPCL: + tp->t_state |= HUPCLS; + break; + + case TIOCFLUSH: + flushtty(tp); + break; + + /* + * ioctl entries to line discipline + */ + case DIOCSETP: + case DIOCGETP: + (*linesw[tp->t_line].l_ioctl)(com, tp, addr); + break; + + /* + * set and fetch special characters + */ + case TIOCSETC: + if (copyin(addr, (caddr_t)&tun, sizeof(struct tc))) + u.u_error = EFAULT; + break; + + case TIOCGETC: + if (copyout((caddr_t)&tun, addr, sizeof(struct tc))) + u.u_error = EFAULT; + break; + + default: + return(0); + } + return(1); +} + +/* + * Wait for output to drain, then flush input waiting. + */ +wflushtty(tp) +register struct tty *tp; +{ + + spl5(); + while (tp->t_outq.c_cc && tp->t_state&CARR_ON) { + (*tp->t_oproc)(tp); + tp->t_state |= ASLEEP; + sleep((caddr_t)&tp->t_outq, TTOPRI); + } + flushtty(tp); + spl0(); +} + +/* + * flush all TTY queues + */ +flushtty(tp) +register struct tty *tp; +{ + register s; + + while (getc(&tp->t_canq) >= 0) + ; + wakeup((caddr_t)&tp->t_rawq); + wakeup((caddr_t)&tp->t_outq); + s = spl6(); + tp->t_state &= ~TTSTOP; + (*cdevsw[major(tp->t_dev)].d_stop)(tp); + while (getc(&tp->t_outq) >= 0) + ; + while (getc(&tp->t_rawq) >= 0) + ; + tp->t_delct = 0; + splx(s); +} + + + +/* + * transfer raw input list to canonical list, + * doing erase-kill processing and handling escapes. + * It waits until a full line has been typed in cooked mode, + * or until any character has been typed in raw mode. + */ +canon(tp) +register struct tty *tp; +{ + register char *bp; + char *bp1; + register int c; + int mc; + + spl5(); + while ((tp->t_flags&(RAW|CBREAK))==0 && tp->t_delct==0 + || (tp->t_flags&(RAW|CBREAK))!=0 && tp->t_rawq.c_cc==0) { + if ((tp->t_state&CARR_ON)==0 || tp->t_chan!=NULL) { + return(0); + } + sleep((caddr_t)&tp->t_rawq, TTIPRI); + } + spl0(); +loop: + bp = &canonb[2]; + while ((c=getc(&tp->t_rawq)) >= 0) { + if ((tp->t_flags&(RAW|CBREAK))==0) { + if (c==0377) { + tp->t_delct--; + break; + } + if (bp[-1]!='\\') { + if (c==tp->t_erase) { + if (bp > &canonb[2]) + bp--; + continue; + } + if (c==tp->t_kill) + goto loop; + if (c==tun.t_eofc) + continue; + } else { + mc = maptab[c]; + if (c==tp->t_erase || c==tp->t_kill) + mc = c; + if (mc && (mc==c || (tp->t_flags&LCASE))) { + if (bp[-2] != '\\') + c = mc; + bp--; + } + } + } + *bp++ = c; + if (bp>=canonb+CANBSIZ) + break; + } + bp1 = &canonb[2]; + b_to_q(bp1, bp-bp1, &tp->t_canq); + + if (tp->t_state&TBLOCK && tp->t_rawq.c_cc < TTYHOG/5) { + if (putc(tun.t_startc, &tp->t_outq)==0) { + tp->t_state &= ~TBLOCK; + ttstart(tp); + } + tp->t_char = 0; + } + + return(bp-bp1); +} + + +/* + * block transfer input handler. + */ +ttyrend(tp, pb, pe) +register struct tty *tp; +register char *pb, *pe; +{ + int tandem; + + tandem = tp->t_flags&TANDEM; + if (tp->t_flags&RAW) { + b_to_q(pb, pe-pb, &tp->t_rawq); + if (tp->t_chan) + sdata(tp->t_chan); else + wakeup((caddr_t)&tp->t_rawq); + } else { + tp->t_flags &= ~TANDEM; + while (pb < pe) + ttyinput(*pb++, tp); + tp->t_flags |= tandem; + } + if (tandem) + ttyblock(tp); +} + +/* + * Place a character on raw TTY input queue, putting in delimiters + * and waking up top half as needed. + * Also echo if required. + * The arguments are the character and the appropriate + * tty structure. + */ +ttyinput(c, tp) +register c; +register struct tty *tp; +{ + register int t_flags; + register struct chan *cp; + + tk_nin += 1; + c &= 0377; + t_flags = tp->t_flags; + if (t_flags&TANDEM) + ttyblock(tp); + if ((t_flags&RAW)==0) { + c &= 0177; + if (tp->t_state&TTSTOP) { + if (c==tun.t_startc) { + tp->t_state &= ~TTSTOP; + ttstart(tp); + return; + } + if (c==tun.t_stopc) + return; + tp->t_state &= ~TTSTOP; + ttstart(tp); + } else { + if (c==tun.t_stopc) { + tp->t_state |= TTSTOP; + (*cdevsw[major(tp->t_dev)].d_stop)(tp); + return; + } + if (c==tun.t_startc) + return; + } + if (c==tun.t_quitc || c==tun.t_intrc) { + flushtty(tp); + c = (c==tun.t_intrc) ? SIGINT:SIGQUIT; + if (tp->t_chan) + scontrol(tp->t_chan, M_SIG, c); + else + signal(tp->t_pgrp, c); + return; + } + if (c=='\r' && t_flags&CRMOD) + c = '\n'; + } + if (tp->t_rawq.c_cc>TTYHOG) { + flushtty(tp); + return; + } + if (t_flags&LCASE && c>='A' && c<='Z') + c += 'a'-'A'; + putc(c, &tp->t_rawq); + if (t_flags&(RAW|CBREAK)||(c=='\n'||c==tun.t_eofc||c==tun.t_brkc)) { + if ((t_flags&(RAW|CBREAK))==0 && putc(0377, &tp->t_rawq)==0) + tp->t_delct++; + if ((cp=tp->t_chan)!=NULL) + sdata(cp); else + wakeup((caddr_t)&tp->t_rawq); + } + if (t_flags&ECHO) { + ttyoutput(c, tp); + if (c==tp->t_kill && (t_flags&(RAW|CBREAK))==0) + ttyoutput('\n', tp); + ttstart(tp); + } +} + + +/* + * Send stop character on input overflow. + */ +ttyblock(tp) +register struct tty *tp; +{ +register x; + x = q1.c_cc + q2.c_cc; + if (q1.c_cc > TTYHOG) { + flushtty(tp); + tp->t_state &= ~TBLOCK; + } + if (x >= TTYHOG/2) { + if (putc(tun.t_stopc, &tp->t_outq)==0) { + tp->t_state |= TBLOCK; + tp->t_char++; + ttstart(tp); + } + } +} + +/* + * put character on TTY output queue, adding delays, + * expanding tabs, and handling the CR/NL bit. + * It is called both from the top half for output, and from + * interrupt level for echoing. + * The arguments are the character and the tty structure. + */ +ttyoutput(c, tp) +register c; +register struct tty *tp; +{ + register char *colp; + register ctype; + + tk_nout += 1; + /* + * Ignore EOT in normal mode to avoid hanging up + * certain terminals. + * In raw mode dump the char unchanged. + */ + + if ((tp->t_flags&RAW)==0) { + c &= 0177; + if (c==CEOT) + return; + } else { + putc(c, &tp->t_outq); + return; + } + + /* + * Turn tabs to spaces as required + */ + if (c=='\t' && (tp->t_flags&TBDELAY)==XTABS) { + c = 8; + do + ttyoutput(' ', tp); + while (--c >= 0 && tp->t_col&07); + return; + } + /* + * for upper-case-only terminals, + * generate escapes. + */ + if (tp->t_flags&LCASE) { + colp = "({)}!|^~'`"; + while(*colp++) + if(c == *colp++) { + ttyoutput('\\', tp); + c = colp[-2]; + break; + } + if ('a'<=c && c<='z') + c += 'A' - 'a'; + } + /* + * turn to if desired. + */ + if (c=='\n' && tp->t_flags&CRMOD) + ttyoutput('\r', tp); + putc(c, &tp->t_outq); + /* + * Calculate delays. + * The numbers here represent clock ticks + * and are not necessarily optimal for all terminals. + * The delays are indicated by characters above 0200. + * In raw mode there are no delays and the + * transmission path is 8 bits wide. + */ + colp = &tp->t_col; + ctype = partab[c]; + c = 0; + switch (ctype&077) { + + /* ordinary */ + case 0: + (*colp)++; + + /* non-printing */ + case 1: + break; + + /* backspace */ + case 2: + if (*colp) + (*colp)--; + break; + + /* newline */ + case 3: + ctype = (tp->t_flags >> 8) & 03; + if(ctype == 1) { /* tty 37 */ + if (*colp) + c = max(((unsigned)*colp>>4) + 3, (unsigned)6); + } else + if(ctype == 2) { /* vt05 */ + c = 6; + } + *colp = 0; + break; + + /* tab */ + case 4: + ctype = (tp->t_flags >> 10) & 03; + if(ctype == 1) { /* tty 37 */ + c = 1 - (*colp | ~07); + if(c < 5) + c = 0; + } + *colp |= 07; + (*colp)++; + break; + + /* vertical motion */ + case 5: + if(tp->t_flags & VTDELAY) /* tty 37 */ + c = 0177; + break; + + /* carriage return */ + case 6: + ctype = (tp->t_flags >> 12) & 03; + if(ctype == 1) { /* tn 300 */ + c = 5; + } else if(ctype == 2) { /* ti 700 */ + c = 10; + } + *colp = 0; + } + if(c) + putc(c|0200, &tp->t_outq); +} + +/* + * Restart typewriter output following a delay + * timeout. + * The name of the routine is passed to the timeout + * subroutine and it is called during a clock interrupt. + */ +ttrstrt(tp) +register struct tty *tp; +{ + + tp->t_state &= ~TIMEOUT; + ttstart(tp); +} + +/* + * Start output on the typewriter. It is used from the top half + * after some characters have been put on the output queue, + * from the interrupt routine to transmit the next + * character, and after a timeout has finished. + */ +ttstart(tp) +register struct tty *tp; +{ + register s; + + s = spl5(); + if((tp->t_state&(TIMEOUT|TTSTOP|BUSY)) == 0) + (*tp->t_oproc)(tp); + splx(s); +} + +/* + * Called from device's read routine after it has + * calculated the tty-structure given as argument. + */ +ttread(tp) +register struct tty *tp; +{ + + if ((tp->t_state&CARR_ON)==0) + return(0); + if (tp->t_canq.c_cc || canon(tp)) + while (tp->t_canq.c_cc && passc(getc(&tp->t_canq))>=0) + ; + return(tp->t_rawq.c_cc + tp->t_canq.c_cc); +} + +/* + * Called from the device's write routine after it has + * calculated the tty-structure given as argument. + */ +caddr_t +ttwrite(tp) +register struct tty *tp; +{ + register c; + + if ((tp->t_state&CARR_ON)==0) + return(NULL); + while (u.u_count) { + spl5(); + while (tp->t_outq.c_cc > TTHIWAT) { + ttstart(tp); + tp->t_state |= ASLEEP; + if (tp->t_chan) + return((caddr_t)&tp->t_outq); + sleep((caddr_t)&tp->t_outq, TTOPRI); + } + spl0(); + if ((c = cpass()) < 0) + break; + ttyoutput(c, tp); + } + ttstart(tp); + return(NULL); +} + diff --git a/tty/sys/orig/tty.h b/tty/sys/orig/tty.h new file mode 100644 index 0000000..71fbd8b --- /dev/null +++ b/tty/sys/orig/tty.h @@ -0,0 +1,162 @@ +/* + * A clist structure is the head + * of a linked list queue of characters. + * The characters are stored in 4-word + * blocks containing a link and several characters. + * The routines getc and putc + * manipulate these structures. + */ +struct clist +{ + int c_cc; /* character count */ + char *c_cf; /* pointer to first char */ + char *c_cl; /* pointer to last char */ +}; + +/* + * A tty structure is needed for + * each UNIX character device that + * is used for normal terminal IO. + * The routines in tty.c handle the + * common code associated with + * these structures. + * The definition and device dependent + * code is in each driver. (kl.c dc.c dh.c) + */ + +struct tc { + char t_intrc; /* interrupt */ + char t_quitc; /* quit */ + char t_startc; /* start output */ + char t_stopc; /* stop output */ + char t_eofc; /* end-of-file */ + char t_brkc; /* input delimiter (like nl) */ +}; + +struct tty +{ + struct clist t_rawq; /* input chars right off device */ + struct clist t_canq; /* input chars after erase and kill */ + struct clist t_outq; /* output list to device */ + int (* t_oproc)(); /* routine to start output */ + int (* t_iproc)(); /* routine to start input */ + struct chan *t_chan; /* destination channel */ + caddr_t t_linep; /* aux line discipline pointer */ + caddr_t t_addr; /* device address */ + dev_t t_dev; /* device number */ + short t_flags; /* mode, settable by ioctl call */ + short t_state; /* internal state, not visible externally */ + short t_pgrp; /* process group name */ + char t_delct; /* number of delimiters in raw q */ + char t_line; /* line discipline */ + char t_col; /* printing column of device */ + char t_erase; /* erase character */ + char t_kill; /* kill character */ + char t_char; /* character temporary */ + char t_ispeed; /* input speed */ + char t_ospeed; /* output speed */ + union { + struct tc; + struct clist t_ctlq; + } t_un; +}; + +#define tun tp->t_un + +/* + * structure of arg for ioctl + */ +struct ttiocb { + char ioc_ispeed; + char ioc_ospeed; + char ioc_erase; + char ioc_kill; + int ioc_flags; +}; + +#define TTIPRI 28 +#define TTOPRI 29 + +#define CERASE '#' /* default special characters */ +#define CEOT 004 +#define CKILL '@' +#define CQUIT 034 /* FS, cntl shift L */ +#define CINTR 0177 /* DEL */ +#define CSTOP 023 /* Stop output: ctl-s */ +#define CSTART 021 /* Start output: ctl-q */ +#define CBRK 0377 + +/* limits */ +#define TTHIWAT 100 +#define TTLOWAT 50 +#define TTYHOG 256 + +/* modes */ +#define TANDEM 01 +#define CBREAK 02 +#define LCASE 04 +#define ECHO 010 +#define CRMOD 020 +#define RAW 040 +#define ODDP 0100 +#define EVENP 0200 +#define NLDELAY 001400 +#define TBDELAY 006000 +#define XTABS 006000 +#define CRDELAY 030000 +#define VTDELAY 040000 + +/* Hardware bits */ +#define DONE 0200 +#define IENABLE 0100 + +/* Internal state bits */ +#define TIMEOUT 01 /* Delay timeout in progress */ +#define WOPEN 02 /* Waiting for open to complete */ +#define ISOPEN 04 /* Device is open */ +#define FLUSH 010 /* outq has been flushed during DMA */ +#define CARR_ON 020 /* Software copy of carrier-present */ +#define BUSY 040 /* Output in progress */ +#define ASLEEP 0100 /* Wakeup when output done */ +#define XCLUDE 0200 /* exclusive-use flag against open */ +#define TTSTOP 0400 /* Output stopped by ctl-s */ +#define HUPCLS 01000 /* Hang up upon last close */ +#define TBLOCK 02000 /* tandem queue blocked */ +#define DKCMD 04000 /* datakit command channel */ +#define DKMPX 010000 /* datakit user-multiplexed mode */ +#define DKCALL 020000 /* datakit dial mode */ +#define DKLINGR 040000 /* datakit lingering close mode */ +#define CNTLQ 0100000 /* interpret t_un as clist */ + +/* + * tty ioctl commands + */ +#define TIOCGETD (('t'<<8)|0) +#define TIOCSETD (('t'<<8)|1) +#define TIOCHPCL (('t'<<8)|2) +#define TIOCMODG (('t'<<8)|3) +#define TIOCMODS (('t'<<8)|4) +#define TIOCGETP (('t'<<8)|8) +#define TIOCSETP (('t'<<8)|9) +#define TIOCSETN (('t'<<8)|10) +#define TIOCEXCL (('t'<<8)|13) +#define TIOCNXCL (('t'<<8)|14) +#define TIOCFLUSH (('t'<<8)|16) +#define TIOCSETC (('t'<<8)|17) +#define TIOCGETC (('t'<<8)|18) +#define DIOCLSTN (('d'<<8)|1) +#define DIOCNTRL (('d'<<8)|2) +#define DIOCMPX (('d'<<8)|3) +#define DIOCNMPX (('d'<<8)|4) +#define DIOCSCALL (('d'<<8)|5) +#define DIOCRCALL (('d'<<8)|6) +#define DIOCPGRP (('d'<<8)|7) +#define DIOCGETP (('d'<<8)|8) +#define DIOCSETP (('d'<<8)|9) +#define DIOCLOSE (('d'<<8)|10) +#define DIOCTIME (('d'<<8)|11) +#define DIOCRESET (('d'<<8)|12) +#define FIOCLEX (('f'<<8)|1) +#define FIONCLEX (('f'<<8)|2) +#define MXLSTN (('x'<<8)|1) +#define MXNBLK (('x'<<8)|2) diff --git a/tty/sys/pk.p b/tty/sys/pk.p new file mode 100644 index 0000000..3b9b9a9 --- /dev/null +++ b/tty/sys/pk.p @@ -0,0 +1,78 @@ +/* + * kernel level + */ +#ifdef KERNEL + +#define PADDR ((struct pack *)tp->t_linep) +#define TURNOFF pkturnoff(tp) +#define UCOUNT u.u_count +#define S tp +#define P pk->p_ttyp +#define SDEF struct tty *tp +#define FS , tp + +#define SIGNAL signal(pk->p_ttyp->t_pgrp, SIGPIPE) +#define TERROR pk->p_istate == R_ERROR +#define SETERROR u.u_error = EIO +#define OBUSY tp->t_state&BUSY +#define ODEAD ((tp->t_state&CARR_ON)==0) +char *getepack(); +#define GETEPACK getepack(pk->p_bits) +#define FREEPACK(a,b) freepack(a, b) + + +#define q1 tp->t_rawq +#define q2 tp->t_rawq /* was canq, and is cleared before q1 is set up */ +#define q3 tp->t_outq + +#define LOCK s = spl6() +#define UNLOCK splx(s) +#define DSYSTEM struct tty *p_ttyp +#define ISYSTEM tp = pk->p_ttyp +#define SLEEP(a, b) sleep((caddr_t)a, b) +#define SLEEPNO (tp->t_chan!=NULL) +#define WAKEUP(a) wakeup((caddr_t)a) +#define IOMOVE(p, c, f) iomove(p, c, f) +#define PKGETPKT(p) +#define DTOM(a) dtom(a) +#include "../h/param.h" +#include "../h/dir.h" +#include "../h/user.h" +#include "../h/pk.h" +#include "../h/tty.h" +#include "../h/buf.h" +#include "../h/proc.h" + +#endif +/* + * user level + */ +#ifdef USER +#define SLEEP(a, b) +#define SIGNAL +#define WAKEUP(a) +#define DSYSTEM int p_ifn, p_ofn +#define ISYSTEM +#define GETEPACK malloc(pk->p_xsize) +#define FREEPACK(a, b) free(a) +#define OBUSY 0 +#define PKGETPKT(p) pkgetpack(p); +#define DTOM(a) 1; +#define S ipk, ibuf, icount +#define SDEF int icount; char *ibuf; struct pack *ipk +#define UCOUNT icount +#define IOMOVE(p, c, f) pkmove(p, ibuf, c, f) ; ibuf += c; UCOUNT -= c +#define PADDR ipk +#define TURNOFF +#define LOCK +#define UNLOCK +#define SETERROR +#define GENERROR(p, s) +#define PACKSIZE 64 +#define WINDOWS 3 +#define PKDEBUG(l, f, s) { extern Debug; if (Debug >= l) fprintf(stderr, f, s);} +#define PKASSERT(e, f, v) if (!(e)) {\ +fprintf(stderr, "AERROR - (%s) ", "e");\ +fprintf(stderr, f, v);\ +pkfail();}; +#endif diff --git a/tty/sys/sgtty.h b/tty/sys/sgtty.h new file mode 100644 index 0000000..c31f16c --- /dev/null +++ b/tty/sys/sgtty.h @@ -0,0 +1,101 @@ +/* + * Structure for stty and gtty system calls. + */ + +struct sgttyb { + char sg_ispeed; /* input speed */ + char sg_ospeed; /* output speed */ + char sg_erase; /* erase character */ + char sg_kill; /* kill character */ + short sg_flags; /* mode flags */ + char sg_nldly; /* delay for nl */ + char sg_crdly; /* delay for cr */ + char sg_htdly; /* delay for ht */ + char sg_vtdly; /* delay for vt */ + char sg_width; /* screen width */ + char sg_length; /* screen length */ +}; + +/* + * List of special characters + */ +struct tchars { + char t_intrc; /* interrupt */ + char t_quitc; /* quit */ + char t_startc; /* start output */ + char t_stopc; /* stop output */ + char t_eofc; /* end-of-file */ + char t_brkc; /* input delimiter (like nl) */ +}; + +/* + * Modes + */ +#define TANDEM 01 +#define CBREAK 02 +#define LCASE 04 +#define ECHO 010 +#define CRMOD 020 +#define RAW 040 +#define ODDP 0100 +#define EVENP 0200 +#define ANYP 0300 +#define SCOPE 0400 +#define INDCTL 01000 +#define XTABS 02000 + +/* + * Speeds + */ +#define B0 0 +#define B50 1 +#define B75 2 +#define B110 3 +#define B134 4 +#define B150 5 +#define B200 6 +#define B300 7 +#define B600 8 +#define B1200 9 +#define B1800 10 +#define B2400 11 +#define B4800 12 +#define B9600 13 +#define EXTA 14 +#define EXTB 15 + +/* + * tty ioctl commands + */ +#define TIOCGETD (('t'<<8)|0) +#define TIOCSETD (('t'<<8)|1) +#define TIOCHPCL (('t'<<8)|2) +#define TIOCMODG (('t'<<8)|3) +#define TIOCMODS (('t'<<8)|4) +#define TIOCGETP (('t'<<8)|8) +#define TIOCSETP (('t'<<8)|9) +#define TIOCSETN (('t'<<8)|10) +#define TIOCSETA (('t'<<8)|11) +#define TIOCGETA (('t'<<8)|12) +#define TIOCEXCL (('t'<<8)|13) +#define TIOCNXCL (('t'<<8)|14) +#define TIOCNRD (('t'<<8)|15) +#define TIOCFLUSH (('t'<<8)|16) +#define TIOCSETC (('t'<<8)|17) +#define TIOCGETC (('t'<<8)|18) +#define DIOCLSTN (('d'<<8)|1) +#define DIOCNTRL (('d'<<8)|2) +#define DIOCMPX (('d'<<8)|3) +#define DIOCNMPX (('d'<<8)|4) +#define DIOCSCALL (('d'<<8)|5) +#define DIOCRCALL (('d'<<8)|6) +#define DIOCPGRP (('d'<<8)|7) +#define DIOCGETP (('d'<<8)|8) +#define DIOCSETP (('d'<<8)|9) +#define DIOCLOSE (('d'<<8)|10) +#define DIOCTIME (('d'<<8)|11) +#define DIOCRESET (('d'<<8)|12) +#define FIOCLEX (('f'<<8)|1) +#define FIONCLEX (('f'<<8)|2) +#define MXLSTN (('x'<<8)|1) +#define MXNBLK (('x'<<8)|2) diff --git a/tty/sys/step1.sh b/tty/sys/step1.sh new file mode 100644 index 0000000..4fd330b --- /dev/null +++ b/tty/sys/step1.sh @@ -0,0 +1,104 @@ +: Script to edit the system files that require removal or replacing +: old files are placed in the appropriate orig directory +: For safety the script should be supplied with the name of the +: system file tree + +: First see if /bin/[ exists +: It did not on my distribution +if ! test -f '/bin/['; then + echo 'The file /bin/[ is needed to support shell scripting' + echo 'and needs creating on this system, please execute:' + echo 'cd /bin' + echo 'ln test [' + exit +fi +if [ "$#" -eq 0 ]; then + echo 'Call this script with the base address of your system source - perhaps /usr/sys' + exit +fi +SYS=$1 +: use this as a flag to suppress update of /usr/include +INCLUDE="" + +echo '*** Step 1: make orig directories and copy backup files' +for name in dev/orig h/orig; do + if [ ! -d $SYS/$name ]; then + echo "Making $SYS/$name" + mkdir $SYS/$name + if [ ! -d $SYS/$name ]; then + echo "Failed to make $SYS/$name - stopping" + exit + fi + fi +done + +if [ "$SYS" = "/usr/sys" ]; then + INCLUDE="/usr/include" + for name in $INCLUDE/orig $INCLUDE/sys/orig; do + if [ ! -d $name ]; then + echo "Making $name" + mkdir $name + if [ ! -d $name ]; then + echo "Failed to make $name - stopping" + exit + fi + fi + done +else + echo "Installing in $SYS and not /usr/sys" + echo "For safety, not updating /usr/include" +fi + +: File backup +: sys/dev +DEST=$SYS/dev/orig +for name in dc.c dh.c dz.c kl.c tty.c; do + if [ ! -f $DEST/$name ]; then + echo "Backup orig/$name to $DEST/$name" + cp orig/$name $DEST/$name + if [ ! -f $DEST/$name ]; then + echo "Failed to copy $name" + exit + fi + fi +done + +: sys/h +DEST=$SYS/h/orig +for name in tty.h pk.p; do + if [ ! -f $DEST/$name ]; then + echo "Backup orig/$name to $DEST/$name" + cp orig/$name $DEST/$name + if [ ! -f $DEST/$name ]; then + echo "Failed to copy $name" + exit + fi + fi +done + +: include +if [ "$INCLUDE" != "" ]; then + DEST=$INCLUDE/orig + for name in sgtty.h; do + if [ ! -f $DEST/$name ]; then + echo "Backup orig/$name to $DEST/$name" + cp orig/$name $DEST/$name + if [ ! -f $DEST/$name ]; then + echo "Failed to copy $name" + exit + fi + fi + done + DEST=$INCLUDE/sys/orig + for name in tty.h pk.p; do + if [ ! -f $DEST/$name ]; then + echo "Backup orig/$name to $DEST/$name" + cp orig/$name $DEST/$name + if [ ! -f $DEST/$name ]; then + echo "Failed to copy $name" + exit + fi + fi + done +fi +exit diff --git a/tty/sys/step2.sh b/tty/sys/step2.sh new file mode 100644 index 0000000..badbaf2 --- /dev/null +++ b/tty/sys/step2.sh @@ -0,0 +1,55 @@ +: Step 2 - install the files +: assuming we have a backup + +if [ "$#" -eq 0 ]; then + echo 'Call this script with the base address of your system source - perhaps /usr/sys' + exit +fi +SYS=$1 +: use this as a flag to suppress update of /usr/include +INCLUDE="" +if [ "$SYS" = "/usr/sys" ]; then + INCLUDE=/usr/include +fi + +echo '*** Step 2: Installing replacement files' +DEST=$SYS/dev +for name in dc.c dh.c dz.c kl.c tty.c; do + if [ -f $DEST/orig/$name ]; then + cp $name $DEST + else + echo "No backup for $DEST/$name - not installed" + fi +done +echo "*** $SYS/dev updated" + +DEST=$SYS/h +cp deftty.h $DEST +for name in tty.h pk.p; do + if [ -f $DEST/orig/$name ]; then + cp $name $DEST + else + echo "No backup for $DEST/$name - not installed" + fi +done + +if [ "$INCLUDE" != "" ]; then + DEST=$INCLUDE + for name in sgtty.h; do + if [ -f $DEST/orig/$name ]; then + cp $name $DEST + else + echo "No backup for $DEST/$name - not installed" + fi + done + + DEST=$INCLUDE/sys + cp deftty.h $DEST + for name in tty.h pk.p; do + if [ -f $DEST/orig/$name ]; then + cp $name $DEST + else + echo "No backup for $DEST/$name - not installed" + fi + done +fi diff --git a/tty/sys/step3.sh b/tty/sys/step3.sh new file mode 100644 index 0000000..13352da --- /dev/null +++ b/tty/sys/step3.sh @@ -0,0 +1,54 @@ +: Step 3 - Validate files +: assuming we have a backup + +if [ "$#" -eq 0 ]; then + echo 'Call this script with the base address of your system source - perhaps /usr/sys' + exit +fi +SYS=$1 +: use this as a flag to suppress update of /usr/include +INCLUDE="" +if [ "$SYS" = "/usr/sys" ]; then + INCLUDE=/usr/include +fi + +echo "*** Step 3: Validating installed files" +echo "If you get any output then installation has failed" + +DEST=$SYS/dev +for name in dc.c dh.c dz.c kl.c tty.c; do + if [ ! -f $DEST/$name ]; then + echo "Cannot find $DEST/$name" + else + cmp $name $DEST/$name + fi +done + +DEST=$SYS/h +for name in deftty.h tty.h pk.p; do + if [ ! -f $DEST/$name ]; then + echo "Cannot find $DEST/$name" + else + cmp $name $DEST/$name + fi +done + +if [ "$INCLUDE" != "" ]; then + DEST=$INCLUDE + for name in sgtty.h; do + if [ ! -f $DEST/$name ]; then + echo "Cannot find $DEST/$name" + else + cmp $name $DEST/$name + fi + done + DEST=$INCLUDE/sys + for name in tty.h deftty.h pk.p; do + if [ ! -f $DEST/$name ]; then + echo "Cannot find $DEST/$name" + else + cmp $name $DEST/$name + fi + done +fi +echo "Done" diff --git a/tty/sys/tty.c b/tty/sys/tty.c new file mode 100644 index 0000000..11563d5 --- /dev/null +++ b/tty/sys/tty.c @@ -0,0 +1,1077 @@ +# +/* + * general TTY subroutines + * + * Defines are + * MX to include calls to the MX code, it's not clear that + * this works. + * DELTAB (defined) delete tabs on input line + * SGTTY (defined) include gtty and stty hooks + * Peter Collinson + * July 2023 + */ +#include "../h/param.h" +#include "../h/systm.h" +#include "../h/dir.h" +#include "../h/user.h" +#include "../h/tty.h" +#include "../h/proc.h" +#ifdef MX +#include "../h/mx.h" +#endif MX +#include "../h/inode.h" +#include "../h/file.h" +#include "../h/reg.h" +#include "../h/conf.h" + + +struct cblock { + struct cblock *c_next; + char c_info[CBSIZE]; +}; + +/* + * Input mapping table-- if an entry is non-zero, when the + * corresponding character is typed preceded by '\', the escape + * sequence is replaced by the table value. Mostly used for + * upper-case only terminals. + * The lower-case representation is also or-ed with the parity bit. + */ +#define P 0200 + +char maptab[] = { + 0, P, P, 0, P, 0, 0, P, + P, 0, 0, P, 0, P, P, 0, + P, 0, 0, P, 0, P, P, 0, + 0, P, P, 0, P, 0, 0, P, + P, '|', 0, P, 0, P, P, '`', + '{', P+'}', P, 0, P, 0, 0, P, + 0, P, P, 0, P, 0, 0, P, + P, 0, 0, P, 0, P, P, 0, + P, 0, 0, P, 0, P, P, 0, + 0, P, P, 0, P, 0, 0, P, + 0, P, P, 0, P, 0, 0, P, + P, 0, 0, P, '\\', P, P+'~', 0, + 0, P+'A', P+'B', 'C', P+'D', 'E', 'F', P+'G', +P+'H', 'I', 'J', P+'K', 'L', P+'M', P+'N', 'O', +P+'P', 'Q', 'R', P+'S', 'T', P+'U', P+'V', 'W', + 'X', P+'Y', P+'Z', 0, P, 0, 0, P +}; + +/* + * cchars is used to determine which characters + * are non-printing (^G is printing). Non-printing + * characters can be echoed as a pair of printing characters. + */ +char cchars[] = { + 0177, 0300, 0377, 0377, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0200 +}; + +/* + * wchars is used to determine whether a character is + * alphanumeric, and therefore part of a word. + */ +char wchars[] = { + 0, 0, 0, 0, 0200, 0, 0377, 03, + 0376, 0377, 0377, 07, 0376, 0377, 0377, 07 +}; + +#define iscntrl(c) (ttybit(cchars, c)) +#define isspcl(c) (ttyspcl(c, tp)) +#define isupper(c) ('A' <= c && c <= 'Z') +#define isword(c) (ttybit(wchars, c)) +#define max(a, b) ((a) > (b) ? (a): (b)) +#define ubyte(c) ((c)&0377) + +#define SGTTY /* include stty and gtty */ +#define DELTAB /* on-screen deletion of tabs if both SCOPE and XTABS */ + +/* + * routine called on first teletype open. + * establishes a process group for distribution + * of quits and interrupts from the tty. + */ +ttyopen(dev, tp) +dev_t dev; +register struct tty *tp; +{ + register struct proc *pp; + + pp = u.u_procp; + tp->t_dev = dev; + if(pp->p_pgrp == 0){ + u.u_ttyp = tp; + u.u_ttyd = dev; + if(tp->t_pgrp == 0) + tp->t_pgrp = pp->p_pid; + pp->p_pgrp = tp->t_pgrp; + } + tp->t_state &= ~WOPEN; + tp->t_state |= ISOPEN; +} + +/* + * set default control characters + * and sizes + */ +ttychars(tp) +register struct tty *tp; +{ + tun.t_intrc = CINTR; + tun.t_quitc = CQUIT; + tun.t_startc = CSTART; + tun.t_stopc = CSTOP; + tun.t_eofc = CEOT; + tun.t_brkc = CBRK; + tp->t_erase = CERASE; + tp->t_kill = CKILL; + tp->t_width = 0; + tp->t_length = 0; +} + +/* + * clean tp on last close + */ +ttyclose(tp) +register struct tty *tp; +{ + tp->t_pgrp = 0; + wflushtty(tp); + tp->t_state = 0; + tp->t_xstate = 0; +} + +#ifdef SGTTY +/* + * stty/gtty writearound + */ +stty() +{ + u.u_arg[2] = u.u_arg[1]; + u.u_arg[1] = TIOCSETP; + ioctl(); +} + +gtty() +{ + u.u_arg[2] = u.u_arg[1]; + u.u_arg[1] = TIOCGETP; + ioctl(); +} +#endif SGTTY + +/* + * ioctl system call + * Check legality, execute common code, and switch out to individual + * device routine. + */ +ioctl() +{ + register struct file *fp; + register struct inode *ip; + register struct a { + int fdes; + int cmd; + caddr_t cmarg; + } *uap; + register dev_t dev; + register fmt; + + uap = (struct a *)u.u_ap; + if((fp = getf(uap->fdes)) == NULL) + return; + if(uap->cmd == FIOCLEX){ + u.u_pofile[uap->fdes] |= EXCLOSE; + return; + } + if(uap->cmd == FIONCLEX){ + u.u_pofile[uap->fdes] &= ~EXCLOSE; + return; + } + ip = fp->f_inode; + fmt = ip->i_mode&IFMT; + if(fmt != IFCHR && fmt != IFMPC){ + u.u_error = ENOTTY; + return; + } + dev = (dev_t)ip->i_un.i_rdev; + (*cdevsw[major(dev)].d_ioctl)(dev, uap->cmd, uap->cmarg, fp->f_flag); +} + +/* + * Common code for several tty ioctl commands + */ +ttioccomm(com, tp, addr, dev) +register struct tty *tp; +caddr_t addr; +{ + unsigned t; + register n; + extern int nldisp; + struct sttiocb { + char ioc_ispeed; + char ioc_ospeed; + char ioc_erase; + char ioc_kill; + short ioc_flags; + }; + + switch(com){ + /* + * get discipline number + */ + case TIOCGETD: + t = tp->t_line; + if(copyout((caddr_t)&t, addr, sizeof(t))) + u.u_error = EFAULT; + break; + /* + * set line discipline + */ + case TIOCSETD: + if(copyin(addr, (caddr_t)&t, sizeof(t))){ + u.u_error = EFAULT; + break; + } + if(t >= nldisp){ + u.u_error = ENXIO; + break; + } + if(tp->t_line) + (*linesw[tp->t_line].l_close)(tp); + if(t) + (*linesw[t].l_open)(dev, tp, addr); + if(u.u_error == 0) + tp->t_line = t; + break; + /* + * prevent more opens on channel + */ + case TIOCEXCL: + tp->t_state |= XCLUDE; + break; + case TIOCNXCL: + tp->t_state &= ~XCLUDE; + break; + /* + * Set new parameters + */ + case TIOCSETP: + wflushtty(tp); + case TIOCSETN: + if(copyin(addr, (caddr_t)&tp->t_ispeed, sizeof(struct sttiocb))){ + u.u_error = EFAULT; + return(1); + } + break; + /* + * send current parameters to user + */ + case TIOCGETP: + if(copyout((caddr_t)&tp->t_ispeed, addr, sizeof(struct sttiocb))) + u.u_error = EFAULT; + break; + /* + * Set all new parameters + */ + case TIOCSETA: + wflushtty(tp); + n = tp->t_length; + if(copyin(addr, (caddr_t)&tp->t_ispeed, sizeof(struct ttiocb))){ + u.u_error = EFAULT; + return(1); + } + if((tp->t_length &= 0177) != n){ + if(tp->t_length) + tp->t_xstate |= XPAGE; + else + tp->t_xstate &= ~XPAGE; + tp->t_lnum = 0; + } + break; + /* + * Send all current parameters to user + */ + case TIOCGETA: + if(copyout((caddr_t)&tp->t_ispeed, addr, sizeof(struct ttiocb))) + u.u_error = EFAULT; + break; + /* + * Hang up line on last close + */ + case TIOCHPCL: + tp->t_state |= HUPCLS; + break; + case TIOCNRD: + t = 0; + if(tp->t_flags&(RAW|CBREAK)) + t = tp->t_rawq.c_cc; + else if(tp->t_delct) { + register char *cp; + + cp = tp->t_rawq.c_cf; + while(ubyte(*cp++) != 0377) + t++; + } + if(copyout((caddr_t)&t, addr, sizeof(t))) + u.u_error = EFAULT; + break; + case TIOCFLUSH: + flushtty(tp); + break; + /* + * ioctl entries to line discipline + */ + case DIOCSETP: + case DIOCGETP: + (*linesw[tp->t_line].l_ioctl)(com, tp, addr); + break; + /* + * set and fetch special characters + */ + case TIOCSETC: + if(copyin(addr, (caddr_t)&tun, sizeof(struct tc))) + u.u_error = EFAULT; + break; + case TIOCGETC: + if(copyout((caddr_t)&tun, addr, sizeof(struct tc))) + u.u_error = EFAULT; + break; + default: + return(0); + } + return(1); +} + +/* + * Wait for output to drain, then flush input waiting. + */ +wflushtty(tp) +register struct tty *tp; +{ + spl5(); + while (tp->t_outq.c_cc && tp->t_state&CARR_ON){ + (*tp->t_oproc)(tp); + tp->t_state |= ASLEEP; + sleep((caddr_t)&tp->t_outq, TTOPRI); + } + flushtty(tp); + spl0(); +} + +/* + * flush all TTY queues + */ +flushtty(tp) +register struct tty *tp; +{ + register s; + + wakeup((caddr_t)&tp->t_rawq); + wakeup((caddr_t)&tp->t_outq); + s = spl6(); + tp->t_state &= ~TTSTOP; + tp->t_xstate &= ~(XPAGE1|XPAGE2|XLITRL|XERASE); + (*cdevsw[major(tp->t_dev)].d_stop)(tp); + while(getc(&tp->t_outq) >= 0); + while(getc(&tp->t_rawq) >= 0); + tp->t_delct = 0; + tp->t_lnum = 0; + splx(s); +} + +/* + * block transfer input handler. +ttyrend(tp, pb, pe) +register struct tty *tp; +register char *pb, *pe; +{ + int tandem; + + tandem = tp->t_flags&TANDEM; + if(tp->t_flags&RAW){ + b_to_q(pb, pe-pb, &tp->t_rawq); +#ifdef MX + if(tp->t_chan) + sdata(tp->t_chan); + else +#endif MX + wakeup((caddr_t)&tp->t_rawq); + } else { + tp->t_flags &= ~TANDEM; + while(pb < pe) + ttyinput(*pb++, tp); + tp->t_flags |= tandem; + } + if(tandem) + ttyblock(tp); +} + */ + +/* + * Place a character on TTY input queue, putting in delimiters + * and waking up top half as needed. + * Also do erase, kill processing, and echo if required. + * The arguments are the character and the appropriate + * tty structure. + */ +ttyinput(c, tp) +register c; +register struct tty *tp; +{ + register int t_flags; +#ifdef MX + register struct chan *cp; +#endif MX + +#ifdef INSTRM + tk_nin += 1; +#endif INSTRM + c &= 0377; + if((tp->t_flags&RAW) == 0) + c &= 0177; + t_flags = tp->t_flags; + if(t_flags&TANDEM) + ttyblock(tp); + if((t_flags&RAW) == 0 || (t_flags&(RAW|CBREAK)) == (RAW|CBREAK)){ + if(tp->t_state&TTSTOP){ + if(c == tun.t_startc){ + tp->t_state &= ~TTSTOP; + ttstart(tp); + return; + } + if(c == tun.t_stopc) + return; + tp->t_state &= ~TTSTOP; + ttstart(tp); + } else { + if(c == tun.t_stopc){ + tp->t_state |= TTSTOP; + (*cdevsw[major(tp->t_dev)].d_stop)(tp); + return; + } + if(c == tun.t_startc) + return; + } + } + if((t_flags&RAW) == 0){ + if(tp->t_xstate&XLITRL){ + tp->t_xstate &= ~XLITRL; + if((t_flags&LCASE) && (maptab[c]&0177)) + c = maptab[c]&0177; + else if(!isspcl(c)){ + putc('\\', &tp->t_rawq); + goto contin; + } + putc(c, &tp->t_rawq); + goto out2; + contin:; + } + if((c == CPAGE) && tp->t_length){ + tp->t_xstate ^= XPAGE; + if((tp->t_xstate&XPAGE1) == 0) + goto out2; + } + if(tp->t_xstate&XPAGE1){ + tp->t_xstate &= ~(XPAGE1|XPAGE2); + if(c == '\r' || c == '\n') + tp->t_lnum = tp->t_length-1; + else + tp->t_lnum = 0; + if(c != tun.t_quitc && c != tun.t_intrc) + goto out3; + } + if(c == tun.t_quitc || c == tun.t_intrc){ + flushtty(tp); + c = (c == tun.t_intrc) ? SIGINT:SIGQUIT; +#ifdef MX + if(tp->t_chan) + scontrol(tp->t_chan, M_SIG, c); + else { +#endif MX + signal(tp->t_pgrp, c); + ttyecho('\n', tp); + ttstart(tp); +#ifdef MX + } +#endif MX + return; + } + if(t_flags&CBREAK) + goto out0; + if(c == tp->t_erase){ + if(iscntrl(c)){ + ttywipe(zapc(&tp->t_rawq), tp); + goto out3; + } + else { + zapc(&tp->t_rawq); + goto out2; + } + } + else if(c == tp->t_kill){ + if(((t_flags&SCOPE) == 0) || (!iscntrl(c))){ + while(zapc(&tp->t_rawq) >= 0); + ttyecho(c, tp); + ttyecho('\n', tp); + } + else { + while((c = zapc(&tp->t_rawq)) >= 0){ + if(ubyte(tp->t_col) > 0) + ttywipe(c, tp); + } + } + goto out3; + } + else if(c == tun.t_eofc){ + ttyecho('\n', tp); + goto out1; + } + else if(c == CLITERAL){ + tp->t_xstate |= XLITRL; + ttyecho(c, tp); + goto out3; + } + else if(c == CRETYPE){ + if(tp->t_outq.c_cc) + goto out3; +#ifdef DELTAB + ttyecho('\n', tp); + ttyretype(tp, 1); +#else + ttyretype(tp); +#endif DELTAB + goto out3; + } + else if(c == CWORD){ + ttywipe((c = zapc(&tp->t_rawq)), tp); + if(isword(c)){ + while(isword(c = zapc(&tp->t_rawq))) + ttywipe(c, tp); + if(c >= 0) + putc(c, &tp->t_rawq); + } + goto out3; + } + } +out0: + if(c == '\r' && (t_flags&CRMOD)) + c = '\n'; + if((t_flags&LCASE) && isupper(c)) + c += 'a'-'A'; + if(tp->t_rawq.c_cc > TTYHOG){ + flushtty(tp); + return; + } + putc(c, &tp->t_rawq); +out1: + if((t_flags&(RAW|CBREAK)) || (c == '\n' || c == tun.t_eofc || c == tun.t_brkc)){ + if((t_flags&(RAW|CBREAK)) == 0 && putc(0377, &tp->t_rawq) == 0) + tp->t_delct++; +#ifdef MX + if((cp = tp->t_chan) != NULL) + sdata(cp); + else +#endif MX + wakeup((caddr_t)&tp->t_rawq); + } +out2: + if(ttyecho(c, tp) == 0) + return; +out3: + ttstart(tp); +} + +#ifdef DELTAB +/* + * ttyretype performs two functions: + * 1) print the input buffer on CRETYPE (flag = 1); + * 2) count the column position for tab deletion + * (flag = 0). + */ +ttyretype(tp, flag) +register struct tty *tp; +{ + register char *cp; + register int c, n; + register int width, col; + + cp = tp->t_rawq.c_cf; + n = tp->t_delct; + width = tp->t_width ? ubyte(tp->t_width): 0377; + col = 0; + for(c = tp->t_rawq.c_cc; c--; ){ + if(n){ + if(ubyte(*cp++) == 0377) + n--; + } + else { + if((tp->t_flags&LCASE) && isupper(*cp) || isspcl(*cp)){ + if(flag) + ttyecho('\\', tp); + else + col++; + } + if(flag) + ttyecho(*cp++, tp); + else { + if(*cp++ == '\t') + col += 8 - (col&07); + else + col++; + if(col >= width) + col -= width; + } + } + if(((int)cp & CROUND) == 0) + cp = (((struct cblock *)cp) - 1)->c_next->c_info; + } + return(col); +} +#else +ttyretype(tp) +register struct tty *tp; +{ + register char *cp; + register int c, n; + + cp = tp->t_rawq.c_cf; + n = tp->t_delct; + ttyecho('\n', tp); + for(c = tp->t_rawq.c_cc; c--; ){ + if(n){ + if((*cp++&0377) == 0377) + n--; + } + else { + if((tp->t_flags&LCASE) && isupper(*cp) || isspcl(*cp)) + ttyecho('\\', tp); + ttyecho(*cp++, tp); + } + if(((int)cp & CROUND) == 0) + cp = (((struct cblock *)cp) - 1)->c_next->c_info; + } +} +#endif DELTAB + +/* + * Echo a character on the terminal. + * All echoed characters are only 7 bits wide. + */ +ttyecho(c, tp) +register c; +register struct tty *tp; +{ + c &= 0177; + if((tp->t_flags&ECHO) == 0 || (iscntrl(c) && (tp->t_flags&INDCTL) == 0)) + return(0); + if(tp->t_xstate&XERASE){ + tp->t_xstate &= ~XERASE; + ttyoutput(']', tp); + } + if((tp->t_flags&LCASE) && isupper(c)) + c += 'a'-'A'; + /* + * kludge for paging - yuck + */ + if((c == '\n') && (tp->t_xstate&XPAGE) && ((tp->t_flags&RAW) == 0)){ + if(tp->t_lnum > 0) + tp->t_lnum--; + } + ttyoutput(c, tp); + return(1); +} + +/* + * indicate the deletion of a character + * on the terminal + */ +ttywipe(c, tp) +register c; +register struct tty *tp; +{ + if((tp->t_flags&ECHO) == 0) + return; + if(c < 0){ + ttyoutput(CBELL, tp); + return; + } + if(tp->t_flags&SCOPE){ + register n = 1; + + if(iscntrl(c)){ + n = 0; + if(tp->t_flags&INDCTL) + n = 2; + if(isspcl(c)) + n++; + } + else if(c == '\t'){ +#ifdef DELTAB + if(n = ttyretype(tp, 0)) + n = ubyte(tp->t_col) - max(n,ubyte(tp->t_htdly)); + else if((n = ubyte(tp->t_col) - ubyte(tp->t_htdly)) == 0) + n = 8; + if(ubyte(tp->t_htdly) >= (ubyte(tp->t_col) - n)) + tp->t_htdly = 0; +#else + ttyretype(tp); + return; +#endif DELTAB + } + else if(tp->t_flags&LCASE){ + if(isupper(c) || c == '`' || c >= '{') + n++; + } + if((n < 0) || (n > ubyte(tp->t_col))) { + /* if(tp->t_col) */ + ttyoutput('\n',tp); + ttyretype(tp,1); + } + else + while(n--){ + ttyoutput('\b', tp); + ttyoutput(' ', tp); + ttyoutput('\b', tp); + } + return; + } + if((tp->t_xstate&XERASE) == 0){ + tp->t_xstate |= XERASE; + ttyoutput('[', tp); + } + ttyoutput(c, tp); +} + +/* + * remove a character from the end of + * a queue, unless it is a delimeter + */ +zapc(q) +register struct clist *q; +{ + extern struct cblock *cfreelist; + register struct cblock *bp; + register char *cp; + register int c, s; + + s = spl6(); + if(((cp = q->c_cl) == NULL) || ((c = ubyte(*--cp)) == 0377)){ + splx(s); + return(-1); + } + q->c_cl = cp; + cp -= sizeof(char *); + if(--q->c_cc <= 0){ + bp = (struct cblock *) ((int) cp & ~CROUND); + q->c_cf = q->c_cl = NULL; + bp->c_next = cfreelist; + cfreelist = bp; + } + else if(((int) cp & CROUND) == 0){ + bp = (struct cblock *) cp; + bp->c_next = cfreelist; + cfreelist = bp; + bp = (struct cblock *) q->c_cf; + bp = (struct cblock *) ((int) bp & ~CROUND); + while(bp->c_next != (struct cblock *) cp) + bp = bp->c_next; + bp->c_next = NULL; + q->c_cl = &bp->c_info[CBSIZE]; + } + splx(s); + return(c); +} + +/* + * ttybit returns non-zero if the bit in the area is set + */ +ttybit(area, bit) +char *area; +register int bit; +{ + return(bit < 0 ? 0: area[bit >> 3]&(1 << (bit&07))); +} + +/* + * Check if this is one of the special characters. + */ +ttyspcl(c, tp) +register c; +register struct tty *tp; +{ + register char *cp; + register n; + + if(c < 0) + return(0); + for(cp = &tun.t_intrc, n = 0; n < sizeof(struct tc); n++){ + if(c == *cp++) + return(1); + } + if((c == tp->t_erase) || (c == tp->t_kill)) + return(1); + if((c == CRETYPE) || (c == CWORD) || ((c == CPAGE) && tp->t_length)) + return(1); + return(0); +} + +/* + * Send stop character on input overflow. + */ +ttyblock(tp) +register struct tty *tp; +{ + register x; + x = tp->t_rawq.c_cc; + if(x > TTYHOG){ + flushtty(tp); + tp->t_state &= ~TBLOCK; + } + if((x >= TTYHOG/2) && ((tp->t_state&TBLOCK) == 0)) { + if(putc(tun.t_stopc, &tp->t_outq) == 0){ + tp->t_state |= TBLOCK; + ttstart(tp); + } + } +} + +/* + * put character on TTY output queue, adding delays, + * expanding tabs, and handling the CR/NL bit. + * It is called both from the top half for output, and from + * interrupt level for echoing. + * The arguments are the character and the tty structure. + */ +ttyoutput(c, tp) +register c; +register struct tty *tp; +{ + register char *colp; + +#ifdef INSTRM + tk_nout += 1; +#endif INSTRM + /* + * Turn to if desired. + */ + if(c == '\n' && (tp->t_flags&CRMOD)) + ttyoutput('\r', tp); + if((tp->t_flags&RAW) == 0){ + c &= 0177; + /* + * Print control characters as ^. + */ + if((tp->t_flags&INDCTL) && iscntrl(c)){ + ttyoutput('^', tp); + c |= 0100; + } + /* + * Whoa there Trigger! - it's a page boundary + */ + if(tp->t_length && (tp->t_xstate&XPAGE)){ + if((c == '\n' && (ubyte(++(tp->t_lnum)) >= ubyte(tp->t_length))) || c == '\014'){ + tp->t_lnum = 0; + tp->t_xstate |= XPAGE2; + putc(0200, &tp->t_outq); + } + } + } + /* + * For upper-case-only terminals, + * generate escapes. + * Also, upper-case characters are printed + * with a preceeding '\\'. + */ + if(tp->t_flags&LCASE){ + if('a' <= c && c <= 'z') + c += 'A' - 'a'; + else if(isupper(c)) + ttyoutput('\\', tp); + else { + colp = "({)}!|^~'`"; + while(*colp++){ + if(c == *colp++){ + ttyoutput('\\', tp); + c = colp[-2]; + break; + } + } + } + } + /* + * If it is a printing character, count it, + * and fold the line if it is too long. + */ + colp = &tp->t_col; + /* + * Turn tabs to spaces as required + */ + if(c == '\t' && (tp->t_flags&XTABS)){ +#ifdef DELTAB + if(tp->t_htdly == 0) + tp->t_htdly = tp->t_col; +#endif DELTAB + do + ttyoutput(' ', tp); + while((*colp)&07); + return; + } + if(' ' <= c && c <= '~'){ + if(tp->t_width && (ubyte(*colp) >= ubyte(tp->t_width))) + ttyoutput('\n', tp); + (*colp)++; + putc(c, &tp->t_outq); + return; + } + putc(c, &tp->t_outq); + /* + * Calculate delays. + * The delays are indicated by characters above 0200. + * In raw mode there are no delays and the + * transmission path is 8 bits wide. However, do the + * column calculation. + */ + switch(c&0177){ + case '\b': + if(*colp) + (*colp)--; + default: + return; + case '\t': + *colp |= 07; + (*colp)++; + c = tp->t_htdly; + break; + case '\n': +#ifdef DELTAB + if(tp->t_flags&XTABS) + tp->t_htdly = 0; +#endif DELTAB + *colp = 0; + c = tp->t_nldly; + break; + case 013: + case 014: + c = tp->t_vtdly; + break; + case '\r': + *colp = 0; + c = tp->t_crdly; + break; + } + if(c && ((tp->t_flags&RAW) == 0)) + putc(c|0200, &tp->t_outq); +} + +/* + * Restart typewriter output following a delay + * timeout. + * The name of the routine is passed to the timeout + * subroutine and it is called during a clock interrupt. + */ +ttrstrt(tp) +register struct tty *tp; +{ + tp->t_state &= ~TIMEOUT; + ttstart(tp); +} + +/* + * Start output on the typewriter. It is used from the top half + * after some characters have been put on the output queue, + * from the interrupt routine to transmit the next + * character, and after a timeout has finished. + */ +ttstart(tp) +register struct tty *tp; +{ + register s; + + s = spl5(); + if((tp->t_state&(TIMEOUT|TTSTOP|BUSY)) == 0) + (*tp->t_oproc)(tp); + splx(s); +} + +/* + * Called from device's read routine after it has + * calculated the tty-structure given as argument. + */ +ttread(tp) +register struct tty *tp; +{ + register int c; + + if((tp->t_state&CARR_ON) == 0) + return(0); + spl5(); + while(((tp->t_flags&(RAW|CBREAK)) == 0 && tp->t_delct == 0) + || ((tp->t_flags&(RAW|CBREAK)) != 0 && tp->t_rawq.c_cc == 0)){ +#ifdef MX + if((tp->t_state&CARR_ON) == 0 || tp->t_chan != NULL) +#else + if((tp->t_state&CARR_ON) == 0) +#endif MX + return(tp->t_rawq.c_cc); + sleep((caddr_t)&tp->t_rawq, TTIPRI); + } + spl0(); + if(tp->t_flags&(RAW|CBREAK)){ + while(tp->t_rawq.c_cc && passc(getc(&tp->t_rawq)) >= 0); + goto out; + } + while((c = getc(&tp->t_rawq)) != 0377 && passc(c) >= 0); + if((c != 0377) && ubyte(*(tp->t_rawq.c_cf)) == 0377) + c = getc(&tp->t_rawq); + if(c == 0377) + tp->t_delct--; + if(tp->t_length){ + tp->t_xstate |= XPAGE; + tp->t_xstate &= ~(XPAGE1|XPAGE2); + tp->t_lnum = 0; + } + else + tp->t_xstate &= ~XPAGE; +out: + if((tp->t_state&TBLOCK) && tp->t_rawq.c_cc < TTYHOG/5){ + if(putc(tun.t_startc, &tp->t_outq) == 0){ + tp->t_state &= ~TBLOCK; + ttstart(tp); + } + } + return(tp->t_rawq.c_cc); +} + +/* + * Called from the device's write routine after it has + * calculated the tty-structure given as argument. + */ +caddr_t +ttwrite(tp) +register struct tty *tp; +{ + register c; + + if((tp->t_state&CARR_ON) == 0) + return(NULL); + while(u.u_count){ + spl5(); + while(tp->t_outq.c_cc > TTHIWAT || (tp->t_xstate&XPAGE2)){ + ttstart(tp); + tp->t_state |= ASLEEP; +#ifdef MX + if(tp->t_chan) + return((caddr_t)&tp->t_outq); +#endif MX + sleep((caddr_t)&tp->t_outq, TTOPRI); + } + spl0(); + if((c = cpass()) < 0) + break; + ttyoutput(c, tp); + } + ttstart(tp); + return(NULL); +} diff --git a/tty/sys/tty.h b/tty/sys/tty.h new file mode 100644 index 0000000..dcd27c2 --- /dev/null +++ b/tty/sys/tty.h @@ -0,0 +1,183 @@ +/* + * A clist structure is the head + * of a linked list queue of characters. + * The characters are stored in 4-word + * blocks containing a link and several characters. + * The routines getc and putc + * manipulate these structures. + */ +struct clist { + int c_cc; /* character count */ + char *c_cf; /* pointer to first char */ + char *c_cl; /* pointer to last char */ +}; + +/* + * Special character structure. + * All special characters may be redefined, so + * they are held within the tty structure. + */ +struct tc { + char t_intrc; /* interrupt */ + char t_quitc; /* quit */ + char t_startc; /* start output */ + char t_stopc; /* stop output */ + char t_eofc; /* end-of-file */ + char t_brkc; /* input delimiter (like nl) */ +}; + +/* + * A tty structure is needed for + * each UNIX character device that + * is used for normal terminal IO. + * The routines in tty.c handle the + * common code associated with + * these structures. + * The definition and device dependent + * code is in each driver. (kl.c dc.c dh.c) + */ +struct tty { + struct clist t_rawq; /* input list from device */ + struct clist t_outq; /* output list to device */ + int (* t_oproc)(); /* routine to start output */ + int (* t_iproc)(); /* routine to start input */ + short t_pgrp; /* process group name */ + caddr_t t_addr; /* device address */ + dev_t t_dev; /* device number */ + char t_ispeed; /* input speed */ + char t_ospeed; /* output speed */ + char t_erase; /* erase character */ + char t_kill; /* kill character */ + short t_flags; /* mode, settable by ioctl call */ + char t_nldly; /* delay for newline */ + char t_crdly; /* delay for carriage return */ + char t_htdly; /* delay for horizontal tab */ + char t_vtdly; /* delay for vertical tab */ + char t_width; /* max. line length for folding */ + char t_length; /* max. screen length for paging */ + union { + struct tc; + struct clist t_ctlq; + } t_un; + short t_state; /* internal state, not visible externally */ + short t_xstate; /* as above, for extra stuff */ + char t_lnum; /* line number on screen */ + char t_col; /* printing column of device */ + char t_delct; /* number of delimiters in raw q */ + char t_char; /* character temporary */ + struct chan *t_chan; /* destination channel */ + caddr_t t_linep; /* aux line discipline pointer */ + char t_line; /* line discipline */ +}; + +#define tun tp->t_un + +/* + * structure of arg for ioctl + */ +struct ttiocb { + char ioc_ispeed; + char ioc_ospeed; + char ioc_erase; + char ioc_kill; + short ioc_flags; + char ioc_nldly; + char ioc_crdly; + char ioc_htdly; + char ioc_vtdly; + char ioc_width; + char ioc_length; +}; + +#define TTIPRI 28 +#define TTOPRI 29 + +#include "../h/deftty.h" + +/* + * limits + */ +#define TTHIWAT 100 +#define TTLOWAT 50 +#define TTYHOG 256 + +/* + * modes + */ +#define TANDEM 01 +#define CBREAK 02 +#define LCASE 04 +#define ECHO 010 +#define CRMOD 020 +#define RAW 040 +#define ODDP 0100 +#define EVENP 0200 +#define SCOPE 0400 +#define INDCTL 01000 +#define XTABS 02000 + +/* + * Hardware bits + */ +#define DONE 0200 +#define IENABLE 0100 + +/* + * Internal state bits + */ +#define TIMEOUT 01 /* Delay timeout in progress */ +#define WOPEN 02 /* Waiting for open to complete */ +#define ISOPEN 04 /* Device is open */ +#define FLUSH 010 /* outq has been flushed during DMA */ +#define CARR_ON 020 /* Software copy of carrier-present */ +#define BUSY 040 /* Output in progress */ +#define ASLEEP 0100 /* Wakeup when output done */ +#define XCLUDE 0200 /* exclusive-use flag against open */ +#define TTSTOP 0400 /* Output stopped by ctl-s */ +#define HUPCLS 01000 /* Hang up upon last close */ +#define TBLOCK 02000 /* tandem queue blocked */ +#define CNTLQ 0100000 /* interpret t_un as clist */ +/* + * More internal state bits (for t_xstate) + */ +#define XPAGE 01 /* output is being paged */ +#define XPAGE1 02 /* set to indicate stopped at page boundary */ +#define XPAGE2 04 /* set to stop ttwrite after a page is on the queue */ +#define XLITRL 010 /* last character was a literal escape */ +#define XERASE 020 /* erase string in progress */ + +/* + * tty ioctl commands + */ +#define TIOCGETD (('t'<<8)|0) +#define TIOCSETD (('t'<<8)|1) +#define TIOCHPCL (('t'<<8)|2) +#define TIOCMODG (('t'<<8)|3) +#define TIOCMODS (('t'<<8)|4) +#define TIOCGETP (('t'<<8)|8) +#define TIOCSETP (('t'<<8)|9) +#define TIOCSETN (('t'<<8)|10) +#define TIOCSETA (('t'<<8)|11) +#define TIOCGETA (('t'<<8)|12) +#define TIOCEXCL (('t'<<8)|13) +#define TIOCNXCL (('t'<<8)|14) +#define TIOCNRD (('t'<<8)|15) +#define TIOCFLUSH (('t'<<8)|16) +#define TIOCSETC (('t'<<8)|17) +#define TIOCGETC (('t'<<8)|18) +#define DIOCLSTN (('d'<<8)|1) +#define DIOCNTRL (('d'<<8)|2) +#define DIOCMPX (('d'<<8)|3) +#define DIOCNMPX (('d'<<8)|4) +#define DIOCSCALL (('d'<<8)|5) +#define DIOCRCALL (('d'<<8)|6) +#define DIOCPGRP (('d'<<8)|7) +#define DIOCGETP (('d'<<8)|8) +#define DIOCSETP (('d'<<8)|9) +#define DIOCLOSE (('d'<<8)|10) +#define DIOCTIME (('d'<<8)|11) +#define DIOCRESET (('d'<<8)|12) +#define FIOCLEX (('f'<<8)|1) +#define FIONCLEX (('f'<<8)|2) +#define MXLSTN (('x'<<8)|1) +#define MXNBLK (('x'<<8)|2) diff --git a/v7conf/1-basic/README.md b/v7conf/1-basic/README.md new file mode 100644 index 0000000..7c21e8c --- /dev/null +++ b/v7conf/1-basic/README.md @@ -0,0 +1,11 @@ +# 1-basic + +This is the basic setup for the V7 system that is created by the installation. + +## v7boot.ini - SIMH Bootstrap + +Sets up a PDP11/70 with 2M of memory. It has a single RP06 disk and a tapedrive. Finally it will boot from block zero on the disk. + +## v7conf - the kernel config + +Defines the root and swap partitions on the RP06, and includes the tape drive interface. diff --git a/v7conf/1-basic/v7boot.ini b/v7conf/1-basic/v7boot.ini new file mode 100644 index 0000000..4e744f1 --- /dev/null +++ b/v7conf/1-basic/v7boot.ini @@ -0,0 +1,11 @@ +echo +echo After Disabling XQ is displayed type boot +echo and at the : prompt type hp(0,0)unix +echo +set cpu 11/70 +set cpu 2M +set cpu idle +set rp0 rp06 +att rp0 rp06-0.disk +att tm0 tm0.tap +boot rp0 diff --git a/v7conf/1-basic/v7conf b/v7conf/1-basic/v7conf new file mode 100644 index 0000000..2ba13d7 --- /dev/null +++ b/v7conf/1-basic/v7conf @@ -0,0 +1,6 @@ +hp +root hp 0 +swap hp 1 +swplo 0 +nswap 8778 +tm diff --git a/v7conf/2-add-dc/README.md b/v7conf/2-add-dc/README.md new file mode 100644 index 0000000..cf9e26a --- /dev/null +++ b/v7conf/2-add-dc/README.md @@ -0,0 +1,23 @@ +# 2-add-dc - add extra terminals to the basic system + +## v7boot.ini - SIMH Bootstrap + +Adding 4 DC11 lines to [1-basic](../1-basic). These lines are accessible from _telnet_ using _localhost_, port 5555. You can chose your own port value. + +## v7conf - Kernel config + +Adds 4dc lines + +## Installation + +Reboot using the new _v7boot.ini_, then compile and reinstall the kernel with the new _v7conf_. + +Use _ttys.mk_ to create the terminal nodes in _/dev_. Copy the file to _/dev_ and + +``` sh +make -f ttys.mk +``` + +Once that is done, you need to tell the _init_ program what terminals are to be started. The suggested values are in _ttys_ and this is be copied to _/etc_. The first character of each line is '1' or '0' to enable the terminal line. The second character is passed as an argument to _getty_ to select terminal settings. A copy of my _ttys_ file can be found here. + +To make all this work, reboot the system. diff --git a/v7conf/2-add-dc/ttys b/v7conf/2-add-dc/ttys new file mode 100644 index 0000000..73b4931 --- /dev/null +++ b/v7conf/2-add-dc/ttys @@ -0,0 +1,33 @@ +14console +13tty00 +13tty01 +13tty02 +13tty03 +00tty04 +00tty05 +00tty06 +00tty07 +00tty08 +00tty09 +00tty10 +00tty11 +00tty12 +00tty13 +00tty14 +00tty15 +00tty16 +00tty17 +00tty18 +00tty19 +00tty20 +00tty21 +00tty22 +00tty23 +00tty24 +00tty25 +00tty26 +00tty27 +00tty28 +00tty29 +00tty30 +00tty31 diff --git a/v7conf/2-add-dc/ttys.mk b/v7conf/2-add-dc/ttys.mk new file mode 100644 index 0000000..a31b2b3 --- /dev/null +++ b/v7conf/2-add-dc/ttys.mk @@ -0,0 +1,22 @@ +# make file for ttys +# copy to /dev +# make -f ttys.mk +TTYS=tty00 tty01 tty02 tty03 + +all: $(TTYS) + chmod 622 $(TTYS) + +tty00: + /etc/mknod tty00 c 3 0 + +tty01: + /etc/mknod tty00 c 3 1 + +tty02: + /etc/mknod tty00 c 3 2 + +tty03: + /etc/mknod tty00 c 3 2 + +clean: + rm -f $(TTYS) diff --git a/v7conf/2-add-dc/v7boot.ini b/v7conf/2-add-dc/v7boot.ini new file mode 100644 index 0000000..9bff3c3 --- /dev/null +++ b/v7conf/2-add-dc/v7boot.ini @@ -0,0 +1,14 @@ +echo +echo After Disabling XQ is displayed type boot +echo and at the : prompt type hp(0,0)unix +echo +set cpu 11/70 +set cpu 2M +set cpu idle +set rp0 rp06 +att rp0 rp06-0.disk +att tm0 tm0.tap +set dci en +set dci lines=4 +att dci 5555 +boot rp0 diff --git a/v7conf/2-add-dc/v7conf b/v7conf/2-add-dc/v7conf new file mode 100644 index 0000000..34baad6 --- /dev/null +++ b/v7conf/2-add-dc/v7conf @@ -0,0 +1,7 @@ +hp +root hp 0 +swap hp 1 +swplo 0 +nswap 8778 +tm +4dc diff --git a/v7conf/3-settime/README.md b/v7conf/3-settime/README.md new file mode 100644 index 0000000..4167984 --- /dev/null +++ b/v7conf/3-settime/README.md @@ -0,0 +1,43 @@ +# 3-settime - set system time at bootstrap + +This cannot be done without making changes to the _date_ command, see [date](../../time). + +## v7boot.ini - SIMH Bootstrap + +Adds the DecTape driver to the simulation. Before the device is added it calls the _timecmd.py_ script to setup the 'tape'.x + +The SIMH driver will create a file _tc0.dec_ for the tape if none exists. DecTape is a blocked device, and the tape format is just a set of 512-byte blocks. SIMH reads in and buffers the contents. + +## v7conf - Kernel config + +Adds the DecTape driver. + +## Installation + +Add a '#' at the start of the _timecmd.py_ line in _v7conf.ini_, for now. Reboot the system with the new _v7conf.ini_. This will create the _tc.dec_ file for the tape. + +Compile the kernel using _v7conf_ and reboot. + +Use _tc.mk_ to create _/dev/tap0_, copy the file to _/dev_ and + +``` sh +make -f tc.mk +``` + +Adding _clean_ to the _make_ command will remove the entries. + +You can use the _tp_ program to test that things are working, to write to the DecTape: + +``` sh +tp xv SOMEFILE +``` + +The contents won't appear in _tc0.dec_ until the simulation is stopped, or the device is re-attached. I use a SIMH _do_ script to do this - _reldt_. + +The _uxtp_ program will read DecTapes in _tp_ format, it understands that files ending in _.dec_ are DecTapes. + +Once the DecTape is working, the time setting script in Python: _timecmd.py_ can be uncommented in _v7boot.ini_. The script writes a _date_ command and a newline at the start of block zero of the tape before the tape is attached. + +Finally, you can install _settime_ in _/etc/_ on the system, and call that in _/etc/rc_. This script is called when the system goes multiuser before the terminals are started. + +Running this means that you lose a few seconds while you are getting the system to start, but it's better than running the _date_ command by hand. diff --git a/v7conf/3-settime/reldt b/v7conf/3-settime/reldt new file mode 100644 index 0000000..4156d3a --- /dev/null +++ b/v7conf/3-settime/reldt @@ -0,0 +1,2 @@ +det tc0 +att tc0 tc0.dat diff --git a/v7conf/3-settime/settime b/v7conf/3-settime/settime new file mode 100644 index 0000000..5fc1464 --- /dev/null +++ b/v7conf/3-settime/settime @@ -0,0 +1,8 @@ +: read 16 bytes from dectape +: containing a date command +rm -f /tmp/setdate +dd if=/dev/tap0 of=/tmp/setdate bs=1 count=16 2> /dev/null +if [ -f /tmp/setdate ]; then + /bin/sh /tmp/setdate +fi +rm -f /tmp/setdate diff --git a/v7conf/3-settime/tc.mk b/v7conf/3-settime/tc.mk new file mode 100644 index 0000000..23fb8b3 --- /dev/null +++ b/v7conf/3-settime/tc.mk @@ -0,0 +1,13 @@ +# make file for tc dec tape +# copy to /dev +# make -f tc.mk +TC0=tap0 + +all: TC0 + chmod 666 $(TC0) + +TC0: + /etc/mknod tap0 b 4 0 + +clean: + rm -f $(TC0) diff --git a/v7conf/3-settime/timecmd.py b/v7conf/3-settime/timecmd.py new file mode 100755 index 0000000..800a368 --- /dev/null +++ b/v7conf/3-settime/timecmd.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 +""" Make a paper tape +should start and end with 100 blank values +""" +import time + + +def cmd(): + """ make a date cmd """ + + tspec = time.strftime("%y%m%d%H%M") + cmd = "date " + tspec + '\n' + body = bytearray(cmd, "ascii") + with open("tc0.dec", "r+b") as f: + f.write(body) + +if __name__ == '__main__': + + cmd() diff --git a/v7conf/3-settime/v7boot.ini b/v7conf/3-settime/v7boot.ini new file mode 100644 index 0000000..0f78655 --- /dev/null +++ b/v7conf/3-settime/v7boot.ini @@ -0,0 +1,18 @@ +echo +echo After Disabling XQ is displayed type boot +echo and at the : prompt type hp(0,0)unix +echo +set cpu 11/70 +set cpu 2M +set cpu idle +set rp0 rp06 +att rp0 rp06-0.disk +att tm0 tm0.tap +!timecmd.py +set tc enable +set tc0 writeenabled +att tc0 tc0.dec +set dci en +set dci lines=4 +att dci 5555 +boot rp0 diff --git a/v7conf/3-settime/v7conf b/v7conf/3-settime/v7conf new file mode 100644 index 0000000..4554e46 --- /dev/null +++ b/v7conf/3-settime/v7conf @@ -0,0 +1,8 @@ +hp +root hp 0 +swap hp 1 +swplo 0 +nswap 8778 +tm +tc +4dc diff --git a/v7conf/4-testrk/README.md b/v7conf/4-testrk/README.md new file mode 100644 index 0000000..9dcd667 --- /dev/null +++ b/v7conf/4-testrk/README.md @@ -0,0 +1,39 @@ +# 4-testrk - Add an RK05 drive + +I wanted to add an RK05 drive to test bad file systems with [fsck](../../fsck). It builds on the settings in [3-settime](../3-settime). + +## v7boot.ini - SIMH Config + +Adds a single RK05 drive. + +## v6config - Kernel config + +Adds the _rk_ driver. + +## Installation + +Once the new kernel is built, copy _rk.mk_ to _dev_ and + +``` sh +make -f rk.mk +``` + +Adding _clean_ to the _make_ command will remove the entries. + +To make a filesystem on the new RK05, use the _mkfs_ program. _mfks_ needs a prototype file, and _rk.proto_ will do this. NB the contents of the proto file has changed some V6. The second line has two numbers, the first is the number of blocks to be used for the file system (4872), the second is the number of files that the system can contain - A.K.A the number of inodes - 2880. You need to be superuser: + +``` sh +# /etc/mkfs /dev/rrk0 rk.proto +/usr/mdec/rkuboot: cannot open init +m/n = 3 500 +# icheck /dev/rrk0 +/dev/rrk0: +files 2 (r=1,d=1,b=0,c=0) +used 1 (i=0,ii=0,iii=0,d=1) +free 4508 +missing 0 +``` + +It cannot find the boot file for the RK05, it complains but still makes the file system. I've looked and cannot find a working _rkuboot.s_. + +The file _rksys.proto_ sets up the RK05 with a system area and some blocks for swapping. diff --git a/v7conf/4-testrk/rk.mk b/v7conf/4-testrk/rk.mk new file mode 100644 index 0000000..2495136 --- /dev/null +++ b/v7conf/4-testrk/rk.mk @@ -0,0 +1,14 @@ +# make file for rk05 disk +# copy to /dev +# make -f rk.mk +RK0=rk0 + +all: RK0 + chmod 666 $(RK0) r$(RK0) + +RK0: + /etc/mknod $(RK0) b 0 0 + /etc/mknod r$(RK0) c 9 0 + +clean: + rm -f $(RK0) r$(RK0) diff --git a/v7conf/4-testrk/rk.proto b/v7conf/4-testrk/rk.proto new file mode 100644 index 0000000..73c9daa --- /dev/null +++ b/v7conf/4-testrk/rk.proto @@ -0,0 +1,4 @@ +/usr/mdec/rkuboot +4872 2880 +d--777 0 3 +$ diff --git a/v7conf/4-testrk/rksys.proto b/v7conf/4-testrk/rksys.proto new file mode 100644 index 0000000..3dffa85 --- /dev/null +++ b/v7conf/4-testrk/rksys.proto @@ -0,0 +1,4 @@ +/usr/mdec/rkuboot +4000 1600 +d--777 0 3 +$ diff --git a/v7conf/4-testrk/rkuboot b/v7conf/4-testrk/rkuboot new file mode 100644 index 0000000..2485c09 Binary files /dev/null and b/v7conf/4-testrk/rkuboot differ diff --git a/v7conf/4-testrk/v7boot.ini b/v7conf/4-testrk/v7boot.ini new file mode 100644 index 0000000..290fa3f --- /dev/null +++ b/v7conf/4-testrk/v7boot.ini @@ -0,0 +1,19 @@ +echo +echo After Disabling XQ is displayed type boot +echo and at the : prompt type hp(0,0)unix +echo +set cpu 11/70 +set cpu 2M +set cpu idle +set rp0 rp06 +att rp0 rp06-0.disk +att rk0 rk0.disk +att tm0 tm0.tap +!timecmd.py +set tc enable +set tc0 writeenabled +att tc0 tc0.dec +set dci en +set dci lines=4 +att dci 5555 +boot rp0 diff --git a/v7conf/4-testrk/v7conf b/v7conf/4-testrk/v7conf new file mode 100644 index 0000000..076336b --- /dev/null +++ b/v7conf/4-testrk/v7conf @@ -0,0 +1,9 @@ +hp +root hp 0 +swap hp 1 +swplo 0 +nswap 8778 +rk +tm +tc +4dc diff --git a/v7conf/5-secondrp/README.md b/v7conf/5-secondrp/README.md new file mode 100644 index 0000000..c203e1b --- /dev/null +++ b/v7conf/5-secondrp/README.md @@ -0,0 +1,38 @@ +# 5-secondrp + +If you need to repair the root file system, it's probably OK to use _icheck_, _dcheck_ or _fsck_ in single user mode. The kernel will be buffering the disk blocks for the filesystem, so use the block interface _/dev/rp0_ as the device to repair. Broken filesystems can often be read. However it's safer to repair offline filesystems. + +If you need to repair the root file system on the main RP06 disk, it might be wise to create a second RP06 disk, and replace the new disk file with the current contents of _rp06-0.disk_. You can then check and fix the root file system that's offline, and then replace your working copy with the fixed image. + +## v7boot.ini - SIMH Config + +Adds a second RP06 disk. + +## v6config - Kernel config + +No change is needed to the kernel + +## Installation + +The _rp2.mk_ creates entries for the new disk in _/dev_. The _hp_ driver for RP06 supports several overlapping partition settings supplying layouts for disks of different sizes. This V7 installation uses three - _rp0_ (minor device 0) for the first section of the disk, _swap_ (minor device 1) and _rp3_ (minor device 7) that uses the remainder of the RP06. + +For the spare disk on drive 1, the minor device has 8 added to the device number. The value in the minor number above the bottom 3-bits selects the drive. I decided to ignore the swap partition and assign _rp4_ for the small partition and _rp5_ for the rest. + +To create the devices, copy _rp2.mk_ to _dev_, go there and run + +``` sh +make -f rp2.mk +``` + +again you can give the _makefile_ a _clean_ argument to remove the entries. + +## Reformatting the second disk + +If you want to create new filesystem on _rp4_ or _rp5_, you can use one or both of the _rp4.proto_ or _rp5.proto_. They are configured to match the sizes of the original distribution RP06. + +``` sh +/etc/mkfs /dev/rrp4 rp4.proto +/etc/mkfs /dev/rrp5 rp5.proto +``` + +Use _icheck_, _dcheck_ or _fsck_ to check the disks. diff --git a/v7conf/5-secondrp/rp2.mk b/v7conf/5-secondrp/rp2.mk new file mode 100644 index 0000000..4335b56 --- /dev/null +++ b/v7conf/5-secondrp/rp2.mk @@ -0,0 +1,19 @@ +# Makefile for second rp disk +# Use rp4 and rp5 for partitions +# provide access to copy of rp0 +RP4=rp4 +RP5=rp5 + +all: $(RP4) $(RP5) + chmod go-w $(RP4) r$(RP4) $(RP5) r$(RP5) + +$(RP4): + /etc/mknod $(RP4) b 6 8 + /etc/mknod r$(RP4) c 14 8 + +$(RP5): + /etc/mknod $(RP5) b 6 15 + /etc/mknod r$(RP5) c 14 15 + +clean: + rm -f $(RP4) r$(RP4) $(RP5) r$(RP5) diff --git a/v7conf/5-secondrp/rp4.proto b/v7conf/5-secondrp/rp4.proto new file mode 100644 index 0000000..799238f --- /dev/null +++ b/v7conf/5-secondrp/rp4.proto @@ -0,0 +1,4 @@ +/usr/mdec/rpuboot +5000 1600 +d--777 0 3 +$ diff --git a/v7conf/5-secondrp/rp5.proto b/v7conf/5-secondrp/rp5.proto new file mode 100644 index 0000000..d0e8132 --- /dev/null +++ b/v7conf/5-secondrp/rp5.proto @@ -0,0 +1,4 @@ +/usr/mdec/rpuboot +322278 65512 +d--777 0 3 +$ diff --git a/v7conf/5-secondrp/v7boot.ini b/v7conf/5-secondrp/v7boot.ini new file mode 100644 index 0000000..05f0283 --- /dev/null +++ b/v7conf/5-secondrp/v7boot.ini @@ -0,0 +1,20 @@ +echo +echo After Disabling XQ is displayed type boot +echo and at the : prompt type hp(0,0)unix +echo +set cpu 11/70 +set cpu 2M +set cpu idle +set rp0 rp06 +att rp0 rp06-0.disk +set rp1 rp06 +att rp1 rp06-1.disk +att tm0 tm0.tap +!timecmd.py +set tc enable +set tc0 writeenabled +att tc0 tc0.dec +set dci en +set dci lines=4 +att dci 5555 +boot rp0 diff --git a/v7conf/README.md b/v7conf/README.md new file mode 100644 index 0000000..a2ec8e4 --- /dev/null +++ b/v7conf/README.md @@ -0,0 +1,46 @@ +# Configure SIMH and V7 + +This directory contains various setups for the V7 kernel and the instructions that are needed for SIMH's corresponding startup file. If you want to set things up from scratch visit the [bootstrap](../bootstrap) directory that holds a preloaded disk image. There are step-by-step instructions on how to get going, and what to expect. + +## Recompiling the kernel + +The kernel source is found on _/usr/sys_, there are several directories: + +* _conf_ - the place to configure and rebuild the kernel +* _dev_ - sources for device drivers +* _h_ - header files, the files are also duplicated on _/usr/include/sys_ +* _sys_ - source files for the remainder of the kernel + +The initial files in _conf_ are really there to rebuild the distribution. The supplied _makefile_ doesn't do a good job of rebuilding the system when some parts of the source changes. You'll find a script called _conftidy_ here that moves all the files that are not needed into a newly built _orig_ directory. The _makefile_ here generates _unix_ binaries and has dependencies that will recompile the kernel safely. + +The process of building the kernel: + +* The _makefile_ assumes that the two libraries _sys/LIB1_ and _dev/LIB2_ are current. If you've changed any of their contents, you need to run ```make all``` to recreate them. +* System configuration depends on a file called _v7conf_, which needs to be copied in from [1-basic](1-basic). This is automatically run through the _mkconf_ command to generate _c.c_ containing the device switch tables. It also makes _l.s_ which adds code to link the interrupt system of the PDP11 into the C code. +* Once _v7conf_ is in place, type ```make``` and a working kernel will appear in the _unix_ file. +* I tend to copy this to _/nunix_ until I am sure that it will boot. There's always mileage of hanging onto a _/ounix_, just in case. + +## Kernel Configurations + +When you change the devices in the kernel, you need to change the SIMH setup file to attach the new devices. In addition, you may need to make new device nodes in _/dev_. The various setups below each contain: + +* _v7conf_ - the kernel configuration file +* _v7boot.ini_ - the SIMH control file +* _*.mk_ - a shell script to make the devices that are needed in the system to access the new devices +* Other config files + +The setups are: + +* [_1-basic_](1-basic) - the initial setup to run the RP06 system +* [_2-add-dc_](2-add-dc) - add extra terminals that can be accessed by _telnet_ or _gtelnet_ to give you another four live terminals for the system. + + I used this setup to install the other new parts of _unixv7-extras_: + * [_halt_](../halt) - add a halt command to cleanly shutdown the system + * [_tty_](../tty) - new terminal driver + * [_time_](../time) - ensure that the time command can use current times + +* [_3-settime_](3-settime) - use the DECTAPE drive to set the system time at boot time. A small python program run from SIMH setup writes the current time onto the DECTAPE. + +* [_4-testrk_](4-testrk) - I needed a disk that I could use to 'break' and test the [fsck](../fsck) command + +* [_5-secondrp_](5-secondrp) - add a new RP06 disk that can be used to clone the system disk for fixing (if necessary) using offline access to the root partition. diff --git a/v7conf/conftidy b/v7conf/conftidy new file mode 100755 index 0000000..2dcabd7 --- /dev/null +++ b/v7conf/conftidy @@ -0,0 +1,19 @@ +: Move unwanted config files to orig directory +: Argument is the name of the sys/conf directory +if [ $# -ne 1 ]; then + echo 'Usage conftidy confdir' + exit +fi +cd $1 +if [ ! -d orig ]; then + mkdir orig + if [ ! -d orig ]; then + echo "Failed to make orig directory" + exit + fi +fi +for name in hphtconf rktmconf rphtconf tconf hptmconf rkhtconf rp1conf rptmconf makefile; do + if [ -f $name ]; then + mv $name orig/$name + fi +done diff --git a/v7conf/makefile b/v7conf/makefile new file mode 100644 index 0000000..6e25d28 --- /dev/null +++ b/v7conf/makefile @@ -0,0 +1,23 @@ +CONFIG=v7conf + +unix: l.o mch.o c.o ../sys/LIB1 ../dev/LIB2 + ld -o unix -X -i l.o mch.o c.o ../sys/LIB1 ../dev/LIB2 + +c.o: c.c + cc -c c.c + +l.o: l.s c.c + as - -o l.o l.s + +c.c: mkconf $(CONFIG) + mkconf < $(CONFIG) + +mkconf: mkconf.c + cc -o mkconf -O mkconf.c + +mch.o: mch0.s mch.s + as -o mch.o mch0.s mch.s + +all: + cd ../sys; cc -c -O *.c; mklib; rm *.o + cd ../dev; cc -c -O *.c; mklib; rm *.o diff --git a/v7conf/v7 b/v7conf/v7 new file mode 100755 index 0000000..f846ff4 --- /dev/null +++ b/v7conf/v7 @@ -0,0 +1 @@ +exec pdp11 v7boot.ini